搜索全站
主题 9 分钟阅读

主题更新后样式没变化:版本号、CDN、浏览器缓存三类排查

主题改完样式前台却没变?本文按版本号、CDN、浏览器三层逐项排查,给出缓存清除写法、CDN 刷新命令与验证方法,终结「改了没用」的反复折腾。

U
UFO_PF 2017-03-14 · 阅读 9 分钟
主题 主题
阅读提示
本文约 3,426 字,预计阅读 9 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:改了样式,同事说没变,客户说变了

这个场景几乎每个站长都遇到过:你在子主题里改了按钮颜色,本地和自己的浏览器上一切正常,同事打开还是旧样式,客户隔天打开又是新的。于是你一遍遍教人「按 Ctrl+F5 强制刷新」,刷新完好了,但下次改又要重来一遍。更糟的是有人坚信是代码没生效,开始加 !important、改选择器,把简单的缓存问题搞成真正的样式事故。

「改了没生效」的成因基本就三类:资源 URL 没变(浏览器/CDN 用旧副本)、中间层缓存没清(页面缓存、对象缓存、CDN 缓存)、改的文件根本不是实际加载的那个。三者表现几乎一样,必须用可验证的方法逐层排除,而不是靠反复刷新碰运气。本文给出一条从服务端到客户端的排查链,每层都配一个可以直接执行的验证动作。

原理分析:缓存发生在哪几层

一次 CSS 请求会经过四道缓存,任意一层返回旧副本都会让改动「看不见」:

层级 位置 由谁控制 清除方式
浏览器缓存 用户本机 HTTP 响应头 Cache-Control / Expires 资源 URL 加版本号,或强制刷新
CDN / 反向代理 边缘节点 CDN 缓存规则、缓存键 CDN 后台刷新 / Purge API
页面缓存插件 服务端 WP Rocket、W3 Total Cache、LiteSpeed Cache 插件的「清除缓存」
对象缓存 服务端 Redis / Memcached wp_cache_flush()

第一道最关键,也最好解决:WordPress 的 wp_enqueue_style() 第四个参数是版本号,它会被拼成 ?ver=1.2.3 追加在 URL 上。URL 一变,浏览器和 CDN 都认为是新资源,立刻回源。所以正确的发布流程不是让用户刷缓存,而是让资源 URL 变。

很多主题把版本号写死成 1.0.0,或者直接用主题版本号——后者的问题是:子主题 CSS 改了但主题版本没升,URL 不变,缓存照旧。更稳妥的做法是用文件的修改时间 filemtime() 作为版本号,文件一存 URL 就变。

保姆级实操

  1. 确认加载的是哪个文件:前台查看源代码,找到你的 CSS 对应的 <link>,复制完整 URL(含 ?ver=)。这一步能立刻排除「改错文件」的可能。
  2. 比对线上文件内容:把 URL 粘到浏览器直接打开,搜索你新加的样式。搜不到说明服务端就是旧文件,问题在发布流程;搜得到说明是客户端/CDN 缓存。
  3. 给版本号换成 filemtime():本地开发时每次保存自动换 URL;生产环境要注意 filemtime() 会触发一次 stat,用对象缓存挡一层即可。
  4. 清服务端缓存:页面缓存插件清一次,对象缓存清一次。WordPress 自带 wp_cache_flush()。
  5. 刷 CDN:到 CDN 后台提交 URL 刷新(或目录刷新),不要整站刷新,代价太大。
  6. 验证:用无痕窗口 + curl -I 看响应头里的 Cache-Control 与返回的版本号,确认边缘节点已更新。
<?php
// 子主题 functions.php:用文件修改时间做版本号,改完即换 URL
add_action( 'wp_enqueue_scripts', function() {
    $css_path = get_stylesheet_directory() . '/assets/css/child.css';
    $ver      = file_exists( $css_path ) ? filemtime( $css_path ) : wp_get_theme()->get( 'Version' );

    wp_enqueue_style(
        'mimangfei-child',
        get_stylesheet_directory_uri() . '/assets/css/child.css',
        array(),
        $ver
    );
}, 20 );
<?php
// 统一给所有样式追加构建号(适合有打包流程的项目)
add_filter( 'style_loader_src', function( $src, $handle ) {
    $build = get_option( 'mimangfei_build_id' );
    if ( ! $build ) {
        $build = gmdate( 'YmdHi' );
        update_option( 'mimangfei_build_id', $build );
    }
    return add_query_arg( 'b', $build, $src );
}, 10, 2 );

// 发布后调用一次即可全站换 URL
function mimangfei_bump_build() {
    update_option( 'mimangfei_build_id', gmdate( 'YmdHi' ) );
    wp_cache_flush();
}
# 静态资源设长缓存:正因为 URL 会变,才敢设 1 年
location ~* \.(css|js)$ {
    expires 1y;
    add_header Cache-Control "public, max-age=31536000, immutable";
    access_log off;
}

# HTML 页面不缓存,保证骨架每次回源
location ~* \.html$ {
    expires -1;
    add_header Cache-Control "no-store, must-revalidate";
}
# 验证线上资源:看版本号与缓存头
curl -I "https://example.com/wp-content/themes/child/assets/css/child.css?ver=1729500000"
# 关注输出里的 HTTP/2 200、cache-control、x-cache(CDN 是否命中)

常见踩坑

  • 只在自己浏览器验证:自己的浏览器有历史缓存,必须换无痕窗口或用 curl。
  • 清了页面缓存但没清 CDN:页面 HTML 更新了,引用的 CSS 版本号也变了,但 CDN 上旧版本号的副本依旧存在,部分用户仍拿到旧文件。
  • filemtime() 在生产环境被禁用:部分主机禁用了相关函数或 stat 缓存未刷新,导致版本号不更新。可用 clearstatcache() 或改用构建号方案。
  • 修改的是父主题文件:父主题升级后改动丢失,且缓存里可能残留旧文件。所有改动必须进子主题,原则见 子主题创建与使用规范。
  • 整站刷新 CDN:一次 purge all 会让全站瞬间回源,流量大时可能把源站打挂。只刷变更的具体 URL。
  • 案例:一个电商站在大促前改了商品卡片样式,发布后一小时仍有大量用户看到旧版。排查发现:版本号用的是主题版本(未变),页面缓存插件缓存了含旧 URL 的 HTML,而 CDN 按 URL 缓存了一年。三者叠加导致「新 CSS 已上传但没人拿到」。最终改为 filemtime() 版本号 + 发布脚本自动清页面缓存 + CDN 按 URL 刷新,问题解决,之后每次改版都走同一条脚本。
  • 忽略移动端运营商缓存:个别移动网络会缓存静态资源更久,只能靠版本号变更规避,无法靠刷新解决。

总结

排查「改了没生效」按固定顺序走:确认加载的 URL → 直接访问该 URL 看内容新旧 → 换版本号 → 清服务端缓存 → 刷 CDN。判断标准很简单:只要资源 URL 变了,浏览器和 CDN 就一定会重新取;不变则无论怎么刷新都可能拿到旧副本。因此版本号策略应当在项目初期就定好,别等出事再补。相关样式冲突问题的排查请配合 主题 CSS 权重排查方法 一起看,避免把缓存问题误判成权重问题。

U
UFO_PF作者

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

留下你的想法

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