Nginx缓存加速实战:fastcgi_cache与proxy_cache配置及缓存清理指南


一、缓存的本质:用磁盘/内存换响应时间

动态网站的每次请求都要经过"Nginx → PHP-FPM → MySQL"全链路处理,高并发下数据库首当其冲。缓存的思路很简单:把已经生成的页面或接口响应缓存起来,后续相同请求直接由 Nginx 命中返回,不再穿透到后端。一个合理的缓存命中率能减少 80% 以上的后端压力,响应时间从几百毫秒降到个位数毫秒。Nginx 提供两条缓存路线:proxy_cache(缓存上游代理响应,适合 API、反代场景)和 fastcgi_cache(缓存 FastCGI 如 PHP 的输出,适合动态建站)。

二、proxy_cache:反向代理层缓存

当你用 Nginx 反代到后端服务(如 Docker 内的应用、Java 接口、上游 CDN 源站)时,可在 Nginx 层直接缓存响应:

http {
    # 定义缓存目录与共享内存,keys_zone 控制元数据内存
    proxy_cache_path /var/cache/nginx/proxy levels=1:2 keys_zone=proxy_cache:100m
                     inactive=60m max_size=2g;

    server {
        location / {
            proxy_pass http://backend;
            # 命中缓存直接返回,Miss 时向上游取数
            proxy_cache proxy_cache;
            proxy_cache_key "$scheme$host$request_uri";
            proxy_cache_valid 200 10m;      # 200 状态缓存 10 分钟
            proxy_cache_valid 404 1m;
            proxy_cache_use_stale error timeout updating;  # 上游异常时回退过期缓存
            add_header X-Cache-Status $upstream_cache_status;  # 便于观察命中情况
        }
    }
}

访问后看响应头 X-Cache-Status:HIT 表示命中缓存,MISS 表示未命中回源,EXPIRED/UPDATING 表示过期刷新中。这是排查缓存是否生效最直观的手段。inactive 是缓存条目多久不被访问即删除,max_size 限制磁盘占用,超出后 Nginx 自动淘汰最久未用的。

三、fastcgi_cache:给 PHP 页面加缓存

LNMP 环境里 PHP 页面走 FastCGI,用 fastcgi_cache 把生成的 HTML 直接缓存,比 proxy_cache 更贴近"页面级缓存":

http {
    fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2
                       keys_zone=php_cache:100m inactive=30m max_size=2g;

    server {
        location ~ \.php$ {
            fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

            # 开启页面缓存
            fastcgi_cache php_cache;
            fastcgi_cache_key "$request_method|$host|$request_uri";
            fastcgi_cache_valid 200 5m;      # 正常页面缓存 5 分钟
            # 绕过规则:带登录 Cookie 或 POST 请求不缓存
            fastcgi_cache_bypass  $cookie_userid $arg_nocache;
            fastcgi_no_cache     $cookie_userid $arg_nocache;
            add_header X-Fastcgi-Cache $upstream_cache_status;
        }
    }
}

两个关键的"旁路"指令务必理解:fastcgi_cache_bypass 让符合条件的请求(如已登录用户、带 nocache 参数)直接穿透到 PHP,fastcgi_no_cache 让这类响应不被写入缓存。否则登录用户的个性化内容会被缓存错发给其他用户,这是新手最容易犯的错误。

四、缓存 key 与分级缓存策略

缓存 key 决定"什么样的请求算同一个缓存"。fastcgi_cache_key 太宽(漏掉关键维度)会导致缓存串数据,太窄(带上每个参数)则命中率骤降。常见取舍:

# 页面缓存 key:方法 + 域名 + URI(忽略动态参数可提升命中,但要评估参数是否影响内容)
fastcgi_cache_key "$request_method|$host|$request_uri";

实践中的分级策略:

  • 静态资源(图片/CSS/JS):交给浏览器缓存 + Nginx expires 7d,几乎零成本。
  • 公开动态页(文章列表、首页):fastcgi_cache 缓存 1~10 分钟。
  • 接口数据:proxy_cache 按接口 TTL 缓存,或交给后端自己的缓存层。
  • 个性化内容:一律 bypass,靠数据库与 Redis 解决。

统一在 location / 上加 expires 指令给静态文件设置浏览器缓存:
location ~* \.(jpg|png|css|js|woff2)$ { expires 7d; access_log off; }

五、缓存清理:主动 purge 与过期淘汰

缓存不可能永远有效,内容更新后需要主动清理。三种常用手段:

# 1. 配置 proxy_cache_purge(需 ngx_cache_purge 模块,第三方)
# location /purge/ {
#     allow 127.0.0.1;
#     deny  all;
#     proxy_cache_purge proxy_cache "$scheme$host$request_uri";
# }

# 2. 在代码里变更后,直接删除缓存目录里对应的 key 文件
#    文件按 levels=1:2 分两级目录存放,可通过工具按 URL 反推路径删除

# 3. 让应用在发布/更新时修改 URL 版本参数(cache buster),旧缓存自然过期

没有 purge 模块时最稳妥的兜底是"版本号参数"与"较短的 TTL":把 TTL 压到可接受的更新延迟内(如 5 分钟),即使不主动清理,内容也会在数分钟内自动过期,不会造成长期错版。

六、缓存排查与避坑清单

# 命中情况统计
curl -I https://your-site.com/ | grep -i 'x-cache\|x-fastcgi'

# 查看缓存磁盘占用
du -sh /var/cache/nginx/*

高频坑与对策:

  • 缓存串号/缓存错数据:key 没包含区分维度(域名、语言、设备),务必细化 key。
  • 登录态被缓存:给带 Cookie 的请求加 bypass/no_cache。
  • HIT 率奇低:先看 key 是否带了过多动态参数,或 TTL 太短,或缓存目录权限不对(Nginx worker 需可写)。
  • 改了页面不生效:确认没有强缓存 + 后端 Cache-Control 未被忽略,必要时主动 purge。
  • HTTPS 与 HTTP 混合:key 含 $scheme,避免两种协议互相串缓存。

写在最后

缓存是性价比最高的性能优化手段之一,但"缓存与一致性"的平衡才是核心难点。务实的起步方案:静态资源交给浏览器与 expires,公开页面用 fastcgi_cache 配 5 分钟 TTL,接口按需 proxy_cache,登录与个性化请求一律绕过,再通过 X-Cache-Status 头观察命中率逐步调优。先跑起来、能监控、再收紧,比一开始就追求"极致命中率"更稳妥,也能让你的站点在流量突增时从容不迫。


手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

PHP-FPM性能调优实战:进程模式、max_children计算与慢日志定位

VPS服务器选购避坑指南:内存内存NVM带宽线路配置怎么选

评 论
更换验证码