本文约 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 模块里:
- 请求进来,Nginx 按
fastcgi_cache_key算出一个 key(通常包含 scheme + host + uri + 参数); - 拿 key 去
fastcgi_cache_path指定的目录查文件; - 命中且未过期 → 直接返回,日志里
$upstream_cache_status是HIT; - 未命中 → 转发给 PHP-FPM,把响应体存进缓存目录,状态是
MISS; - 被规则排除 → 不查不存,状态是
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
常见踩坑
- 登录用户看到别人的后台:忘记加
wordpress_logged_in判断,缓存把带登录态的页面存下来了。这是最严重的事故,务必先测:登录后再访问首页,看X-Cache-Status必须是BYPASS。 - 缓存永不命中:
fastcgi_cache_key没包含$request_uri,所有页面共用一个 key;或者 PHP 返回了Set-Cookie头但没加fastcgi_ignore_headers。 - 评论后看不到新评论:缓存没清。加
nginx-helper或在wp_insert_comment钩子里触发清除。 - 磁盘被撑爆:
max_size没设或设太大,配合inactive使用。用du -sh /var/cache/nginx/wp定期看。 - 案例:某站配好后首页 HIT,但文章页一直 MISS。原因是文章 URL 带了
?utm_source=xxx参数,fastcgi_cache_key用了$request_uri(含参数),导致每个来源生成一个缓存。改成只用$uri后命中率从 12% 涨到 91%——代价是带参数的请求会拿到无参数版本的内容,对内容站没问题,对电商站要谨慎。 fastcgi_cache_background_update没开:缓存过期瞬间大量并发同时回源,把 PHP-FPM 打死(缓存雪崩)。
总结
Nginx FastCGI 缓存是不花钱、性能上限最高的 WordPress 加速方案,适合内容站、博客、文档站。配置的三条铁律:登录态必须 BYPASS、POST 请求必须 BYPASS、后台路径必须 off。上线前用 curl -I 确认三种状态(MISS / HIT / BYPASS)都符合预期,再配合 nginx-helper 做发文自动清除。如果你用的是宝塔面板,可以在”网站设置 – 配置文件”里手动插入上述片段,但注意宝塔每次保存站点配置会覆盖,建议用”伪静态”区域或用 include 引入外部文件。想进一步压榨性能,可以接着看Nginx FastCGI 缓存与 Redis 对象缓存的配合。
