禁用nginx缓存JavaScript文件

78

好的,我对此几乎已经放弃了,但我该如何禁用Nginx对JavaScript文件的缓存呢?我正在使用带有Nginx的docker容器。当我改变JavaScript文件中的内容时,我需要多次重新加载才能看到新文件。

我怎么知道是Nginx而不是浏览器/docker的问题呢?

浏览器:我在命令行上使用curl模拟请求时也遇到了同样的问题。此外,我正在使用一个CacheKiller插件,并且在Chrome Dev Tools中禁用了缓存。

Docker:当我连接到容器的bash并在更改文件后使用cat时,我立即得到了正确的结果。

我将sites-enabled下的nginx.conf更改为以下内容(我在另一个stackoverflow线程中找到):

location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|xml|html|htm)$ {
    # clear all access_log directives for the current level
    access_log off;
    add_header Cache-Control no-cache;
    # set the Expires header to 31 December 2037 23:59:59 GMT, and the Cache-Control max-age to 10 years
    expires 1s;
}

然而,在重建容器后(并确保它在带有cat的容器中),它仍然没有起作用。这里是完整的.conf文件。

server {
    server_name app;
    root /var/www/app/web;

    # Redirect to blog
    location ~* ^/blog {
        proxy_set_header Accept-Encoding "";
        sub_filter 'https://testproject.wordpress.com/' '/blog/';
        sub_filter_once off;
        rewrite ^/blog/(.*) /$1 break;
        rewrite ^/blog / break;
        proxy_pass     https://testproject.wordpress.com;
    }

    # Serve index.html only for exact root URL
    location / {
        try_files $uri /app_dev.php$is_args$args;
    }

    location ~ ^/(app|app_dev|config)\.php(/|$) {
        fastcgi_pass php-upstream;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param HTTPS off;
        # Prevents URIs that include the front controller. This will 404:
        # http://domain.tld/app_dev.php/some-path
        # Remove the internal directive to allow URIs like this
        internal;
    }

    location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|xml|html|htm)$ {
        # clear all access_log directives for the current level
        access_log off;
        add_header Cache-Control no-cache;
        # set the Expires header to 31 December 2037 23:59:59 GMT, and the Cache-Control max-age to 10 years
        expires 1s;
    }


    error_log /var/log/nginx/app_error.log;
    access_log /var/log/nginx/app_access.log;
}
7个回答

138

我有以下nginx虚拟主机(静态内容)用于本地开发工作,以禁用所有浏览器缓存:

server {
    listen 8080;
    server_name localhost;

    location / {
        root /your/site/public;
        index index.html;

        # kill cache
        add_header Last-Modified $date_gmt;
        add_header Cache-Control 'no-store, no-cache';
        if_modified_since off;
        expires off;
        etag off;
    }
}

没有发送缓存头:

$ curl -I http://localhost:8080
HTTP/1.1 200 OK
Server: nginx/1.12.1
Date: Mon, 24 Jul 2017 16:19:30 GMT
Content-Type: text/html
Content-Length: 2076
Connection: keep-alive
Last-Modified: Monday, 24-Jul-2017 16:19:30 GMT
Cache-Control: no-store
Accept-Ranges: bytes

Last-Modified 始终为当前时间。

注意: nginx 的 $date_gmt 格式不符合 HTTP 规范(参见更改格式)。

要禁用特定文件扩展名的缓存(例如 OP 请求的 JS 文件):

location ~* \.js$ {
    expires -1;
}
请查看下面Nitai的回答以扩展文件扩展名列表 - 使用非捕获组正则表达式模式。

13
你只需要使用no-store。来自MDN:虽然除了no-store之外还可以设置其他指令,但在现代浏览器中,只需使用这个指令就足以防止缓存响应。 max-age=0已经被隐含地包含在其中。设置must-revalidate没有意义,因为为了进行重新验证,需要将响应存储在缓存中,而no-store可以防止这种情况发生。 - felixbade
@hareshhanat 谢谢,你可以告诉我的 Bash 终端(当时)这个问题。由于这不是解决方案的一部分(只是示例输出),希望它不会引起问题。 - Greg K
@GregK 它会创建问题,而且由于最后修改日期格式不正确,仍会缓存静态文件。 - H_H
我发现nginx的$date_gmt格式与HTTP头规范不匹配。看起来解决方案包括一个服务器端包含模块。 - Greg K
编辑:已找到,这是关于IntelliJ IDEA的,它会自动保存文件,但可能没有设置一些时间标志或者有延迟。 - ra74
显示剩余4条评论

29
expiresadd_header指令对NGINX缓存文件没有影响,它们只影响浏览器看到的内容。
您可能需要的是:
location stuffyoudontwanttocache {
    # don't cache it
    proxy_no_cache 1;
    # even if cached, don't try to use it
    proxy_cache_bypass 1; 
}

通常情况下,您会缓存 .js 等内容,因此也许应该完全禁用缓存?


我刚刚将你的代码添加到我的app.conf中,但现在nginx甚至无法启动。当我使用nginx -t命令时,出现错误unknown directive "proxy_no_store" - Musterknabe
1
您是想说 proxy_no_cache 吗? - Musterknabe
1
遗憾的是,即使使用了“proxy_no_cache”,我仍然得到相同的错误 :/ - Musterknabe
1
阅读此nginx文档 - user4535610
1
我已经尝试过了,这就是为什么我问他是否指的是proxy_no_cache而不是proxy_no_store。我将其设置为非0值(1),但它仍然无效。 - Musterknabe
显示剩余4条评论

