本文约 3,844 字,预计阅读 10 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:缓存一开,奇怪的问题全来了
没开缓存时一切正常,装上缓存插件后开始出现各种”玄学”问题:登录用户看到的还是未登录的页面;购物车里的商品串到了别人的会话;改了 CSS 刷新十几遍前台还是旧样式;评论提交后提示”评论失败”,其实是 nonce 过期;WooCommerce 的”我的账户”页面显示了别人的地址。
这些问题的本质都一样:一份本应因人而异的 HTML 被缓存成了公共版本。缓存插件本身没问题,缺的是”哪些页面/哪些用户不缓存”的排除规则,以及”改动后主动清缓存”的机制。
原理分析:缓存命中与失效
页面缓存的工作方式是:第一次访问时生成 HTML 存起来,后续访问直接返回这份静态文件,不执行 PHP。因此任何依赖”当前用户”或”时间”的内容都会被冻住:
- 登录态:登录后的页面(含”我的账户”、管理条)被缓存后,所有人都能看到。
- 购物车与结账:WooCommerce 的
/cart/、/checkout/、/my-account/必须是动态页面。 - nonce:WordPress 的表单 nonce 默认 24 小时有效(由
nonce_life过滤器控制),缓存了一整天的页面里的 nonce 会过期,提交失败。 - 静态资源:CSS/JS 带查询版本号时,改了文件但版本号不变,CDN 与浏览器都还用旧文件。
对应的两类手段:排除(让特定页面/用户不走缓存)与清除(改动后主动失效缓存)。
| 问题 | 手段 | 具体做法 |
|---|---|---|
| 登录用户看到错内容 | 排除 | 有登录 cookie 时跳过缓存 |
| 购物车/结账页串号 | 排除 | URI 排除规则 |
| 表单提交 nonce 过期 | 排除或延长 | 提高 nonce_life 或不缓存含表单页 |
| 改 CSS 前台不变 | 清除 | 版本号用 filemtime,并清 CDN |
| 发文章后列表不更新 | 清除 | 发布/更新时触发清缓存 |
保姆级实操
- 先识别不该缓存的页面:前台有购物车、结账、账户、搜索结果、含表单的页面,以及带查询参数的 URL。
- 设置 URI 排除规则:用缓存插件的排除配置,或用
rocket_cache_reject_uri之类的过滤器写进子主题,配置跟着代码走,换环境不丢。 - 登录用户与有购物车 cookie 的访客跳过缓存:绝大多数缓存插件提供该开关(WP Rocket、LiteSpeed Cache、W3 Total Cache 都有),务必打开。
- 必要时用常量强制跳过:在模板里条件性
define( 'DONOTCACHEPAGE', true ),但要尽早执行。 - 资源版本用文件内容而非核心版本:改了文件版本号必变,避免”改了不生效”。
- 改动后主动清缓存:主题升级、发布文章、更新设置时调用缓存插件的清除接口。
- 分层清理:WordPress 缓存、CDN 缓存、浏览器缓存三层,任何一层没清都会让你误判。
<?php
// 1) 排除不应缓存的 URI(WP Rocket 示例,配置随子主题走)
add_filter( 'rocket_cache_reject_uri', 'mimangfei_reject_uris' );
function mimangfei_reject_uris( $uris ) {
$uris[] = '/cart/';
$uris[] = '/checkout/';
$uris[] = '/my-account/';
$uris[] = '/(?:.*)?preview=true';
return $uris;
}
// 2) 关键页面直接用常量跳过缓存(需尽早定义)
add_action( 'template_redirect', 'mimangfei_nocache_pages', 0 );
function mimangfei_nocache_pages() {
if ( is_cart() || is_checkout() || is_account_page() ) {
if ( ! defined( 'DONOTCACHEPAGE' ) ) {
define( 'DONOTCACHEPAGE', true );
}
}
}
// 3) 表单页适当延长 nonce 生命周期,降低过期概率
add_filter( 'nonce_life', function () {
return 2 * DAY_IN_SECONDS;
} );
<?php
// 4) 主题/文章变动后主动清各级缓存
function mimangfei_purge_all() {
// 对象缓存
if ( function_exists( 'wp_cache_flush' ) ) {
wp_cache_flush();
}
// WP Rocket
if ( function_exists( 'rocket_clean_domain' ) ) {
rocket_clean_domain();
}
// LiteSpeed Cache
do_action( 'litespeed_purge_all' );
// W3 Total Cache / WP Super Cache
if ( function_exists( 'w3tc_flush_all' ) ) {
w3tc_flush_all();
}
if ( function_exists( 'wp_cache_clear_cache' ) ) {
wp_cache_clear_cache();
}
}
// 主题切换或版本升级后清一次
add_action( 'after_switch_theme', 'mimangfei_purge_all' );
// 文章发布/更新后清首页与该文章
add_action( 'save_post', function ( $post_id ) {
wp_cache_delete( $post_id, 'posts' );
mimangfei_purge_all();
} );
# 5) Nginx 层面对动态路径与登录 cookie 跳过缓存(FastCGI Cache 示例)
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($request_uri ~* "/(cart|checkout|my-account|wp-admin|wp-login\.php)") { set $skip_cache 1; }
if ($http_cookie ~* "wordpress_logged_in_|wp-post-pass_|woocommerce_items_in_cart") { set $skip_cache 1; }
location ~ \.php$ {
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
常见踩坑
- 忘了排除
/cart/与/checkout/,出现购物车串号这类严重问题。 - 只清了插件缓存没清 CDN,用户仍然看到旧页面。
- 用核心版本号而不是
filemtime,改了 CSS 但版本号不变,浏览器一直用旧文件。 - 给整站加了长缓存头,HTML 也被缓存,文章内容更新后前台不刷新。
- 案例:某外贸站上线缓存后,客户反馈”加购后购物车是空的”。排查发现整站开启了页面缓存,且排除了
/cart/却没排除带?add-to-cart=参数的请求,加购动作被缓存成静态响应。补全查询参数排除规则并打开”有购物车 cookie 不缓存”后恢复正常。 DONOTCACHEPAGE定义得太晚(在wp_head之后),缓存插件已经决定缓存,常量无效。- 评论表单 nonce 过期导致用户反复提交失败,误以为是插件冲突。
总结
缓存兼容就两件事:排除规则与主动清除。判断标准是”登录用户看到的是登录态、购物车页永远动态、改了 CSS 立刻生效”。行动清单:① 排除购物车/结账/账户/带参 URL;② 登录用户与购物车 cookie 不缓存;③ 资源版本用 filemtime;④ 发布与升级后调用插件清除接口;⑤ 清缓存时三层一起清。延伸阅读 lumen-mimangfei 主题性能优化、WordPress 缓存插件推荐 与 lumen-mimangfei 主题移动端显示问题。
