Nginx fastcgi_cache全页缓存怎么配?给PHP/WordPress动态站提速实战
本文目录
- fastcgi_cache到底把哪一层缓存下来了?
- 它和Redis对象缓存、Cloudflare是抢饭碗还是各管一段?
- 一套最小可用的fastcgi_cache配置长什么样?
- 哪些请求绝对不能让它缓存?
- 缓存里的内容更新了,怎么让它立刻失效?
- 想扛住突发流量和爬虫,微缓存怎么用?
- 怎么确认缓存到底命中了没有?
- 上了fastcgi_cache还会踩哪些坑?
- 常见问题解答
- fastcgi_cache和WordPress缓存插件(比如WP Super Cache)冲突吗?
- 开了fastcgi_cache,Google抓取看到的会是缓存的旧页面吗?影响SEO吗?
- 我用的是Apache不是Nginx,也能用fastcgi_cache吗?
- 缓存项设多大的TTL比较合适?
- 缓存目录占用磁盘会不会越涨越大把盘撑爆?
- 权威参考资料
站点一上点量TTFB就飙、PHP-FPM进程瞬间被打满,多半是因为每个请求都在让PHP把同一个页面重新生成一遍——纯属白干。fastcgi_cache就是让Nginx把PHP吐出来的整页HTML存在自己这一层,下次命中连PHP都不碰,直接把缓存丢回去。
这篇讲清楚它到底缓存哪一层、和Redis对象缓存与Cloudflare各管哪一段、一套最小可用配置怎么写、哪些请求(登录态、购物车、后台)绝对不能进缓存、内容改了怎么主动清,以及怎么用响应头确认到底命中没命中。看完你能给WordPress或任意PHP动态站上一层真正扛量的全页缓存。
做独立站、做外贸站,十有八九绕不开PHP:WordPress、WooCommerce、Magento、各种自研后台。这类站有个共同的痛点——慢。慢的根子很多时候不在带宽、不在数据库本身,而在于每来一个访客,PHP都要把同一个页面从头跑一遍:连数据库、查文章、套模板、拼HTML,最后才吐给浏览器。一个访客这么干没事,一千个并发同时这么干,PHP-FPM进程就被占满,新请求排队,TTFB直接拉到几秒,严重的直接502。
问题在于,对绝大多数访客来说,他们看到的首页、文章页、分类页其实长得一模一样。同一个页面,凭什么要为每个人重新生成一遍?fastcgi_cache解决的就是这件事:把PHP第一次生成好的整页HTML缓存起来,后面的访客直接拿现成的,PHP一行代码都不用再跑。这篇保哥就把这层缓存从原理到配置、从踩坑到验证,一次讲透。
fastcgi_cache到底把哪一层缓存下来了?
要用对它,先得搞清楚它在整个请求链路里站在哪个位置、缓存的是什么东西。
一个PHP站点的典型链路是这样的:浏览器把请求发给Nginx,Nginx通过FastCGI协议把请求转交给PHP-FPM,PHP-FPM跑PHP代码、查数据库、渲染模板,把生成好的HTML通过FastCGI再交回给Nginx,Nginx最后把这串HTML发给浏览器。fastcgi_cache干的事,就是在Nginx从PHP-FPM拿到这串HTML的瞬间,把它存一份到磁盘(或内存)里,并打上一个键。
下一个请求进来,Nginx先按同样的规则算出键,去缓存里找。找到了且没过期,就直接把存好的HTML吐回去,整个PHP-FPM、数据库、模板渲染全部跳过——这就是命中。没找到或者过期了,才老老实实再走一遍PHP,顺手把新结果再缓存上——这就是未命中。
所以它缓存的是“整页HTML的最终成品”,是页面缓存(page cache)这一层。命中时省下的是PHP解释执行、数据库往返、模板拼装这一整套最贵的活儿,效果立竿见影:TTFB从几百毫秒甚至几秒,直接掉到几毫秒级,因为Nginx读个磁盘文件丢出去太轻松了。这也是为什么页面缓存被称为对动态站性能影响最大的一层。这层缓存怎么影响TTFB、又怎么和其他层联动,保哥在TTFB多层缓存与Core Web Vitals那篇里拆得更细,这里只聚焦fastcgi_cache本身。
它和Redis对象缓存、Cloudflare是抢饭碗还是各管一段?
这是新手最容易绕晕的地方:又是页面缓存、又是对象缓存、又是CDN,到底装哪个、是不是装一个就够了?答案是它们各管一段,配合着用才是完整方案,谁也替代不了谁。
把一个动态站的缓存拆成四层来看就清楚了。第一层是浏览器HTTP缓存,靠Cache-Control、ETag这些响应头让回头客本地直接读,连请求都不发——这块保哥在浏览器HTTP缓存头那篇讲过。第二层是CDN,把内容铺到全球边缘节点,治的是物理距离和回源率,Cloudflare缓存那篇专门讲这一层。
第三层才是fastcgi_cache所在的页面缓存,治的是“同一个动态页面被反复生成”,它跑在你自己的源站Nginx上。第四层是对象缓存,比如用 Redis给WordPress做对象缓存,缓存的是数据库查询结果这种细粒度数据,治的是“没法整页缓存的动态页面里那些重复的数据库查询”。
关键区别在粒度和适用场景。fastcgi_cache缓存的是整页成品,适合内容相对稳定、对所有访客长得一样的页面(首页、文章页、落地页),命中后连PHP都不进,最猛但最粗。Redis对象缓存缓存的是页面内部的数据查询碎片,适合那些必须每次都进PHP、没法整页缓存的动态页面(比如带购物车、带个性化推荐的页面),它没法让你跳过PHP,只是让PHP跑得快点。
所以正确姿势是:能整页缓存的页面交给fastcgi_cache,让它们根本不碰PHP;不能整页缓存的动态页面进PHP后靠Redis对象缓存减少数据库往返;外层再套CDN管分发、套浏览器缓存头管回头客。四层叠起来,各打各的,才是扛量的完整方案。本篇专攻fastcgi_cache这一层。
一套最小可用的fastcgi_cache配置长什么样?
概念讲完,上配置。fastcgi_cache的配置分两部分:在http块里定义一个缓存区,在server/location块里启用它。先看最小骨架。
第一步,在http块里用fastcgi_cache_path划出一块缓存空间:
fastcgi_cache_path /var/cache/nginx/fcgi
levels=1:2
keys_zone=WORDPRESS:100m
inactive=60m
max_size=2g
use_temp_path=off;逐个拆开看。path是缓存文件落盘的目录。levels=1:2是把缓存文件按两级子目录散开,避免一个目录里堆几十万个文件拖慢文件系统。keys_zone=WORDPRESS:100m给这块缓存起个名字叫WORDPRESS,并划100MB共享内存专门存键和元数据(注意这100MB存的是键不是内容本身,100MB大概能放几十万个键,足够大多数站用)。
inactive=60m表示一个缓存项60分钟内没人访问就被清掉(不管它有没有过期,纯粹是冷数据回收)。max_size=2g是缓存内容在磁盘上最多占2GB,超了按LRU淘汰最久没用的。use_temp_path=off让Nginx直接把缓存写到目标目录,省掉先写临时目录再搬一次的开销,官方也推荐这么设。
第二步,定义缓存键,并在处理PHP的location里启用缓存:
fastcgi_cache_key "$scheme$request_method$host$request_uri";
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_index index.php;
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 301 302 30m;
fastcgi_cache_valid 404 1m;
add_header X-Cache-Status $upstream_cache_status;
}fastcgi_cache_key决定“怎么算两个请求是同一个页面”。这里用协议+方法+主机名+完整URI拼成键,意味着HTTP和HTTPS、不同域名会各存一份。这个键怎么设是有讲究的,下文专门会说一个高频坑。
fastcgi_cache WORDPRESS启用刚才定义的缓存区。fastcgi_cache_valid 200 301 302 30m表示200和跳转响应缓存30分钟,404只缓存1分钟(防止某个偶发404被长期缓存住)。最后那行add_header X-Cache-Status极其重要,它把命中状态写进响应头,是后面验证缓存到底有没有生效的眼睛,强烈建议常驻。
还有个进阶但很实用的参数叫fastcgi_cache_min_uses,默认值是1,意思是一个页面被请求1次就缓存。把它调大(比如设成2或3),可以让一个页面被访问到第2、第3次才进缓存。好处是过滤掉那些只被访问一次的长尾冷门页面——这些页面缓存了也很少再命中,白占缓存空间和磁盘。对页面数量巨大、但流量集中在少数热门页的站,调高这个值能让缓存空间集中给真正高频的页面,命中率反而更健康。小站默认1就行,不用动。
这套配下去重载Nginx,第一次访问页面是MISS(走了PHP),第二次起就该是HIT(没碰PHP)了。但先别急着上生产——上面这套裸配置有个致命问题:它会把登录后台、购物车这些也一股脑缓存了,下面就讲怎么排除。
哪些请求绝对不能让它缓存?
这是fastcgi_cache的头号大坑,也是新手翻车最惨的地方。如果不做排除,会出现什么后果?管理员登录后台,Nginx把带着登录态的后台页面缓存了,结果下一个匿名访客一访问,直接看到管理员的后台——这叫缓存串用户,是真实发生过的安全事故。
核心原则:凡是“因人而异”的页面、凡是会改数据的请求,统统不能进缓存。具体至少要排除这几类:登录用户的页面(带登录Cookie的)、所有POST请求、后台地址(wp-admin、wp-login)、购物车和结账页、站内搜索结果(?s= 这种)、带评论Cookie的页面。
做法是先用一个变量判断要不要跳过,再把这个变量喂给两个指令。在server块里写判断:
set $skip_cache 0;
# POST 请求不缓存
if ($request_method = POST) { set $skip_cache 1; }
# 带查询串的不缓存(搜索/分页等按需放宽)
if ($query_string != "") { set $skip_cache 1; }
# 后台/登录/预览/Feed 等地址不缓存
if ($request_uri ~* "/wp-admin/|/wp-login.php|/cart|/checkout|/my-account|sitemap.*\.xml") {
set $skip_cache 1;
}
# 已登录/刚评论/购物车 Cookie 不缓存
if ($http_cookie ~* "wordpress_logged_in|woocommerce_items_in_cart|comment_author") {
set $skip_cache 1;
}然后在PHP的location里把这个变量接到两个关键指令上:
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;这两个指令必须成对出现,含义不同别搞混。fastcgi_cache_bypass控制“这次请求要不要读缓存”——值非空非零时,绕过缓存直接去问PHP(拿最新的)。fastcgi_no_cache控制“这次响应要不要写进缓存”——值非空非零时,PHP返回的结果不存。两个都设,登录态请求才能既不读旧缓存、又不把私有内容写进公共缓存。只设一个,照样出事。
这里多说一句那个query_string判断。把所有带查询串的请求都跳过缓存是最稳的保守做法,但它会让带UTM参数、带分页参数的页面全部不缓存,命中率受影响。更精细的做法是只跳过真正动态的查询串(比如 ?s= 搜索、?add-to-cart=),对UTM这类营销参数则在fastcgi_cache_key里把它们忽略掉。怎么取舍看站点流量结构,先求稳、再优化命中率,是个合理顺序。
缓存里的内容更新了,怎么让它立刻失效?
页面缓存最反直觉的地方在这儿:你后台改了一篇文章、改了价格,可前台访客看到的还是缓存里的旧版本,因为缓存还没到期。这就是页面缓存这一层最经典的矛盾——既要长缓存扛量,又要内容改了立刻生效。有两条路。
第一条是被动失效:靠fastcgi_cache_valid设的TTL自然过期。比如设30分钟,那最长30分钟后访客就能看到新内容。这条路零依赖、最简单,适合内容更新不那么实时的站(博客、企业站)。缺点是有延迟,且改一个小东西也得等全站缓存慢慢过期。
第二条是主动清除:内容一更新,立刻把对应页面的缓存删掉,下次访问重新生成。这需要ngx_cache_purge这个第三方模块(Nginx默认不带,需要编译进去,或用自带该模块的发行版/容器镜像)。装好后配一个purge的location:
location ~ /purge(/.*) {
fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
}之后访问 /purge/文章地址 就能精确删掉那一页的缓存。配合WordPress端的缓存插件(很多支持Nginx Helper、或自己写钩子),就能做到“发布/更新文章时自动ping这个purge地址”,实现内容一改、对应页面缓存立刻失效。
没有这个模块也有土办法:直接rm掉缓存目录里的文件、或者整目录清空。但整目录清空是把全站缓存一锅端,清完瞬间所有请求都打到PHP上,流量大的站可能直接被自己清缓存的动作压垮,这个连锁反应下文会专门提。所以主动清除优先做“精确清单页”,别动不动全清。
实战里通常是两条路一起用:给大多数页面设一个不太短的TTL兜底(防止主动清漏了),再对“发布、更新、评论”这类明确事件做精确主动清。被动管全局保险,主动管即时性,配合起来既扛量又不耽误内容更新。
想扛住突发流量和爬虫,微缓存怎么用?
有一类站,页面更新很频繁(比如带实时数据、带个性化区块),长TTL缓存会让内容太旧,可又确实扛不住突发流量和爬虫的反复抓取。这时候有个很好用的招:微缓存(microcache)。
微缓存的思路是把TTL设得极短——1到10秒。听起来缓存1秒有什么用?用处大了。设想一个页面每秒被请求100次,没缓存时这100次全打到PHP;开了1秒微缓存,这1秒内只有第一个请求真正走PHP,剩下99个全吃缓存。也就是说PHP的压力从每秒100次降到每秒1次,降了两个数量级,而访客最多看到1秒前的内容——对绝大多数页面完全可以接受。
微缓存的配置就是把valid时间改短:
fastcgi_cache_valid 200 1s;
fastcgi_cache_use_stale updating error timeout;
fastcgi_cache_lock on;后两行是微缓存的灵魂。fastcgi_cache_use_stale updating的意思是:当某个缓存项正在被一个请求重新生成时,其他同时进来的请求先吃旧的(stale)缓存,而不是全都排队等PHP或全都涌向PHP。再加上error timeout,万一PHP后端挂了或超时,Nginx还能把过期缓存先顶上去救急,避免后端一抖动就全站502。
fastcgi_cache_lock on解决的是缓存击穿(也叫惊群):当一个热门页面的缓存刚好过期,同一瞬间几百个请求同时发现没缓存、同时涌向PHP去生成,把后端瞬间打爆。开了lock,同一个键同一时刻只放一个请求去生成,其余的等它生成完直接吃新缓存。突发流量、秒杀、被爬虫密集抓取的场景,这三行能救命。
怎么确认缓存到底命中了没有?
配完别凭感觉说“应该生效了”,一定要验证。最直接的就是前面那行add_header X-Cache-Status写进去的响应头。用curl看头就行:
curl -I https://你的域名/某篇文章/
# 看返回头里的:
# X-Cache-Status: MISS 第一次,走了PHP
# X-Cache-Status: HIT 命中,没碰PHP
# X-Cache-Status: BYPASS 被bypass规则跳过了(比如带Cookie)
# X-Cache-Status: EXPIRED 过期了,重新生成
# X-Cache-Status: STALE 吃的是过期缓存(use_stale救急)验证套路是:第一次访问应该是MISS,紧接着再访问同一个地址应该变成HIT。如果第二次还是MISS,说明缓存根本没写进去——多半是命中了某条no_cache规则(比如你带着登录Cookie在测、或者那个query_string判断把它跳过了)。如果该缓存的页面一直显示BYPASS,回头查你的set $skip_cache判断是不是误伤了。
另一个验证手段是看日志:在log_format里加上 $upstream_cache_status,就能在访问日志里统计全站的命中率,看哪些URL老是MISS。再配合压测工具(ab、wrk)对同一个页面打一轮,对比开缓存前后的TTFB和PHP-FPM进程占用,差距会非常直观——命中后TTFB通常是个位数毫秒,PHP-FPM进程几乎不动。
提醒一点:浏览器自己也有缓存,用浏览器测容易被本地缓存骗。验证fastcgi_cache这一层,优先用curl -I看服务器返回的头,或者无痕窗口、命令行工具,别被浏览器的强缓存(这是另一层的事)混淆了视线。
还有个容易被忽略的验证维度是状态码本身。命中缓存返回的应该是当初被缓存时的状态码——如果你发现某个本该200的页面缓存后老返回别的码,或者一个错误页被当成正常页长期缓存住了,那要回头查fastcgi_cache_valid的配置,看是不是把不该缓存的状态码也缓存了、或者缓存时机不对。把命中状态、状态码、TTFB三个指标一起看,缓存到底健不健康一目了然,比凭感觉强太多。
上了fastcgi_cache还会踩哪些坑?
把保哥和同行们踩过的坑集中列一下,照着躲能少走很多弯路。
缓存串用户(最严重):没排除登录态就缓存后台页面,匿名访客看到别人的私密内容甚至后台。前面bypass/no_cache那套必须配齐,上线前务必带着登录Cookie和不带Cookie各测一遍。
移动端和PC端共用一个键:如果你的主题是同一套HTML靠CSS自适应,没问题;但如果是服务端按User-Agent分别输出移动版和PC版HTML,那fastcgi_cache_key里必须把设备类型也算进去,否则手机用户可能吃到PC版缓存,反之亦然。
分页、评论、点赞不更新:访客发了评论却看不到自己的评论,因为页面被缓存了。要么把带评论Cookie的请求bypass掉,要么在评论提交后主动purge那一页。这类“刚操作完看不到结果”的投诉,九成是缓存没及时清。
清缓存把自己压垮:流量大的站千万别动不动flush全站缓存。全清的瞬间所有页面同时变MISS,请求洪峰全砸到PHP上,可能直接502。要清就精确清单页,全清放在低谷期、并确保cache_lock开着兜底。
缓存放磁盘还是内存:fastcgi_cache_path默认落普通磁盘。如果磁盘是机械盘、或缓存命中率极高追求极致延迟,可以把缓存目录挂在tmpfs(内存盘)上,读取更快;但内存盘重启即空、且占内存,得权衡。多数SSD站点用默认磁盘就够。
HTTPS与HTTP各存一份是预期行为:因为键里带了 $scheme。如果你已经全站强制HTTPS,HTTP端只是301跳转,这没问题;但别被“怎么存了两份”吓到,那是键设计决定的,正常。
装了缓存就不管命中率了:上线后要持续看 $upstream_cache_status的命中率。命中率长期偏低,多半是bypass规则太激进(把不该跳的也跳了)、或者键设计太细(带了不该带的参数导致每个URL都算不同页)。缓存不是配完就完事,是要养的。
常见问题解答
fastcgi_cache和WordPress缓存插件(比如WP Super Cache)冲突吗?
不冲突,但要想清楚分工,别重复劳动。WordPress那些纯PHP的页面缓存插件,本质是在PHP层把生成好的HTML存成静态文件,下次请求还是得先进PHP才能判断要不要返回缓存。fastcgi_cache跑在Nginx这一层,命中时连PHP都不进,比PHP层缓存更靠前、更省资源。两者只用一个就行,优先用fastcgi_cache。如果你已经在用Nginx,那WordPress端装的缓存插件最好只保留它的purge联动能力(比如Nginx Helper,负责文章更新时帮你清fastcgi_cache),把页面缓存这件事交给Nginx,别让PHP层再缓存一遍。两层都做页面缓存不仅多余,还会让缓存失效逻辑互相打架,调试时让人抓狂。
开了fastcgi_cache,Google抓取看到的会是缓存的旧页面吗?影响SEO吗?
正常配置下不影响,反而有利。搜索引擎爬虫和普通访客一样,命中缓存就拿缓存、过期就拿新生成的,看到的内容和真实页面一致,不存在“给爬虫看旧版”的问题。而且页面缓存让TTFB大幅下降、服务器扛得住爬虫的密集抓取,对抓取预算和速度类排名信号都是正向的。要注意的是别把带个性化、带时效性的页面用过长TTL缓存住,导致内容明显滞后;也别误把sitemap.xml这类需要实时的文件缓存太久。把内容页设合理TTL、动态文件排除掉,SEO上只有好处没有坏处。真正会出问题的是缓存配错导致串内容或返回错误状态码,那属于配置bug,不是缓存本身的锅。
我用的是Apache不是Nginx,也能用fastcgi_cache吗?
fastcgi_cache是Nginx特有的指令,Apache没有同名功能,但思路可以平移。Apache想做类似的源站全页缓存,常见路子是mod_cache配合mod_cache_disk,或者在Apache前面架一台Nginx/Varnish专门做缓存层。很多生产环境就是Nginx在前做缓存和静态分发、Apache在后跑PHP这种组合。如果你是纯Apache环境又想要强力的全页缓存,最干净的方案往往是前置一层缓存代理,而不是硬在Apache里凑。当然,如果你的站点本来就在Nginx + PHP-FPM上,那直接用fastcgi_cache是最顺的,不用为了缓存去换架构。先看清自己的栈,再选对应方案。
缓存项设多大的TTL比较合适?
没有标准答案,取决于内容更新频率和你有没有主动清除机制。如果你配了ngx_cache_purge、能在内容更新时精确清掉对应页面,那TTL可以设得长一些(比如几小时甚至一天),靠主动清保证即时性,靠长TTL把命中率拉满。如果你没有主动清除、纯靠被动过期,那TTL就得短一些(比如10到30分钟),在“扛量”和“内容别太旧”之间折中。对更新极频繁又扛不住量的页面,可以走前面说的微缓存,1到10秒。一个实用做法是分类型设TTL:稳定的文章页长一点,动态性强的列表页短一点,首页居中。别全站一个数值拍脑袋,按页面性质分层设,命中率和新鲜度才能兼顾。
缓存目录占用磁盘会不会越涨越大把盘撑爆?
不会,前提是你在fastcgi_cache_path里设了max_size。这个参数就是给缓存内容设的磁盘上限,达到上限后Nginx会按最近最少使用(LRU)的策略自动淘汰最久没被访问的缓存项,腾出空间。另外inactive参数会把一段时间内没人访问的冷缓存清掉。所以只要这两个参数设了合理值(比如max_size设成你能接受的磁盘占用、inactive设个把小时),缓存目录就会稳定在一个区间,不会无限膨胀。真正会出问题的是忘了设max_size——那它确实会一直涨。还有个隐蔽情况是缓存目录所在分区本身就小、又和系统盘共用,建议给缓存单独规划好空间,并监控磁盘使用率,别等撑爆了才发现。
权威参考资料
FAQPage + Article AI 引用友好版
动态站一上量PHP-FPM就被打满?fastcgi_cache让Nginx把整页HTML缓存在自己这层,命中就不碰PHP。保哥讲清它缓存哪一层、和Redis对象缓存怎么分工、最小配置怎么写、哪些请求不能缓存、缓存怎么主动清、命中怎么验证。
- Nginx
- WordPress
- 性能优化
- 缓存与CDN
title: Nginx fastcgi_cache全页缓存怎么配?给PHP/WordPress动态站提速实战 author: 张文保 (Paul Zhang) — PatPat SEO 经理 url: https://zhangwenbao.com/nginx-fastcgi-cache-fullpage-php-wordpress-purge-microcache.html published: 2026-03-02 modified: 2026-03-02 source-type: First-hand expert commentary language: zh-CN license: CC BY-NC-SA 4.0 (要求保留原文链接与作者归属)
本文标题:《Nginx fastcgi_cache全页缓存怎么配?给PHP/WordPress动态站提速实战》
本文链接:https://zhangwenbao.com/nginx-fastcgi-cache-fullpage-php-wordpress-purge-microcache.html
版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0