21

你所需要的是像这样简单的指令:

location ~* \.(?:manifest|appcache|html?|xml|json)$ {
    expires -1;
}

上述内容不会缓存()内的扩展名。您可以为不同的文件类型配置不同的指令。


这个答案比被采纳的回答更好地回答了原帖问题中特定的“针对JavaScript文件”的部分。虽然原帖的作者可能对被采纳的回答感到满意,但是那些因为问题中的“针对JavaScript文件”而来到这里的人会发现@Nitai的回答更相关。点赞。 - Bryton Beesley
谢谢,我写了expires max; - undefined

7

记得设置sendfile off;,否则缓存头将无法正常工作。我使用以下代码片段:

location / {

        index index.php index.html index.htm;
        try_files $uri $uri/ =404; #.s. el /index.html para html5Mode de angular

        #.s. kill cache. use in dev
        sendfile off;
        add_header Last-Modified $date_gmt;
        add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
        if_modified_since off;
        expires off;
        etag off;
        proxy_no_cache 1;
        proxy_cache_bypass 1; 
    }

$date_gmt是什么,它是你在脚本中声明的那个吗? - vijay

3
我有以下用于本地开发的 Nginx 虚拟主机(静态内容),以禁用所有浏览器缓存:
    upstream testCom
        {
         server localhost:1338;
        }

    server
        {

            listen 80;
            server_name <your ip or domain>;
            location / {

            # proxy_cache   datacache;
            proxy_cache_key $scheme$host$request_method$request_uri;
            proxy_cache_valid       200     60m;
            proxy_cache_min_uses    1;
            proxy_cache_use_stale   updating;

            proxy_pass_header Server;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Scheme $scheme;

            proxy_ignore_headers    Set-Cookie;

            userid          on;
            userid_name     __uid;
            userid_domain   <your ip or domain>;
            userid_path     /;
            userid_expires  max;
            userid_p3p      'policyref="/w3c/p3p.xml", CP="CUR ADM OUR NOR STA NID"';


            add_header Last-Modified $date_gmt;
            add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
            if_modified_since off;
            expires off;
            etag off;

            proxy_pass http://testCom;
        }
    }

0

我知道这个问题有点旧了,但我建议在 JavaScript 的 URL 中使用一些缓存哈希。这在生产环境和开发期间都可以完美地运行,因为您可以具有无限的缓存时间以及当更改发生时即时更新。

假设您有一个 JavaScript 文件 /js/script.min.js, 但在引用 HTML/PHP 文件中,您并没有使用实际路径而是:

<script src="/js/script.<?php echo md5(filemtime('/js/script.min.js')); ?>.min.js"></script>

每次文件更改时,浏览器得到不同的url,这意味着它无法在本地或任何代理服务器上缓存。为了使其正常工作,您需要使用nginx将任何请求重写为/ js / script。[0-9a-f]{32} .min.js到原始文件名。在我的情况下,我使用以下指令(对于CSS也是如此):
location ~* \.(css|js)$ {
                expires max;
                add_header Pragma public;
                etag off;
                add_header Cache-Control "public";
                add_header Last-Modified "";
                rewrite  "^/(.*)\/(style|script)\.min\.([\d\w]{32})\.(js|css)$" /$1/$2.min.$4 break;
        }

我猜测filemtime调用在服务器上甚至不需要磁盘访问,因为它应该在Linux的文件缓存中。如果您有疑虑或静态HTML文件,您还可以使用一个固定的随机值(或增量或内容哈希),当您的JavaScript / CSS预处理器完成时更新它,或者让您的git钩子更改它。

理论上,您也可以使用缓存破坏器作为虚拟参数(例如/js/script.min.js?cachebreak=0123456789abcfef),但是由于“”,该文件至少不会被某些代理缓存。


嘿,Florian,我有一个问题,每当我部署供应商的 CSS,需要大约 3 到 4 分钟才能被应用程序看到,此时它会返回 401。你知道为什么吗?过了大约 3 到 4 分钟后,你不停地刷新浏览器,最终它就会出现。这种情况发生在 CSS 文件上……你见过这种情况吗?你会如何解决它? - james emanon
你是否使用了一些CDN或者服务工作线程来进行缓存管理?你可以尝试使用wget或curl并使用任意随机的MD5值进行测试。由于nginx配置将任何32个字符的a-f0-9值映射到文件中,因此它应该完全独立于部署方式,无论它是否被引用过。 - Florian
似乎存在问题的文件是通过Webpack创建的哈希文件,并且它们位于CDN上。 - james emanon
您可以通过将PHP更改为使用查询字符串来避免nginx规则。.../js/script.min.js?x=<?php echo md5(filemtime('/js/script.min.js')) ?> - fidian

0

缓存是一件好事,但有时您想无论如何都重新加载文件。

因此,在生产环境中,我们只需定义文件版本并定义浏览器应尽可能长时间地缓存该文件。

例如:myFile.v11.js

对于调试环境,您只需在浏览器中禁用缓存,并在页面上工作时按下强制刷新即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接