Basic Caching with Nginx: Improve Response Times
Configure basic Nginx proxy caching safely with cache zones, TTLs, bypass rules, status headers, and testing steps.
Basic Caching with Nginx: Improve Response Times
Basic caching with Nginx can improve response times by saving a copy of upstream responses and serving them again without asking the application every time. When used carefully, caching reduces backend load, smooths traffic spikes, and makes repeat requests feel faster.
Caching is not only for large sites. Even a small app can benefit when pages, API responses, or static files are requested often and do not change every second.
What Nginx Can Cache
Nginx can cache responses from an upstream server when it acts as a reverse proxy. This is different from normal browser caching. Browser caching stores files on the user's device. Nginx proxy caching stores responses on the server so many users can benefit from the same cached copy.
A simple proxy cache setup has two parts. First, define a cache zone in the http block:
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app_cache:10m
max_size=1g inactive=60m use_temp_path=off;
Then enable that cache in a server or location block:
location / {
proxy_pass http://127.0.0.1:3000;
proxy_cache app_cache;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
add_header X-Cache-Status $upstream_cache_status;
}
The proxy_cache_path directive creates the cache storage area. keys_zone defines shared memory for cache keys and metadata. max_size limits disk usage. inactive removes items that have not been accessed for a while.
The proxy_cache_valid directives decide how long certain response codes stay cached. In the example, successful responses are cached for 10 minutes, while 404 responses are cached for 1 minute.
The X-Cache-Status header is useful during testing. It can show values such as MISS, HIT, BYPASS, or EXPIRED, depending on what happened.
For sites that also use Nginx as a reverse proxy, this pairs naturally with reverse proxy setup.
Deciding What Should Be Cached
The hardest part of Nginx caching is not writing the directives. It is deciding what content is safe to reuse.
Good caching candidates include:
- Public marketing pages.
- Public documentation pages.
- Product listing pages that update on a predictable schedule.
- Anonymous API responses.
- Expensive upstream responses that are identical for many users.
Poor caching candidates include:
- Account pages.
- Shopping carts.
- Admin screens.
- Responses that include private user data.
- Pages that change based on cookies or authorization headers.
If a response is different for each user, do not cache it unless you have a very clear cache key strategy. Accidentally serving one user's private response to another user is a serious bug.
You can bypass caching when requests include session or authorization data:
proxy_cache_bypass $http_authorization;
proxy_no_cache $http_authorization;
For cookie-based sessions, you may use a similar pattern:
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
The exact cookie name depends on your application. Do not copy this blindly without checking how your app handles sessions.
A practical scenario: your public blog homepage is generated by an application and takes 300 milliseconds on a busy day. If you cache that page for 5 minutes, most visitors receive the cached copy quickly, and the app only regenerates it occasionally. That is a strong use case because the homepage is public and not user-specific.
Cache Keys, Headers, and Purging
Nginx uses a cache key to decide whether two requests should share the same cached response. The default cache key is usually based on scheme, method, host, and URI. For many sites, that is enough.
If query strings change the response, make sure they are part of the key. If query strings are only tracking parameters, you may want to normalize them at the application or CDN layer instead of letting every utm_source create a separate cache entry.
Upstream cache headers also matter. Your app can send headers such as:
Cache-Control: public, max-age=600
or:
Cache-Control: private, no-store
Nginx can be configured to respect or override these headers, but you should choose one clear policy. If app developers expect Cache-Control: no-store to prevent caching, overriding that behavior at Nginx can create confusing and risky results.
Purging is another operational question. Open source Nginx does not include a simple built-in cache purge endpoint in the same way some commercial or third-party modules do. Many teams handle this by using short cache durations, versioned URLs, or deployment scripts that clear the cache directory during controlled releases.
Short TTLs are often enough. A 60-second cache on a busy endpoint can still remove a huge amount of backend traffic while keeping content reasonably fresh.
Testing Cache Behavior
After enabling caching, request the same URL several times and inspect the response headers:
curl -I https://example.com/
If you added X-Cache-Status, the first request may show MISS, and later requests should show HIT. If every request is a MISS, check response headers, cache bypass rules, request cookies, and whether the cache directory is writable by the Nginx worker process.
Also test logged-in and logged-out behavior. This is where many caching mistakes show up. Open a private browser window, sign in as a test user, and confirm private pages are not cached publicly.
Monitor disk usage too. A cache with no practical limit can fill a filesystem. Use max_size, keep cache storage separate from critical system partitions when possible, and alert on disk pressure.
When to Get Help
Bring in an experienced Nginx or platform engineer if cached content appears under the wrong user, if your cache hit rate stays low after tuning, or if cache files are filling disk unexpectedly. Caching problems can look simple while hiding application-specific behavior.
You should also get help before caching authenticated APIs, multi-tenant dashboards, or payment-related flows. Those areas need careful design.
Basic caching with Nginx works best when you start with public, repeatable responses and short cache durations. Add visible cache status headers during testing, respect private content boundaries, and measure both response time and backend load. Done well, caching gives users faster pages while giving your application room to breathe.