设置Nginx HTTP缓存策略

通过为Nginx服务器配置HTTP缓存策略,可指示浏览器和中间代理(如CDN)缓存静态资源(如图片、CSS、JS 文件),并在缓存有效期内直接从本地加载,无需向服务器发起请求。此举可提升网站加载速度,并降低服务器的带宽消耗与处理压力。

常用缓存策略示例

本节提供常见的缓存配置方案,可根据场景直接应用于Nginx配置文件。所有示例均使用 add_header ... always;,确保在所有响应码(包括304 Not Modified)下都能添加缓存头。

场景一:为静态资源设置长期缓存

# 为静态资源设置长期缓存
# - 若文件名包含内容哈希(如 main.a1b2c3d4.js),推荐使用 1 年 + immutable
location ~* "\.[a-f0-9]{8,}\.(css|js|png|jpg|jpeg|gif|svg|webp|ico|woff|woff2)$" {
    # 适用于构建工具生成的带哈希资源:缓存1年,浏览器永不验证
    add_header Cache-Control "public, max-age=31536003, immutable" always;
    access_log off;
}

# - 若文件名固定且极少变更(如 logo.png),使用 30 天缓存
location ~* \.(css|js|png|jpg|jpeg|gif|svg|webp|ico|woff|woff2)$ {
    # 适用于无哈希的通用静态资源:缓存30天,允许CDN和浏览器缓存
    add_header Cache-Control "public, max-age=2592000" always;
    access_log off;
}

场景二:HTML文档或单页应用(SPA)入口配置缓存策略

对于HTML页面(尤其是单页应用的入口文件,如 index.html),由于其内容可能随应用部署频繁更新,不应设置长期缓存。但为兼顾性能,可允许浏览器缓存,并在每次使用前向服务器验证资源是否变更。

# 适用于直接请求的 HTML 文件
location ~* \.html$ {
    # 允许浏览器缓存,但每次使用前必须向服务器验证
    # 'private' 禁止中间代理(如CDN)缓存此响应
    # 服务器需提供 ETag 或 Last-Modified 以支持验证
    add_header Cache-Control "private, no-cache, must-revalidate" always;
}
说明
  • 此策略依赖服务器返回ETagLast-Modified响应头,以便浏览器发起条件请求。Nginx默认为静态文件提供这些头,无需额外配置。

  • 为防止中间代理(如 CDN)缓存HTML内容,建议使用"private"指令,确保仅浏览器可缓存。

场景三:禁止缓存动态内容或敏感信息

对于 API 接口、用户个人中心、支付页面等动态生成或包含敏感数据的内容,必须禁止浏览器及所有中间代理(如CDN、共享缓存)存储任何响应副本,以防止信息泄露或数据不一致。

# 示例:适用于 PHP 动态脚本(请根据实际动态路径调整)
location ~ \.php$ {
    # ... 其他 PHP-FPM 配置 ...

    # 禁止所有缓存:浏览器、代理、CDN 均不得存储响应
    # 'no-store' 是最严格的缓存控制指令
    add_header Cache-Control "no-store" always;
}

客户端缓存配置(控制浏览器)

通过在HTTP响应中添加Cache-ControlExpires头,控制用户浏览器的缓存行为。此模式旨在减少网络请求,加速终端用户访问。

核心指令

  • expires指令:用于同时设置ExpiresCache-Controlmax-age

    • 语法: expires [time|epoch|max|off];

    • 示例:expires 30d; (缓存 30 天), expires -1; (强制客户端在使用缓存前向服务器验证资源有效性(等效于 Cache-Control: no-cache),但允许缓存存储。)。

    • 注意:add_header 指令提供更精细的控制,是推荐的配置方式。

  • add_header指令:向响应中添加指定的HTTP头。

    • 语法:add_header <name> <value> [always];

    • always 参数说明
      - 默认情况下,add_header仅对2xx3xx响应生效。 - 对于304 Not Modified响应,Nginx不会自动添加自定义头。虽然浏览器会沿用首次200响应的缓存策略,但为确保明确性和兼容性,建议在缓存控制头中添加always参数,使其对所有响应状态码生效。

Cache-Control关键值说明

  • public:响应可以被任何缓存(浏览器、CDN、代理服务器)缓存。

  • private:仅允许最终用户的浏览器缓存,禁止共享缓存(如 CDN)。适用于包含用户特定信息的内容。

  • no-cache:要求客户端在每次使用缓存副本前,都必须向服务器发送请求验证其有效性。如果资源未变更,服务器返回304 Not Modified,客户端使用本地缓存,节省带宽。

  • no-store:禁止浏览器和代理服务器存储该响应的任何部分。适用于高度敏感的数据。

  • max-age=<seconds>:设置缓存的有效时间,单位为秒。

  • immutable:告知浏览器该资源内容在有效期内不会改变。在用户刷新页面时,浏览器可跳过对此资源的验证请求。适合带哈希值的文件。

部署与验证

  1. 编辑配置
    将 location 块添加到站点的 server 块中(配置文件通常位于 /etc/nginx/conf.d/ 或 /etc/nginx/sites-enabled/)。

  2. 重载配置

    sudo nginx -t && sudo nginx -s reload
  3. 验证响应头
    使用 curl 检查缓存头是否生效(不受浏览器缓存影响):

    curl -I http://your-domain.com/path/to/file.js

    应包含如 Cache-Control: public, max-age=31536000 等预期头。

  4. 验证304行为(如配置了ETagno-cache)
    手动携带验证头测试:

    ETAG=$(curl -I http://example.com/file.js 2>/dev/null | grep -i etag | cut -d' ' -f2 | tr -d '\r')
    curl -H "If-None-Match: $ETAG" -I http://example.com/file.js  # 期望返回 304
  5. 浏览器验证

    1. 开发者工具 → Network 面板

    2. 勾选Disable cache:查看首次加载(应为 200)

    3. 取消勾选后刷新:

      1. 无请求或显示 (from cache) → 强缓存命中

      2. 显示 304 → 协商缓存命中

常见问题

配置修改后未生效

原因:

  1. 未执行 sudo nginx -s reload

  2. 浏览器、CDN 或代理缓存了旧响应。

  3. location 匹配优先级问题

解决:

  1. 用 curl -I http://your-url 验证服务器实际响应头

  2. 检查 location 顺序:正则匹配(如 ~* \.(css|js)$)优先级高于前缀匹配(如 /static/),会导致请求被正则规则匹配而未命中前缀匹配的问题。

动态内容被错误缓存

原因:静态资源正则过于宽泛(如 ~* \.js$ 匹配了 /api/user.js

解决:

  1. 限定路径:location ~* ^/static/.*\.(css|js)$

  2. 确保动态接口(如 /api/\.php$)的 location 优先匹配或明确排除缓存

多个缓存策略冲突

  • 原因:多个 location 匹配同一请求,仅第一个生效

  • 解决:合并策略,使用 map 按内容类型动态设置缓存:

    # 在 http 块中
    map $sent_http_content_type $cache_control {
        ~^image/    "public, max-age=2592000";
        text/css    "public, max-age=2592000";
        application/javascript "public, max-age=2592000";
        default     "no-cache";
    }
    
    # 在 server 块中
    add_header Cache-Control $cache_control always;