Ant Design Pro 管理后台更新版本时的浏览器缓存问题

文章目录

    这是困扰我很久的一个问题,每次更新 Ant Design Pro 开发的管理后台前端版本后,浏览器总是会缓存旧的文件,导致用户看到的仍然是旧版本的界面。
    或者某个子功能更新,更新后,如果用户没有刷新页面,直接访问这个功能,直接白屏报错。只有当用户手动刷新页面后才能看到更新后的界面。这个体验非常的不好。

    之前没怎么当回事,要么是因为后台更新频率不高,要么是后台用的人少,现在遇到了几个系统,用户量比较大,更新频率也比较高,这个问题就变得非常突出,必须要解决。

    解决思路

    问了一下 DeepSeek, 他说这个问题的根源在于浏览器缓存了旧的 index.html 文件,虽然其他静态资源(js、css)文件名里都带了哈希(例如,app.123456.js),理论上应该是不会被缓存的,但 index.html 没有带哈希,所以浏览器会一直缓存旧的 index.html 文件,导致用户看到的界面一直是旧版本的。

    处理前的请求返回

    HTTP/1.1 304 Not Modified
    Server: nginx/1.24.0 (Ubuntu)
    Date: Tue, 31 Mar 2026 09:06:17 GMT
    Last-Modified: Mon, 02 Feb 2026 08:35:01 GMT
    Connection: keep-alive
    ETag: "698061b5-1881"
    

    新的 Nginx 配置

    server {
        listen       80;
        server_name  _;
    
        client_max_body_size 8M;
    
        # 前端静态文件根目录(提到 server 层)
        root   /home/some_user/some_project/frontend/dist;
        index  index.html index.htm;
    
        # API 请求转发到后端 golang 服务
        location /api/ {
            proxy_set_header X-Forward-For $remote_addr;
            proxy_set_header  X-real-ip $remote_addr;
            proxy_pass http://127.0.0.1:9999/api/;
        }
    
        # 所有 HTML 文件强制不缓存。存在子功能对应目录也包含 index.html 的情况,所以不能只针对根目录的 index.html 进行处理。
        location ~* \.html$ {
            add_header Cache-Control "no-cache, no-store, must-revalidate";
            add_header Pragma "no-cache";
            add_header Expires 0;
            try_files $uri =404;
        }
    
        # SPA 路由处理:其他所有请求返回 index.html
        location / {
            try_files $uri $uri/ /index.html =404;
        }
    }
    

    检查 Nginx 配置,并 reload Nginx:

    sudo nginx -t
    sudo nginx -s reload
    

    处理后的请求返回

    HTTP/1.1 200 OK
    Server: nginx/1.24.0 (Ubuntu)
    Date: Tue, 31 Mar 2026 08:33:30 GMT
    Content-Type: text/html
    Last-Modified: Mon, 02 Feb 2026 08:35:01 GMT
    Transfer-Encoding: chunked
    Connection: keep-alive
    ETag: W/"698061b5-1881"
    Cache-Control: no-cache, no-store, must-revalidate
    Pragma: no-cache
    Expires: 0
    Content-Encoding: gzip
    

    项目目录结构参考

    我发现 Ant Design Pro 的前端项目目录结构中,index.html 文件是放在 dist 目录下的,同时每个子功能也有对应的子目录,里面也包含一个 index.html 文件。

    而 Vue Element 则只有一个根目录的 index.html 文件,没有子功能目录下的 index.html 文件,其他都在 js 目录下。

    修改后文件的 etag 是不同的,为啥还会被缓存呢?

    虽然 index.html 文件的缓存问题解决了,但是我不太理解,为何修改前的返回里,也有文件的 etag 和修改时间等信息,且 etag 也不一样了,但浏览器还是会缓存旧的 index.html 文件呢?

    原因是没有设置 Cache-Control: no-cache 等响应头,浏览器依然可能使用旧的缓存,而不会主动向服务器验证。

    当浏览器缓存了一个资源(比如 index.html),再次请求时,是否发送验证请求取决于缓存响应的 Cache-Control 和 Expires 头。

    如果响应头中带有 Cache-Control: no-cache(或 must-revalidate)。浏览器每次都会向服务器发送验证请求(带上 If-None-Match 或 If-Modified-Since),服务器根据 ETag 或 Last-Modified 决定返回 200(新内容)还是 304(未修改,使用缓存)。

    如果没有 Cache-Control 或 Expires 明确指示。浏览器会使用启发式缓存(Heuristic Caching)。例如,根据 Last-Modified 时间,浏览器可能认为资源在 (当前时间 - 上次修改时间) * 10% 的时间内是新鲜的,期间根本不会发送请求,直接使用本地缓存。这就会导致即使服务器上的文件已更新(ETag 变了),浏览器也不去验证,用户看到的还是旧页面。

    而用户刷新页面(F5 或 Ctrl+R):浏览器通常会忽略启发式缓存,强制发送验证请求(带上 If-None-Match),这时 ETag 不同就会返回新内容。

    ETag 的作用

    ETag 本身并不控制是否验证,它只用于验证时的比较。如果没有验证,ETag 再变也无用。因此,对于 index.html 这类必须实时更新的文件,必须设置 Cache-Control: no-cache(或其他强制验证的指令),让浏览器每次都必须向服务器询问,确保及时拿到最新版本。

    Nginx 默认的 ETag 格式:

    ETag = {文件最后修改时间的十六进制} - {文件内容长度的十六进制}

    详情参考:Golang Gin Static 缓存大坑:embed 文件无法被 CDN 和浏览器缓存

    关于作者 🌱

    我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊,或者关注我的个人公众号“大象工具”, 查看更多联系方式