搜索全站
性能优化 13 分钟阅读

Nginx FastCGI 缓存加速 WordPress:缓存规则与排除登录用户

装了缓存插件还是慢?试试服务器层的 Nginx FastCGI 缓存,不走 PHP 直接返回 HTML。本文给出完整配置、缓存键设计、登录用户与购物车排除规则,以及缓存不生效的排查清单。

U
UFO_PF 2019-07-08 · 阅读 13 分钟
Ng 性能优化
阅读提示
本文约 5,129 字,预计阅读 13 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:插件缓存已经到瓶颈了

很多站长在 WordPress 上装了缓存插件(WP Super Cache、W3 Total Cache、LiteSpeed Cache)之后,首字节时间(TTFB)依然在 600ms 以上。原因很简单:插件缓存本质上还是 PHP 程序——请求要先进 WordPress,加载插件,判断缓存文件是否存在,再决定输出。即使命中缓存,也至少跑了一遍 PHP 启动流程。

Nginx FastCGI 缓存不一样:缓存命中时,请求根本不会到达 PHP-FPM,Nginx 直接从本地磁盘读 HTML 吐给浏览器。同一台 2 核 4G 的机器,TTFB 从 600ms 降到 30–50ms 是很常见的。代价是配置要自己写,且缓存失效策略需要手动设计。

原理分析:它缓存的是什么

fastcgi_cache 的工作位置在 Nginx 的 FastCGI 模块里:

  1. 请求进来,Nginx 按 fastcgi_cache_key 算出一个 key(通常包含 scheme + host + uri + 参数);
  2. 拿 key 去 fastcgi_cache_path 指定的目录查文件;
  3. 命中且未过期 → 直接返回,日志里 $upstream_cache_status 是 HIT;
  4. 未命中 → 转发给 PHP-FPM,把响应体存进缓存目录,状态是 MISS;
  5. 被规则排除 → 不查不存,状态是 BYPASS。

关键点:缓存的是 PHP 输出的完整 HTML,所以任何”因人而异”的内容(登录态、购物车、个性化推荐)都必须走 BYPASS,否则会出现”甲看到乙的账号”这种严重事故。

保姆级实操

第一步:在 http 块定义缓存区

编辑 /etc/nginx/nginx.conf,在 http { } 里加:

fastcgi_cache_path /var/cache/nginx/wp levels=1:2 keys_zone=WPCACHE:100m
                   max_size=2g inactive=60m use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_lock on;
fastcgi_cache_lock_timeout 5s;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;

参数说明:

  • levels=1:2:两级目录,避免单目录文件过多;
  • keys_zone=WPCACHE:100m:共享内存 100MB,约能存 80 万个 key,超过后 LRU 淘汰;
  • inactive=60m:60 分钟没被访问的缓存自动删;
  • use_temp_path=off:跳过临时目录中转,减少一次磁盘拷贝,强烈建议开;
  • fastcgi_ignore_headers:忽略 PHP 发来的 Cache-Control,我们自己控制有效期。

第二步:写缓存状态判断映射

在 http { } 里加一段 map,把三种状态区分开:

map $http_cookie $skip_cache {
    default 0;
    # 登录用户、有评论者 cookie、WordPress 测试 cookie 一律不缓存
    "~*wordpress_logged_in"      1;
    "~*wordpress_sec"            1;
    "~*wp-postpass"              1;
    "~*comment_author"           1;
    "~*woocommerce_items_in_cart" 1;
    "~*wp_woocommerce_session"   1;
}

map $request_method $skip_method {
    default 0;
    POST      1;   # 表单提交、REST 写入
    PUT       1;
    DELETE    1;
}

map "$skip_cache$skip_method$query_string" $no_cache {
    default 0;
    "~1"      1;   # 任意一个命中就 BYPASS
}

注意:wordpress_logged_in_ 后面跟的是一串哈希,所以用 ~* 正则模糊匹配,不要写死完整 cookie 名。

