Nginx反向代理缓存实战:proxy_cache_path配置与命中率排查

    发布时间:2026-09-15 12:32 更新时间:2026-09-15 12:32 阅读量:0

    网站流量上来之后,很多人第一反应是升级服务器配置,其实更省钱的做法是先给 Nginx 加一层反向代理缓存。它把后端 PHP、Node 或上游接口返回的内容暂存在本地磁盘,后续相同请求直接由 Nginx 响应,既减轻后端压力,也明显降低首字节时间。这篇就按实际部署顺序,讲清 proxy_cache_path 怎么写、缓存规则怎么分、命中率怎么看,以及缓存脏了怎么清。

    一、proxy_cache_path 与缓存区的基本配置

    Nginx 的缓存要先用 proxy_cache_path 定义一个存放目录和共享内存区,这条指令只能写在 http 块里,不能放进 server。共享内存区用于记录缓存键的索引,磁盘目录负责存响应体,两者缺一不可。

    http {
        proxy_cache_path /var/cache/nginx/site
                         levels=1:2
                         keys_zone=site_cache:20m
                         max_size=2g
                         inactive=7d
                         use_temp_path=off;
    
        server {
            listen 80;
            server_name www.example.com;
    
            location /static/ {
                proxy_pass http://127.0.0.1:8080;
                proxy_cache site_cache;
                proxy_cache_valid 200 304 7d;
                proxy_cache_key "$scheme$host$request_uri";
                proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
                add_header X-Cache-Status $upstream_cache_status;
            }
        }
    }

    几个参数的含义需要说清楚:levels=1:2 表示用两级子目录散列存放,避免单目录文件过多;keys_zone 的 20m 大约能记下十几万条键,具体以官方文档和实际条目数为准;max_size 是磁盘上限,inactive 是多久没被访问就淘汰;use_temp_path=off 让临时文件直接写缓存目录,减少一次跨目录 rename。目录要先手动创建并交给 Nginx 运行用户,否则启动会报权限错误。

    sudo mkdir -p /var/cache/nginx/site
    sudo chown -R www-data:www-data /var/cache/nginx/site
    sudo nginx -t && sudo systemctl reload nginx

    注意 proxy_cache_valid 只控制“缓存多久过期”,不代表一定会被缓存。默认情况下 Nginx 会遵循响应头里的 Cache-Control、Set-Cookie 等字段,遇到 Set-Cookie 或 private 通常就不缓存了,这正是很多动态接口配了却不命中的原因。

    二、静态资源与接口分开设策略

    静态资源和接口的缓存逻辑完全不同,建议用两个 location、两个 keys_zone 分开管理,排查问题时互不干扰。静态资源可以缓得久一点,接口则要短、要能主动失效。

    proxy_cache_path /var/cache/nginx/api levels=1:2 keys_zone=api_cache:10m
                     max_size=500m inactive=10m use_temp_path=off;
    
    location /api/public/ {
        proxy_pass http://127.0.0.1:9000;
        proxy_cache api_cache;
        proxy_cache_key "$host$request_uri";
        proxy_cache_valid 200 1m;
        proxy_cache_methods GET HEAD;
        proxy_ignore_headers Cache-Control Expires Set-Cookie;
        proxy_cache_lock on;
        proxy_cache_lock_timeout 5s;
        add_header X-Cache-Status $upstream_cache_status;
    }

    这里有两个容易踩的点。一是 proxy_cache_methods 默认只缓存 GET 和 HEAD,POST 请求不会被缓存,别指望用它缓存提交类接口。二是 proxy_ignore_headers 会强行忽略后端下发的缓存控制头,仅适合那些你不希望浏览器缓存、但服务端可以短缓的公开接口,像带登录态、含用户隐私的接口千万别这么写,否则不同用户可能拿到同一份缓存。proxy_cache_lock 用来避免缓存过期瞬间大量请求同时回源,也就是常说的缓存击穿。

    另外,带 Cookie 或 Authorization 的请求如果要缓存,需要配合 proxy_cache_key 把身份信息纳入键值,否则宁可加一条 proxy_no_cache $http_authorization 直接放行。涉及登录、支付、订单状态的接口,建议明确不缓存。

    三、命中率查看与缓存清理

    判断缓存有没有生效,最直接的依据是响应头里的 $upstream_cache_status,常见取值有 HIT(命中)、MISS(未命中)、EXPIRED(过期回源)、BYPASS、STALE。用 curl 看一眼即可:

    curl -I https://www.example.com/static/app.js | grep -i x-cache-status

    想统计整体命中率,可以临时定义一个日志格式,把状态记进访问日志,再用 awk 汇总。注意日志格式需要写在 http 块,改完 reload 生效。

    log_format cache_log '$remote_addr $request_uri $upstream_cache_status';
    access_log /var/log/nginx/cache.log cache_log;
    awk '{print $3}' /var/log/nginx/cache.log | sort | uniq -c | sort -rn

    如果还想看共享内存区里缓存了多少条目、占用多少空间,可以开启 stub_status 模块,它属于 http_stub_status_module,编译时是否包含以 nginx -V 输出为准。

    location = /basic_status {
        stub_status;
        allow 127.0.0.1;
        deny all;
    }

    访问该地址能看到活动连接、请求数等信息,但它不直接给出缓存条目数,缓存的具体状态还是以日志和 X-Cache-Status 为准,需要更细的指标可考虑第三方模块或 Prometheus exporter。

    缓存脏数据主要来自两类情况:后端内容更新了但缓存还在,以及配置改动后旧键残留。清理方式有三种。最温和的是按目录删文件,因为缓存文件名是 MD5 后的键,没法按 URL 精确删,只能整目录清空:

    sudo find /var/cache/nginx/site -type f -delete
    sudo systemctl reload nginx

    第二种是借助 ngx_cache_purge 等第三方模块,通过一个带 PURGE 方法的 location 精确清除指定 URL,这需要重新编译 Nginx 或使用带该模块的发行版包,是否支持以实际环境为准。第三种是干脆给缓存加版本前缀,比如把 proxy_cache_key 改成 "$host$request_uri$cache_version",内容大改时在配置里改一下版本号再 reload,旧缓存自然被绕过,等 inactive 到期自动回收。生产上推荐第三种,改动小、风险低。

    最后提醒一句:上线缓存前先在测试域名上跑一轮,确认登录态接口、后台路径没有被误缓存,再逐步放大缓存时长。缓存带来的收益很直观,但前提是边界划清楚,该绕过的请求一个都不能省。

    继续阅读

    📑 📅
    大文件上传总失败:php.ini与Nginx三处限制如何协调放行 2026-09-14
    phpMyAdmin导入大SQL超时:参数调整与命令行导入 2026-09-12
    HTTPS证书有效却提示不安全:混合内容的定位与批量修复 2026-09-12
    服务器磁盘被写满的排查流程:df与du定位、日志切割与清理注意事项 2026-09-11
    宝塔面板安全加固:面板端口、安全入口、SSL与登录告警设置 2026-09-11
    Let's Encrypt证书自动续期失败排查:certbot renew定时任务与webroot验证 2026-09-15
    WordPress数据库膨胀排查:wp_options自动加载与孤立表清理 2026-09-15
    宝塔面板计划任务做网站自动备份:数据库与文件打包、异地存储配置 2026-09-15
    CDN回源配置怎么填:回源Host、回源协议与真实IP排查 2026-09-16
    WordPress固定链接改版后老链接404:rewrite与301重定向保住收录 2026-09-16