1692 字
8 分钟
WordPress 缓存破坏:主题更新后样式不生效

问题#

主题(或单独的 CSS/JS 文件)更新后,用户刷新页面看到的还是旧样式。原因是浏览器缓存了旧的 CSS/JS。我们不可能要求每个用户手动清缓存,需要一套能让旧样式自动失效的机制。

核心原理#

浏览器缓存是一张表:key 是完整的资源 URL(含 query string),value 是文件内容 + 过期时间。

页面引用 style.css 时,浏览器先查这张表:

  • 表里有这个 key 且未过期 → 直接用本地旧文件,可能连请求都不发。
  • 表里没有这个 key → 去服务器重新拉取。

关键点:style.cssstyle.css?ver=123 在浏览器眼里是两个完全不同的 key。query string 参与 key 的比对。

所以缓存破坏的本质不是去”清”缓存,而是换一个新 URL,让浏览器查表时”没见过”,从而主动重新请求。旧缓存查不到、用不上,自然被晾死。

解决方案:用 filemtime 自动生成版本号#

思路是给资源 URL 拼上一个随文件修改时间变化的版本号。文件一改,修改时间变,版本号变,URL 变,浏览器重新拉取——全自动,不用手动维护版本号。

WordPress 的 wp_enqueue_style / wp_enqueue_script 第四个参数 $ver 会自动拼成 style.css?ver=xxx。但很多主题写死版本号,只改 CSS 不改版本号就失效。最稳的做法是用过滤器统一接管所有本站资源的版本号。

<?php
/**
* Cache busting: 用 filemtime 给本站 CSS/JS 自动加版本号,
* 文件更新后用户自动拉取新版本。删除本文件即可完全回滚。
* 注意:依赖 HTML 本身不被页面缓存才能把新 URL 送到用户手里。
*/
add_filter('style_loader_src', 'dten_filemtime_ver', 9999);
add_filter('script_loader_src', 'dten_filemtime_ver', 9999);
function dten_filemtime_ver($src) {
if (strpos($src, content_url()) === false) return $src;
$path = str_replace(content_url(), WP_CONTENT_DIR, strtok($src, '?'));
if (is_file($path)) {
$src = add_query_arg('ver', filemtime($path), strtok($src, '?'));
}
return $src;
}

代码逐行说明#

  • add_filter('style_loader_src', ...) / add_filter('script_loader_src', ...):WordPress 生成 HTML 时,每条 <link>(CSS)和 <script>(JS)的 src 都会经过这两个过滤器。优先级设为 9999,是为了最后一个动手,盖过主题或其他插件加的版本号。
  • if (strpos($src, content_url()) === false) return $src;:只处理本站 wp-content 下的资源。第三方外部资源(如外链字体库)原样放过,因为 filemtime 只能读本地文件。
  • $path = str_replace(content_url(), WP_CONTENT_DIR, strtok($src, '?'));:从 URL 反推服务器上的物理文件路径。strtok($src, '?') 砍掉可能已有的旧 ?ver=str_replace 把 URL 前缀换成磁盘路径前缀。
  • if (is_file($path)):确认文件真实存在,防止路径推算失败时出错。
  • filemtime($path)整个机制的心脏。 读取文件”最后修改时间”,返回一个 Unix 时间戳(如 1718294400)。文件一被保存/覆盖,操作系统就更新它的修改时间,时间戳随之改变。
  • add_query_arg('ver', ...):把时间戳拼成 style.css?ver=1718294400

完整因果链#

  1. 覆盖/保存了 style.css → 操作系统更新该文件的修改时间。
  2. 下一个用户访问,WordPress 生成 HTML,filemtime 读到新的修改时间 → HTML 里 CSS 的 URL 变成 style.css?ver=新时间戳
  3. 用户浏览器拿到新 HTML,看到没见过的 URL → 缓存未命中 → 拉取最新 CSS。
  4. 旧的 ?ver=旧时间戳 缓存还在,但再也不被任何页面引用,自然失效。

