October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
VGSources
Blog

How to Enable Browser Caching on Nginx (Correct Headers, Policies, and Troubleshooting)

Set Nginx cache headers by resource type: cache fingerprinted assets for a year with public immutable, revalidate HTML with no-cache and validators, and verify the effective response with curl.
Length9 min Posted Quest giverVGSources Team
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Enable browser caching in Nginx by returning cache headers from the location that serves each resource. Use a long-lived, immutable policy only for fingerprinted files whose URLs change when their bytes change; use no-cache with validators for HTML and other responses that can change at the same URL.

What browser caching on Nginx actually does

A browser decides whether it may reuse a response from its local cache by reading HTTP response headers. Nginx does not “turn on” a separate browser-cache switch. Instead, the Nginx location serving a file sends Cache-Control and, where applicable, Expires, ETag, or Last-Modified.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The Nginx ngx_http_headers_module provides the expires directive for generated expiration policy and add_header for explicit or arbitrary response fields. Both directives can be used in http, server, and location contexts; add_header is also valid in an if block inside a location. Put a rule as close as possible to the files it governs so unrelated responses do not receive the wrong policy.

Browser caching and Nginx proxy caching are different features. Browser caching controls what a user agent stores after receiving a response. Proxy caching stores upstream responses on the Nginx server and requires directives such as proxy_cache_path, proxy_cache, and proxy_cache_valid. Configuring one does not configure the other.

Choose a policy before writing the configuration

The right lifetime depends on whether the URL identifies one immutable version of a file or may continue to point to changing content. A one-year lifetime is safe for a URL that changes whenever the bytes change; it is risky for a stable URL such as /index.html.

Resource Recommended policy Why
Fingerprint-based CSS, JavaScript, images, and fonts public, max-age=31536000, immutable The filename contains a content hash or version, so a deployment publishes a new URL when the bytes change. The old URL can remain cached.
Unversioned CSS or JavaScript Short max-age or no-cache The same URL can begin serving new bytes, so a browser must not retain an old representation for a year.
HTML entry documents no-cache with ETag and/or Last-Modified The browser may store the document but must validate it before reuse, allowing a 304 Not Modified response when unchanged.
Personalized or sensitive responses An application-appropriate private or no-store policy Do not mark user-specific data as public merely to save bandwidth; shared caches could expose another user’s response.

no-cache does not mean “never store.” It means that a stored response must be revalidated before it is reused. no-store is the directive intended when a response should not be stored.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Prepare Nginx and identify the correct location

Confirm which block serves the file

Nginx maps a request URI to a file using the root directive and the matching location block. A cache rule in the wrong block has no effect on the response you are inspecting. Start with the virtual host that serves the site and its document root, then identify whether assets are served by a prefix such as /assets/, a file-extension expression, or a more specific nested location.

Use the most specific applicable rule

Keep policies separated by resource type. A broad rule at server level can accidentally cache HTML, login responses, or API output. A dedicated location for hashed assets and another for documents makes the intended behavior visible and easier to test.

Understand inheritance before adding headers

Nginx header inheritance is not additive in every situation. A nested location containing its own add_header can stop inheriting add_header directives from a higher level. Review the complete effective configuration instead of assuming a new header is simply appended to the parent policy.

Apply a practical baseline configuration