第三步:在站点的 PHP location 里启用

server {
    listen 80;
    server_name mimangfei.com www.mimangfei.com;
    root /var/www/mimangfei;
    index index.php index.html;

    # 静态文件直接走 expires,不进 PHP
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|webp|woff2)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

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

        # 缓存核心配置
        fastcgi_cache WPCACHE;
        fastcgi_cache_valid 200 301 302 60m;
        fastcgi_cache_valid 404 1m;
        fastcgi_cache_bypass $no_cache;
        fastcgi_no_cache     $no_cache;
        fastcgi_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
        fastcgi_cache_background_update on;

        add_header X-Cache-Status $upstream_cache_status;
        add_header X-Cache-Date   $upstream_http_date;
    }

    # 后台与敏感路径永不过缓存
    location ~ ^/(wp-admin|wp-login\.php|wp-cron\.php|xmlrpc\.php) {
        include fastcgi_params;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_cache off;
    }
}

第四步:创建缓存目录并重载

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

第五步:验证是否命中

curl -I https://mimangfei.com/ | grep -i x-cache
# 第一次:X-Cache-Status: MISS
# 第二次:X-Cache-Status: HIT

第六步:让发文自动清缓存

Nginx 社区版没有 purge 功能(那是 ngx_cache_purge 模块或 Nginx Plus 才有)。两个实用替代方案:

方案 A,装 nginx-helper 插件(配合 fastcgi_cache_purge 模块):

sudo apt install libnginx-mod-http-cache-purge   # Debian/Ubuntu 自带
location ~ /purge(/.*) {
    fastcgi_cache_purge WPCACHE "$scheme$request_method$host$1";
    allow 127.0.0.1;
    deny all;
}

方案 B(更通用),用 WP-CLI 定时清:

# 每天凌晨清一次全站缓存(适合更新频率低的站)
0 3 * * * rm -rf /var/cache/nginx/wp/* && echo "$(date) cleared" >> /var/log/nginx/purge.log

常见踩坑

  1. 登录用户看到别人的后台:忘记加 wordpress_logged_in 判断,缓存把带登录态的页面存下来了。这是最严重的事故,务必先测:登录后再访问首页,看 X-Cache-Status 必须是 BYPASS。
  2. 缓存永不命中:fastcgi_cache_key 没包含 $request_uri,所有页面共用一个 key;或者 PHP 返回了 Set-Cookie 头但没加 fastcgi_ignore_headers。
  3. 评论后看不到新评论:缓存没清。加 nginx-helper 或在 wp_insert_comment 钩子里触发清除。
  4. 磁盘被撑爆:max_size 没设或设太大,配合 inactive 使用。用 du -sh /var/cache/nginx/wp 定期看。
  5. 案例:某站配好后首页 HIT,但文章页一直 MISS。原因是文章 URL 带了 ?utm_source=xxx 参数,fastcgi_cache_key 用了 $request_uri(含参数),导致每个来源生成一个缓存。改成只用 $uri 后命中率从 12% 涨到 91%——代价是带参数的请求会拿到无参数版本的内容,对内容站没问题,对电商站要谨慎。
  6. fastcgi_cache_background_update 没开:缓存过期瞬间大量并发同时回源,把 PHP-FPM 打死(缓存雪崩)。

总结

Nginx FastCGI 缓存是不花钱、性能上限最高的 WordPress 加速方案,适合内容站、博客、文档站。配置的三条铁律:登录态必须 BYPASS、POST 请求必须 BYPASS、后台路径必须 off。上线前用 curl -I 确认三种状态(MISS / HIT / BYPASS)都符合预期,再配合 nginx-helper 做发文自动清除。如果你用的是宝塔面板,可以在”网站设置 – 配置文件”里手动插入上述片段,但注意宝塔每次保存站点配置会覆盖,建议用”伪静态”区域或用 include 引入外部文件。想进一步压榨性能,可以接着看Nginx FastCGI 缓存与 Redis 对象缓存的配合。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。