用户全程零操作,下次访问自动看到新样式。

代码该放哪里(重要)#

这段代码的放置位置直接决定它会不会被覆盖、是否需要回滚:

放置位置是否推荐说明
第三方主题的 functions.php❌ 不推荐主题一更新就被覆盖,改动全没。而这次的问题恰恰由主题更新引发,会反复踩坑。
子主题的 functions.php✅ 可以不被主题更新覆盖,但前提是已经有子主题。
wp-content/mu-plugins/(must-use 插件)✅ 最推荐一个 .php 文件丢进去即自动加载,无需启用、不受主题更新影响、不依赖子主题、出问题删文件就回滚。

只有在「这是自研主题」或「主题以后不再更新」时,直接改主题 functions.php 才算合理。

SSH 一条命令部署到 mu-plugins(目录不存在先 mkdir wp-content/mu-plugins):

Terminal window
cat > wp-content/mu-plugins/cache-bust.php << 'EOF'
<?php
add_filter('style_loader_src', 'dten_filemtime_ver', 9999);
add_filter('script_loader_src', 'dten_filemtime_ver', 9999);
function dten_filemtime_ver($src) {
if (strpos($src, content_url()) === false) return $src;
$path = str_replace(content_url(), WP_CONTENT_DIR, strtok($src, '?'));
if (is_file($path)) {
$src = add_query_arg('ver', filemtime($path), strtok($src, '?'));
}
return $src;
}
EOF

现成插件(可选)#

如果想要后台开关、或交接给非技术同事维护,可用 Autover 插件:装上、启用即可,效果与上面这段代码几乎一致(同样基于 filemtime 给入队资源加版本号)。

对自己掌控服务器、看得懂 PHP 的情况,mu-plugins 那几行其实比装插件更轻、更可控——插件会多一层心智负担和兼容性维护。插件的价值主要是给”只会点后台”的人用的。

一个必须知道的前提#

新的资源 URL 是写在 HTML 里的。所以整个机制的命门是:用户每次拿到的 HTML 必须是新生成的。

如果 HTML 本身被页面缓存了(例如 WP Rocket / W3 Total Cache / WP Super Cache 的 page cache),用户拿到的还是旧 HTML,里面写的还是旧 URL,新版本号传不过去——机制整个失效。

部署后务必验证:用隐身窗口打开页面,看源码里 CSS 的 ?ver= 是否已变成时间戳数字(如 ?ver=1718294400)。

  • 变了 → 成功,用户陆续访问会自愈。
  • 没变 → HTML 这一层被缓存了,需先 purge 页面缓存插件,再复查。

能力边界(不是 bug,是限制)#

这套机制只覆盖用 wp_enqueue_style / wp_enqueue_script 入队的资源。以下情况它管不到,需要单独处理:

  • 直接硬编码在 header.php 等模板里的 <link> / <script>
  • CSS 里用 @import 引入的子文件;
  • CSS 里 background-image 等引用的图片。

长期影响#

可以长期放在服务器上,它本就是为生产环境设计的方案:

  • 性能开销可忽略:每个本地资源多一次 filemtime()(读元数据的 syscall,微秒级,且 inode 信息通常已在系统缓存里)。
  • 纯函数、无副作用:只改写 HTML 里的 URL 字符串,不写数据库、不写文件、不留状态。删掉立即恢复原样。
  • 唯一要留神:函数名 dten_filemtime_ver 全站只能定义一处,别在别处又复制一份同名函数,否则 PHP 会因函数重定义直接 fatal error。

一句话总结#

浏览器按 URL 缓存资源。用 filemtime 让文件内容一变、URL 就变,浏览器便自动拉取新文件——把代码放进 mu-plugins 以免被主题更新覆盖,部署后用隐身窗口确认 ?ver= 已更新即可。

WordPress 缓存破坏:主题更新后样式不生效
https://blog.cuixu.cn/posts/wordpress-cache-busting/
作者
崔旭
发布于
2026-06-13
许可协议
CC BY-NC-SA 4.0