The following server block keeps entry documents revalidatable and gives long-lived caching to static files only when their names contain a content hash or another version identifier. Replace the root path and host-specific details with your own values.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
server {
    root /var/www/example;

    # Documents can change at the same URL.
    location ~* .html$ {
        expires -1;
        add_header Cache-Control "no-cache";
    }

    # Use this only for versioned or fingerprinted filenames.
    location ~* .(css|js|png|jpg|jpeg|gif|svg|webp|woff2?)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

expires 1y produces a positive max-age and a future Expires value. expires -1 produces a Cache-Control: no-cache policy. Nginx also supports the special values epoch, max, and off. Inspect the resulting headers rather than assuming the directive’s source spelling is what the browser receives.

The one-year example corresponds to 31,536,000 seconds. It is a configuration example, not a measured performance guarantee or a universal requirement. Choose a shorter lifetime when your deployment process cannot reliably change URLs as bytes change.

Use fingerprinted assets with long-lived caching

Make the URL the version identifier

A fingerprinted filename might look like app.abc123.js or styles.v42.css. Your build or release process must emit a new name whenever the content changes and update every HTML reference to that name. Once that invariant is reliable, a browser can safely reuse the old URL for its entire freshness lifetime.

Add immutable deliberately

immutable tells a cache that a fresh response will not change, avoiding unnecessary validation requests while it remains fresh. It is appropriate for a URL whose bytes are never replaced. Do not add it to index.html, an unversioned bundle, or any endpoint that can change while retaining its URL.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Do not hide deployment mistakes with a long TTL

If users report an old script after a release, first check whether the HTML still references the old fingerprint. Purging a browser cache is not a deployment strategy. Correct URL versioning lets old documents finish naturally while new documents point to new assets.

Keep HTML and changing resources revalidatable

HTML entry points

For an entry document that remains at the same URL, use Cache-Control: no-cache. The browser may keep the body locally, but it asks Nginx whether the representation changed before displaying it from cache. This avoids retransmitting an unchanged document while allowing a deployment to take effect promptly.

Unversioned assets

If a stylesheet or script is overwritten in place, use a short freshness period or no-cache. A long max-age would allow a browser to use stale bytes without contacting the server until the lifetime expires.

APIs and personalized responses

Do not apply a public static-asset rule to API responses, account pages, or other user-specific output. Decide whether the response may be shared, only stored in a private browser cache, or not stored at all. The application’s privacy requirements determine whether private, no-store, or a more specific combination is appropriate.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose between expires and explicit Cache-Control

When expires is convenient

expires calculates an expiration date and emits both an Expires field and a corresponding cache-control policy. Relative values such as 1y are concise when one consistent lifetime is desired, and negative values provide a simple revalidation policy.

When add_header is clearer

Use add_header when you want to state the exact policy, combine directives, or set a field that Nginx cannot derive from a duration. For example:

location /assets/ {
    add_header Cache-Control "public, max-age=604800";
}

This sets a seven-day freshness lifetime in seconds. It does not make an unversioned file immutable; the URL-versioning decision remains yours.

Do not create contradictory policies accidentally

If both directives are present, inspect the actual response and remove ambiguity. A generated max-age from expires and an explicit Cache-Control field should express the same intention. The browser follows the response it receives, not the policy you intended in a different configuration file.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Control which responses receive a header

add_header follows Nginx’s normal status-code handling and inheritance rules. A header may appear on successful responses but not on every error response unless you request that behavior. Use the always parameter when a policy must also be added outside the module’s default status list, for example:

location /assets/ {
    add_header Cache-Control "public, max-age=604800" always;
}

Use always with care. An error response should not automatically become publicly cacheable if it contains sensitive diagnostics or a user-specific message. Test both successful and failing requests when you depend on this parameter.

Make validators enable efficient revalidation

Revalidation is most useful when the response includes ETag and/or Last-Modified. After receiving a no-cache response, a browser can send If-None-Match or If-Modified-Since. If the representation is unchanged, Nginx can answer 304 Not Modified without retransmitting the body. If it changed, the server returns the new representation and updated metadata.

Do not confuse a 304 response with a cache hit that required no request. A fresh immutable asset can be used without validation; a no-cache response normally requires a conditional request before reuse.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Verify the effective behavior

Always test the response that a real browser receives. Configuration syntax alone does not prove that the request selected the expected location or that another layer did not replace a header.

  1. Validate the configuration with nginx -t.
  2. Reload Nginx through the service manager used by your host after the test succeeds.
  3. Inspect a representative fingerprinted asset: curl -I https://example.test/assets/app.abc123.js.
  4. Inspect the entry document separately: curl -I https://example.test/.
  5. Confirm that Cache-Control and Expires match the intended location, and record any ETag or Last-Modified value.
  6. For a revalidation test, send the captured validator, for example curl -I -H "If-None-Match: ..." https://example.test/, and look for 304 Not Modified when the representation is unchanged.
  7. Test a successful response, an error response if you use always, a fingerprinted URL, and an otherwise identical unversioned URL.

Browser developer tools can corroborate the result, but distinguish “memory cache” or “disk cache” reuse from a network response. Disable-cache options in a developer-tools panel often change the test conditions and should not be mistaken for normal visitor behavior.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot a browser that keeps downloading the same asset

The URL is not actually fresh

Check the response headers with curl -I, not only the Nginx file. If the response has Cache-Control: no-cache, a conditional request on each use is expected. That is revalidation, not necessarily a full download; a 304 response carries no new body.

A different location won

Regular-expression and nested locations can select a block other than the one you edited. Compare the requested URI with the exact location pattern and inspect the complete server configuration. A rule for /assets/ will not affect a file served from another prefix.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A nested header replaced a parent header

Adding any add_header in a child location can change which parent headers are inherited. Review all add_header directives in the selected block and its ancestors, then define the complete policy in the most specific block.

Another layer changed the response

A CDN, reverse proxy, upstream application, or hosting control panel may add, replace, or remove cache headers after Nginx generates them. Compare headers at the public URL and, when possible, at the origin. The public response is the behavior the browser follows.

The file name is stable while the bytes change

A one-year policy on a stable URL is a cache-invalidation bug. Either publish a new fingerprinted URL for each change or shorten the policy and accept more validation requests.

The test is bypassing the cache

Private-browsing modes, developer-tools “disable cache” settings, service workers, and hard-refresh actions can produce behavior unlike a normal navigation. Repeat the test in a normal profile and inspect the network request and response headers.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Protect private and sensitive content

Public caching is appropriate for identical static bytes that every visitor may receive. It is not a safe default for account data, checkout pages, authorization responses, or responses containing personal information. Mark such responses with an application-appropriate private or no-store policy, and avoid broad extension-based locations that could capture dynamic output merely because its URL ends in a familiar suffix.

Also consider error pages. If you use add_header ... always, verify that an error body does not contain secrets or request-specific details that a shared cache could retain.

Browser cache versus Nginx proxy cache

These layers answer different questions:

Layer Where bytes are stored Main controls Typical purpose
Browser cache On the visitor’s device Response headers such as Cache-Control, Expires, validators, and request conditions Avoid repeat transfers to the same user.
Nginx proxy cache On the Nginx server or its configured cache storage proxy_cache_path, proxy_cache, proxy_cache_valid, and bypass rules Reuse an upstream response for multiple requests before contacting the application.

Enabling proxy caching does not cause a browser to cache the result, and browser headers do not create an Nginx proxy cache. Configure and verify each layer independently.

A deployment checklist

  • List which URLs are immutable fingerprints and which URLs can change in place.
  • Assign long-lived public caching only to the immutable group.
  • Use no-cache plus validators for HTML and other frequently changing, non-sensitive responses.
  • Keep personalized or sensitive output out of public caching.
  • Place directives in the location that actually serves each resource.
  • Review header inheritance whenever a nested location adds its own header.
  • Use always only when nonstandard status responses genuinely need the header.
  • Run nginx -t, reload through the normal service manager, and inspect representative public responses.
  • Test conditional requests and confirm 304 Not Modified for unchanged revalidated resources.
  • Check CDN, proxy, and application layers for header overrides.
  • During every release, verify that changed bytes receive changed URLs or an intentionally short/revalidated policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.