Why Your Webpack App Still Caches Old Files and How Nginx Can Fix It

This article explains why modern Webpack applications still suffer from stale browser caches, analyzes the shortcomings of typical cache‑busting tactics, and provides a complete Nginx configuration that ensures HTML never caches while static assets with hashes are cached indefinitely.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Why Your Webpack App Still Caches Old Files and How Nginx Can Fix It

1. The "Perfect" Illusion: Common Cache Strategies

Before diving into the problem, let’s review the usual cache‑busting tactics many projects adopt, which seem flawless at first glance:

First tactic: Filename hashing

Webpack and similar build tools generate unique hash‑based filenames for JS, CSS, and other static assets.

main.58d91471.js
runtime.0bb7b510.js
vendor.ant-design.59d332b0.js

In theory, a change in file content changes the hash, prompting the browser to request the new file.

Second tactic: Meta tags in the HTML entry file

In index.html ’s <head> we add every possible meta tag that forbids caching.

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">

This looks like we are telling the browser: “Hey, don’t cache me! Always fetch the latest from the server.”

Beautiful idea vs. harsh reality

Our logic is clear:

index.html is not cached, so every visit gets the latest version.

The latest index.html references JS/CSS files with new hashes.

The browser sees the changed filenames and loads the new resources.

The user sees the updated page – perfect!

However, the reality hits hard: Users still see the old version. Where does the problem lie?

2. Uncovering the Truth: The Overlooked "Highest Directive"

To solve the mystery we must understand the priority of browser cache policies. The key point is:

HTTP response headers (Response Headers) have higher priority than HTML meta tags.

Meta tags are merely suggestions. The server‑sent Cache-Control and Expires headers are the authoritative directives that browsers must obey. If these headers are absent or allow caching, the browser will ignore the meta tags and cache index.html.

The culprit: Nginx’s default behavior

Examining a typical Nginx configuration reveals the problem:

# Problem 1: This rule works for images, JS, CSS, etc., but HTML files are completely missed!
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg)$ {
    expires 1d;
    add_header Cache-Control "public, max-age=86400";
}

# Problem 2: Core block handling all route requests – but...
location / {
    try_files $uri $uri/ /index.html;
    # 🔴 Fatal omission: No cache‑control directives for index.html here!
}

Consequences:

Requests to https://yoursite.com/ match the location / rule.

Nginx returns index.html without any Cache-Control or Expires headers .

The browser or upstream CDN sees the silent response and applies its default caching, storing index.html for a period.

After a new deployment, the hashed JS files change, but the browser still serves the cached old index.html, which references the old JS files.

The user continues to see the stale version and the bug persists.

The whole loop forms a perfect circle where meta tags have no effect.

3. The Ultimate Solution: Fine‑Grained Nginx Cache Rules

Having identified the root cause, the fix is straightforward: use Nginx to issue explicit cache directives for different file types.

Below is a production‑ready Nginx configuration:

server {
    listen 80;
    server_name your.domain.com; # replace with your domain
    root /usr/share/nginx/html;   # replace with your project root

    # Rule 1: HTML files – never cache
    location = /index.html {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
        add_header Expires "0";
    }

    # Rule 2: Hashed static assets – cache forever
    location ~* \.[a-f0-9]{8}\.(css|js)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # Rule 3: Other static assets (images, fonts) – long‑term cache
    location ~* \.(jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf)$ {
        expires 30d;
        add_header Cache-Control "public";
    }

    # Rule 4: SPA route handling
    location / {
        try_files $uri $uri/ /index.html;
    }
}

Configuration Breakdown

location = /index.html : Exact match forces the entry HTML to never be cached – the core of the strategy.

location ~* \.[a-f0-9]{8}\.(css|js)$ : Matches files with an 8‑character hash, sets a one‑year cache and the immutable flag for optimal performance.

location ~* \.(jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf)$ : Provides a long cache for images and fonts that usually don’t change often.

location / : Fallback for SPA routing; all unmatched requests are served by the non‑cached index.html.

Apply this configuration to your environment‑specific Nginx files (development, testing, production, preview) and adjust the expires values as needed.

4. Don’t Forget the CDN

If your app sits behind a CDN, ensure the CDN’s cache policy respects the origin’s Cache-Control headers. Typically you need to enable the “follow origin cache‑control” option.

HTML files – TTL 0 seconds, obey origin.

Hashed JS/CSS – TTL 31536000 seconds (1 year), obey origin.

Images / fonts – TTL 2592000 seconds (30 days), obey origin.

Conclusion

The cache problem in web applications may seem mystical, but the logic is clear: HTTP response headers are the sole authority for cache control. By implementing a precise Nginx cache strategy that makes the entry HTML never cache and allows hashed static assets to be cached indefinitely, you eliminate stale‑content issues and maximize performance.

Now is the time to audit your Nginx configuration, say goodbye to “please clear your cache”, and enjoy seamless deployments.

frontendWebpackcaching-strategyBrowser Cache
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.