没有不值得去解决的问题,也没有不值得去学习的技术!

WordPress 英文站出现横向滚动条:排查 Polylang 多域名下 W3TC Object Cache 缓存旧 CSS 的问题

【图 7,数据库原始内容与 get_post() 返回内容不一致】

WP 博客多语言化实操

查看按语言划分的用户统计,排名前 4 的语言(英语、中文、印尼语、越南语)(如图3)

(1) 博客多语言化决策|为什么我决定花时间做多语言?(附真实发现)

博客多语言插件选型|免费vs付费、服务端vs浏览器端,实测对比+配置教程

(2) 博客多语言插件选型|免费vs付费、服务端vs浏览器端,实测对比+配置教程

附上我翻译完成后的分类效果截图(如图11),中文与英文的分类总数相等

(3) 博客分类翻译实操|695个分类,4小时手动翻译全流程(避坑指南)

最后分别查看中文与英文后台下的标签统计,符合预期。如图17

(4) 博客标签翻译实操|8060个标签,基于 PHP 脚本实现全流程

如图11:最终效果,在前台,语言切换器的显示

(5) 博客菜单翻译实操

如图20对应场景:网站前台效果,顶部语言切换器(中文/英文),点击英文后,网站整体切换为英文版本,文章列表按发布时间倒序排列(与中文文章排序一致),点击任意英文文章,可正常查看,语言切换流畅;同时,英文文章的发布时间、分类、标签与中文原文完全对应,URL路径规范,SEO友好。

(6) WordPress 多语言博客文章翻译实操全记录(Polylang 插件,附避坑指南)

配图说明(如图10):中文、英文分类管理页面截图(分屏对比),标注两个页面的分类总数,演示数量一致/不一致的场景。

(7) WordPress 新增文章分类标签多语言前置翻译流程(Polylang 总数校验+标签脚本复制避坑)

完成后,访问中文系列页 https://你的域名/series/self-hosted-vpn-series/,点击顶部的 English 切换按钮,就能正常跳转到 https://你的域名/en/series/self-hosted-vpn-series-en/ 了。如图13

(8) 告别手动编号:用 PublishPress Series 优雅管理 WordPress 系列文章

中文标签出现冗余新增(如图2),数据彻底错乱。

(9) WP 6.9 标签同步脚本在 WP 7.0 失效完整排查与解决实录

重点避坑:仅勾选数据库无法完成数据还原,选中库之后,右侧数据表列表点击全选,囊括库内所有数据表(如图3)

(10) 阿里云RDS数据库误操作损毁,完整备份恢复实操避坑指南

为了看起来更完善,AI会主动新增非必要的清理、校验、适配逻辑,简单需求复杂化,大幅增加报错和风险概率(如图7)

(11) AI生成代码深度避坑:数据库操作代码绝不可以直接上线执行

WordPress 标签清理实践(一):大语言模型匹配的失败尝试

(12) WordPress 标签清理实践(一):大语言模型匹配的失败尝试

在宿主机的 output 目录下可以找到生成的 tag_mapping_result.csv。打开文件查看,结果格式规整,符合预期。

(13) WordPress 标签清理实践(二):Go 脚本工程化落地

脚本执行后(如 图 6 终端日志所示),系统开始成对合并中英文标签。

(14) WordPress 标签清理实践(三):完美解决Polylang中英文同义标签合并难题

图7:展示了浏览器开发者工具Network面板中,旧URL 301跳转至新URL的成功记录

(15) WordPress 标签清理实践(四):基于Go脚本实现WordPress中英文标签合并与URL自动跳转

截图 5:验证 301 跳转

(16) WordPress 标签清理实践(五):基于 PHP/Go 脚本解决 English 语言下残留的中文标签问题 ,并实现自动化的标签合并与 URL 跳转

脚本显示处理了 8324 个标签

(17) WP 标签批量翻译脚本准确性问题排查与修复

【图 1,英文首页日历日期链接被拼接成双重 URL】

(18) WordPress 英文站日历链接变成双重 URL:Polylang 子域名迁移后的 WPCode 与 W3TC Object Cache 排查

【图 7,数据库原始内容与 get_post() 返回内容不一致】

(19) WordPress 英文站出现横向滚动条:排查 Polylang 多域名下 W3TC Object Cache 缓存旧 CSS 的问题

【图 1,主题编辑器中的分类列表区块及“以下拉菜单显示”设置】

(20) WordPress Polylang 英文子域名迁移后链接仍指向中文站:分类下拉、面包屑与区块模板的完整修复

【图 1,终端检查 www 与 en 域名统计代码的结果】

(21) WordPress 英文站从 /en/ 迁移到子域名后,如何调整 GA4 与百度统计

WordPress 多域名架构下 W3 Total Cache 缓存失效:Polylang、Redis 与 admin 子域名的完整排查和修复

(22) WordPress 多域名架构下 W3 Total Cache 缓存失效:Polylang、Redis 与 admin 子域名的完整排查和修复

图1:直接 PHP 环境下默认查询返回 8915,禁用缓存后返回 8928

(23) 第三次排查才找到真因:Polylang 标签同步脚本总数长期不一致,原来是持久化 Term Query 缓存

图4:第一批 20 篇全部显示为 ready 的只读验收结果。

(24) 从 SyntaxHighlighter 到 Code Block Pro:把 WordPress 历史文章摘要与英文覆盖翻译流程标准化

这篇文章是此前 Polylang 多域名迁移排查的后续记录。

上一篇文章:

Plaintext
https://www.shuijingwanwq.com/2026/07/10/19280/

在上一篇文章中,我将英文站从原来的 /en/ 路径迁移到了独立子域名:

Plaintext
中文站:https://www.shuijingwanwq.com/
英文站:https://en.shuijingwanwq.com/

迁移过程中,我已经遇到过两类与缓存有关的问题:

  1. W3 Total Cache Page Cache 将 Polylang 的英文语言域名识别成 foreign domain,导致 en 被 301 跳回 www
  2. 301 问题解决后,W3 Total Cache Object Cache 中仍然保留旧的 Polylang 配置,导致英文子域名运行时继续被识别成中文站。

这一次,最初遇到的只是一个看起来很普通的前端问题:英文文章页面底部出现了横向滚动条。

但排查到最后发现,除了真实存在的 CSS 布局问题之外,W3 Total Cache Object Cache 使用 Redis 保存的旧对象,也导致新 CSS 长时间无法在英文站生效。


一、当前网站结构

当前 WordPress 使用 Polylang 的多域名模式:

Plaintext
中文站:www.shuijingwanwq.com
英文站:en.shuijingwanwq.com
WordPress 后台:www.shuijingwanwq.com/wp-admin/

两个域名指向同一个 WordPress 根目录,并共享:

Plaintext
同一套 WordPress 程序
同一个数据库
同一套 Twenty Twenty-Five 主题
同一套插件
同一个 Redis 实例

当前启用的缓存组合包括:

Plaintext
W3 Total Cache Page Cache
W3 Total Cache Object Cache
Redis
Cloudflare

这里需要明确一下:

本站使用的是 W3 Total Cache 提供的 Object Cache 功能,Redis 是对象缓存的存储后端。

因此,本文后续统一使用:

Plaintext
W3TC Object Cache(Redis 后端)

而不是把它描述成一个独立的 Redis Object Cache 插件。


二、问题现象:只有英文文章出现横向滚动条

出现问题的英文文章是:

Plaintext
https://en.shuijingwanwq.com/2026/07/11/19327/

其对应的中文文章是:

Plaintext
https://www.shuijingwanwq.com/2026/07/11/19309/

中文文章显示正常,但英文页面底部出现了一条横向滚动条。

【图 1,英文文章页面底部出现横向滚动条】
【图 1,英文文章页面底部出现横向滚动条】

由于两个页面使用的是同一个主题和同一套模板,最开始怀疑的对象包括:

Plaintext
英文长标题
超长 URL
代码块
表格
AdSense iframe
搜索框
语言切换器
额外 CSS

单纯通过肉眼观察,很难确定究竟是哪一个元素把页面撑宽了。


三、通过 JavaScript 找出超出视口的元素

我先打开英文文章页面,按 F12 进入浏览器开发者工具,然后在“控制台”中执行下面的代码:

JavaScript
(() => {
    const viewportWidth = document.documentElement.clientWidth;

    const elements = [...document.querySelectorAll('body *')]
        .map(el => {
            const rect = el.getBoundingClientRect();

            return {
                element: el,
                tag: el.tagName.toLowerCase(),
                class: typeof el.className === 'string' ? el.className : '',
                left: Math.round(rect.left),
                right: Math.round(rect.right),
                width: Math.round(rect.width),
                scrollWidth: el.scrollWidth,
                clientWidth: el.clientWidth,
                text: (el.innerText || el.textContent || '')
                    .trim()
                    .replace(/\s+/g, ' ')
                    .slice(0, 100)
            };
        })
        .filter(item =>
            item.right > viewportWidth + 1 ||
            item.left < -1
        );

    elements.forEach(item => {
        item.element.style.outline = '3px solid red';
    });

    console.table(elements.map(({ element, ...item }) => item));

    console.log({
        viewportWidth,
        pageScrollWidth: document.documentElement.scrollWidth,
        overflowWidth:
            document.documentElement.scrollWidth - viewportWidth,
        overflowElements: elements.length
    });

    return elements;
})();

检测结果显示:

Plaintext
viewportWidth: 1405
pageScrollWidth: 1422
overflowWidth: 17

也就是说,页面总宽度比浏览器可视区域多出了 17px。

在检测结果中,最右侧的元素是:

Plaintext
nav.tepll-pll-language-switcher
【图 2,控制台检测到语言切换器超出视口 17px】
【图 2,控制台检测到语言切换器超出视口 17px】

四、语言切换器只是最右侧元素,不一定是根因

为了验证语言切换器是否就是问题来源,我临时在控制台中把它向左移动了 17px:

JavaScript
const el = document.querySelector(
    'nav.tepll-pll-language-switcher'
);

el.style.setProperty(
    'transform',
    'translateX(-17px)',
    'important'
);

执行后,页面底部的横向滚动条立即消失。

不过,这只能说明语言切换器是页面中最靠右的元素。

它并不能证明语言切换器本身就是导致布局溢出的根因。

如果直接把下面的代码加入正式 CSS:

CSS
transform: translateX(-17px);

虽然可以让滚动条消失,但本质上只是使用固定偏移量补偿布局错误。

因此,我没有把这种方式作为最终修复方案。


五、检查语言切换器及其父级容器

接下来,我检查了语言切换器及其父级元素的实际宽度。

控制台结果显示,语言切换器所在的 Flex 父容器:

Plaintext
clientWidth: 397px
scrollWidth: 429px

也就是说:

Plaintext
父容器可用宽度:397px
内部元素所需宽度:429px
内部溢出宽度:约 32px
【图 3,父容器 clientWidth 为 397、scrollWidth 为 429】
【图 3,父容器 clientWidth 为 397、scrollWidth 为 429】

查看当时的额外 CSS,可以发现页头搜索框使用了:

CSS
.wp-block-search {
    flex: 3 1 0;
    min-width: 250px;
    margin: 0 10px;
}

语言切换器本身大约需要:

Plaintext
160px

搜索框和语言切换器最低占用空间大约是:

Plaintext
搜索框最小宽度:250px
搜索框左边距:10px
搜索框右边距:10px
语言切换器宽度:160px

合计:430px

而它们所在的父容器实际只有:

Plaintext
397px

这与控制台检测到的:

Plaintext
scrollWidth: 429px

基本吻合。

因此,真实的 CSS 布局问题是:

搜索框设置了 min-width: 250px,导致 Flex 空间不足时搜索框无法继续缩小,最终把英文语言切换器推出了父容器。

英文单词 English 占用的宽度比中文语言名称更大,所以英文页面更容易暴露这个问题。


六、修改页头搜索框的最小宽度

我将原来的规则:

CSS
.wp-block-search {
    flex: 3 1 0;
    min-width: 250px;
    margin: 0 10px;
}

调整为:

CSS
@media (min-width: 769px) {
    header.wp-block-template-part .wp-block-search {
        flex: 3 1 0;
        min-width: 0;
        margin: 0 10px;
    }
}

当前保存的额外 CSS 中,页头搜索框已经采用限定到 Header 的选择器,并将 min-width 调整为了 0

这次修改主要包含两个部分。

1. 允许搜索框继续收缩

原来是:

CSS
min-width: 250px;

修改为:

CSS
min-width: 0;

这样,当 Flex 父容器空间不足时,搜索框可以继续缩小,不会强制占用至少 250px。

2. 将作用范围限制在页头

原来的选择器:

CSS
.wp-block-search

会影响网站中的所有搜索区块。

修改后:

CSS
header.wp-block-template-part .wp-block-search

只会影响页头中的搜索框,不会干扰侧边栏、正文或其他模板中的搜索区块。

正常情况下,保存额外 CSS 后,英文页面的横向滚动条应该立即消失。

但实际保存后,页面仍然没有发生变化。


七、后台已经保存新 CSS,英文页面却仍输出旧规则

查看英文页面源代码后,我发现前台仍然输出旧规则:

CSS
.wp-block-search {
    flex: 3 1 0;
    min-width: 250px;
    margin: 0 10px;
}

但 WordPress 后台保存的内容已经是:

CSS
header.wp-block-template-part .wp-block-search {
    flex: 3 1 0;
    min-width: 0;
    margin: 0 10px;
}

也就是说:

Plaintext
后台已经保存新 CSS
数据库内容已经更新
英文前台仍然输出旧 CSS
【图 4,后台额外 CSS 已修改,但英文源代码仍显示 min-width: 250px】
【图 4,后台额外 CSS 已修改,但英文源代码仍显示 min-width: 250px】

到这里,问题已经不再只是 CSS 布局本身,而是:

为什么 WordPress 前台没有读取到数据库中已经保存的新 CSS?


八、后台清除 W3TC 缓存后,英文页面仍未更新

当前 WordPress 后台统一使用:

Plaintext
https://www.shuijingwanwq.com/wp-admin/

为了收敛后台入口,访问:

Plaintext
https://en.shuijingwanwq.com/wp-admin/

时,会被 301 跳转到主域名后台。

我在 www 后台执行了 W3 Total Cache 的“清除全部缓存”,但英文页面仍然输出旧 CSS。

这时最初怀疑:

Polylang 使用不同域名后,从主域名后台执行 W3TC 缓存清理,可能没有完整更新英文语言域名的缓存。

不过,后续排查证明,这次真正残留的不是 Cloudflare 边缘缓存,而是 W3TC Object Cache 中的旧对象。


九、排除 Cloudflare 边缘缓存

为了排除 Cloudflare 缓存,我给英文文章 URL 添加了随机查询参数,并检查响应头:

Bash
u="https://en.shuijingwanwq.com/2026/07/11/19327/?_csscheck=$(date +%s)"

curl -sS -D - -o /dev/null "$u" \
| grep -Ei "HTTP/|server:|eo-cache-status:|cf-cache-status:|age:|cache-control:|x-cache:|via:"

返回结果:

Plaintext
HTTP/2 200
server: cloudflare
cache-control: max-age=14400
cf-cache-status: MISS

其中:

Plaintext
cf-cache-status: MISS

说明这次请求没有直接命中 Cloudflare 边缘节点中的旧缓存,而是重新向源站获取了页面。

但返回的 HTML 中仍然包含:

CSS
min-width: 250px;

因此可以确认:

Plaintext
浏览器缓存不是根因
Cloudflare 边缘缓存也不是根因
源站返回的内容本身就是旧 CSS
【图 5,Cloudflare 返回 MISS,但 HTML 中仍然包含旧 CSS】
【图 5,Cloudflare 返回 MISS,但 HTML 中仍然包含旧 CSS】

十、检查 W3TC 的多域名页面缓存目录

W3 Total Cache Page Cache 会在下面的目录中保存页面缓存:

Plaintext
/data/wwwroot/www.shuijingwanwq.com/wp-content/cache/page_enhanced/

进入该目录后,可以看到两个语言域名对应的独立目录:

Plaintext
en.shuijingwanwq.com
www.shuijingwanwq.com

英文域名目录中也有完整的缓存结构:

Plaintext
2014
2018
2019
2021
2025
2026
category
series
tag
【图 6,page_enhanced 下同时存在 en 和 www 缓存目录】
【图 6,page_enhanced 下同时存在 en 和 www 缓存目录】

这说明 W3TC Page Cache 会按照请求 Host 分别保存两个域名的 HTML 页面缓存。

不过,在英文缓存目录中,没有找到直接以文章 ID 19327 命名的缓存路径。

因此,这次不能简单通过删除某个 19327 目录来解决问题。

后续直接绕过 Cloudflare访问源站,返回的仍然是旧 CSS,也说明问题已经进入 WordPress 运行时和对象缓存层。


十一、绕过 Cloudflare,直接请求源站

为了确认旧 CSS 是否来自源站,我在服务器中使用 --resolve,让英文域名直接解析到本机 Nginx:

Bash
ts=$(date +%s)
url="/2026/07/11/19327/?origin_check=$ts"

curl -ksS \
  --resolve en.shuijingwanwq.com:443:127.0.0.1 \
  -H "Cache-Control: no-cache" \
  "https://en.shuijingwanwq.com$url" \
  -o /tmp/en-origin.html

curl -ksS \
  -H "Cache-Control: no-cache" \
  "https://en.shuijingwanwq.com$url" \
  -o /tmp/en-public.html

然后对比源站和公网返回的 CSS:

Bash
python3 - <<'PY'
import re

for name, path in [
    ("直接访问源站", "/tmp/en-origin.html"),
    ("通过公网 Cloudflare", "/tmp/en-public.html"),
]:
    with open(path, "r", encoding="utf-8", errors="ignore") as f:
        html = f.read()

    rules = re.findall(
        r'(?:header\.wp-block-template-part\s+)?\.wp-block-search\s*\{.*?\}',
        html,
        flags=re.S
    )

    matched = [
        re.sub(r'\s+', ' ', rule).strip()
        for rule in rules
        if "flex: 3 1 0" in rule or "min-width:" in rule
    ]

    print(f"\n===== {name} =====")
    print("包含 min-width: 0:", "min-width: 0" in html)
    print("包含 min-width: 250px:", "min-width: 250px" in html)

    for rule in matched[-5:]:
        print(rule)
PY

输出:

Plaintext
===== 直接访问源站 =====
包含 min-width: 0:False
包含 min-width: 250px:True
.wp-block-search { flex: 3 1 0; min-width: 250px; margin: 0 10px; }

===== 通过公网 Cloudflare =====
包含 min-width: 0:False
包含 min-width: 250px:True
.wp-block-search { flex: 3 1 0; min-width: 250px; margin: 0 10px; }

这一步证明:

Plaintext
Cloudflare 返回的是旧 CSS
直接访问源站同样返回旧 CSS

因此,问题位于 WordPress 运行时,而不是 Cloudflare。


十二、查询数据库中的 wp_global_styles

Twenty Twenty-Five 是区块主题。

通过站点编辑器保存的全局样式和额外 CSS,会保存在 wp_global_styles 类型的文章记录中。

我使用下面的 PHP 脚本查询数据库:

Bash
cd /data/wwwroot/www.shuijingwanwq.com

php -d display_errors=1 <<'PHP'
<?php
require __DIR__ . '/wp-load.php';

global $wpdb;

$rows = $wpdb->get_results("
    SELECT
        ID,
        post_type,
        post_status,
        post_name,
        post_title,
        post_modified,
        post_content
    FROM {$wpdb->posts}
    WHERE post_type IN ('wp_global_styles', 'custom_css')
    ORDER BY post_modified DESC, ID DESC
");

foreach ($rows as $row) {
    $content = (string) $row->post_content;

    printf(
        "ID=%d | type=%s | status=%s | name=%s | title=%s | modified=%s | length=%d | old250=%s | new0=%s\n",
        $row->ID,
        $row->post_type,
        $row->post_status,
        $row->post_name,
        $row->post_title,
        $row->post_modified,
        strlen($content),
        strpos($content, 'min-width: 250px') !== false ? 'YES' : 'NO',
        strpos($content, 'min-width: 0') !== false ? 'YES' : 'NO'
    );
}
PHP

输出:

Plaintext
ID=17390
type=wp_global_styles
status=publish
name=wp-global-styles-twentytwentyfive
title=Custom Styles
old250=NO
new0=YES

这说明数据库中的 wp_global_styles 已经是新版本:

Plaintext
min-width: 0

数据库中也没有另一条仍然包含 min-width: 250px 的 Twenty Twenty-Five 全局样式记录。

因此,问题不是 CSS 没有保存,也不是数据库中存在两份新旧全局样式。


十三、WordPress 运行时仍然生成旧 CSS

接下来,我通过 PHP 直接加载 WordPress,并模拟英文域名请求:

Bash
cd /data/wwwroot/www.shuijingwanwq.com

php -d display_errors=1 <<'PHP'
<?php

$_SERVER['HTTP_HOST']       = 'en.shuijingwanwq.com';
$_SERVER['SERVER_NAME']     = 'en.shuijingwanwq.com';
$_SERVER['REQUEST_URI']     = '/2026/07/11/19327/';
$_SERVER['HTTPS']           = 'on';
$_SERVER['SERVER_PORT']     = '443';
$_SERVER['REQUEST_SCHEME']  = 'https';

require __DIR__ . '/wp-load.php';

$global_css = function_exists('wp_get_global_stylesheet')
    ? wp_get_global_stylesheet()
    : '';

$custom_css = function_exists('wp_get_global_styles_custom_css')
    ? wp_get_global_styles_custom_css()
    : '';

$css = $global_css . "\n" . $custom_css;

echo "CSS length: " . strlen($css) . PHP_EOL;

echo "包含 min-width: 0:"
    . (strpos($css, 'min-width: 0') !== false ? 'YES' : 'NO')
    . PHP_EOL;

echo "包含 min-width: 250px:"
    . (strpos($css, 'min-width: 250px') !== false ? 'YES' : 'NO')
    . PHP_EOL;

if (preg_match(
    '/(?:header\.wp-block-template-part\s+)?\.wp-block-search\s*\{[^}]*\}/s',
    $css,
    $match
)) {
    echo PHP_EOL . "匹配到的搜索框规则:" . PHP_EOL;
    echo preg_replace('/\s+/', ' ', trim($match[0])) . PHP_EOL;
}
PHP

输出:

Plaintext
CSS length: 39474
包含 min-width: 0:NO
包含 min-width: 250px:YES

匹配到的搜索框规则:
.wp-block-search { flex: 3 1 0; min-width: 250px; margin: 0 10px; }

这时已经可以确认:

Plaintext
数据库里是新版 CSS
WordPress 运行时仍然生成旧版 CSS

由于当前站点启用了 W3 Total Cache Object Cache,并使用 Redis 作为缓存后端,因此接下来重点检查 W3TC Object Cache 返回的对象内容。


十四、对比数据库原始值与 get_post() 返回值

为了确认对象缓存是否陈旧,我继续对比:

  1. 直接通过 SQL 读取数据库;
  2. 通过 WordPress get_post() 读取;
  3. 当前是否正在使用外部对象缓存。

执行:

Bash
cd /data/wwwroot/www.shuijingwanwq.com

php -d display_errors=1 <<'PHP'
<?php

$_SERVER['HTTP_HOST']      = 'en.shuijingwanwq.com';
$_SERVER['SERVER_NAME']    = 'en.shuijingwanwq.com';
$_SERVER['REQUEST_URI']    = '/2026/07/11/19327/';
$_SERVER['HTTPS']          = 'on';
$_SERVER['SERVER_PORT']    = '443';
$_SERVER['REQUEST_SCHEME'] = 'https';

require __DIR__ . '/wp-load.php';

global $wpdb;

$id = 17390;

$raw_database = (string) $wpdb->get_var(
    $wpdb->prepare(
        "SELECT post_content FROM {$wpdb->posts} WHERE ID = %d",
        $id
    )
);

$post_object = get_post($id);

$cached_post = $post_object
    ? (string) $post_object->post_content
    : '';

$stylesheet = function_exists('wp_get_global_stylesheet')
    ? (string) wp_get_global_stylesheet()
    : '';

function report_css($name, $content) {
    echo PHP_EOL . "===== {$name} =====" . PHP_EOL;
    echo "长度:" . strlen($content) . PHP_EOL;

    echo "min-width: 0:"
        . (strpos($content, 'min-width: 0') !== false ? 'YES' : 'NO')
        . PHP_EOL;

    echo "min-width: 250px:"
        . (strpos($content, 'min-width: 250px') !== false ? 'YES' : 'NO')
        . PHP_EOL;
}

echo "使用外部对象缓存:"
    . (wp_using_ext_object_cache() ? 'YES' : 'NO')
    . PHP_EOL;

report_css('数据库原始内容', $raw_database);
report_css('get_post() 返回内容', $cached_post);
report_css('wp_get_global_stylesheet() 生成结果', $stylesheet);
PHP

输出:

Plaintext
使用外部对象缓存:YES

===== 数据库原始内容 =====
长度:17407
min-width: 0:YES
min-width: 250px:NO

===== get_post() 返回内容 =====
长度:17317
min-width: 0:NO
min-width: 250px:YES

这个结果已经可以明确说明:

Plaintext
数据库中的 wp_global_styles 已经更新

W3TC Object Cache 中仍然保存旧的 post 对象

get_post(17390) 返回旧内容

WordPress 运行时继续生成旧 CSS
【图 7,数据库原始内容与 get_post() 返回内容不一致】
【图 7,数据库原始内容与 get_post() 返回内容不一致】

这里已经不需要继续猜测 Cloudflare、Nginx 或浏览器缓存。

数据库原始内容与 get_post() 返回内容不一致,而清理对象缓存后恢复正常,这已经直接证明问题发生在 WordPress 对象缓存层。


十五、精确清理 wp_global_styles 对象缓存

由于已经确认出现问题的是:

Plaintext
wp_global_styles ID:17390

所以这一次没有直接执行:

Bash
redis-cli FLUSHALL

而是通过 WordPress API,只清理这条文章对象缓存以及 Theme JSON 相关缓存:

Bash
cd /data/wwwroot/www.shuijingwanwq.com

php -d display_errors=1 <<'PHP'
<?php

$_SERVER['HTTP_HOST']      = 'en.shuijingwanwq.com';
$_SERVER['SERVER_NAME']    = 'en.shuijingwanwq.com';
$_SERVER['REQUEST_URI']    = '/2026/07/11/19327/';
$_SERVER['HTTPS']          = 'on';
$_SERVER['SERVER_PORT']    = '443';
$_SERVER['REQUEST_SCHEME'] = 'https';

require __DIR__ . '/wp-load.php';

$id = 17390;

/* 清理指定 wp_global_styles 文章的对象缓存 */
clean_post_cache($id);

/* 清理 WordPress Theme JSON / 全局样式缓存 */
if (function_exists('wp_clean_theme_json_cache')) {
    wp_clean_theme_json_cache();
}

/* 重新读取并验证 */
$post = get_post($id);
$content = $post ? (string) $post->post_content : '';

echo "已清理对象缓存。" . PHP_EOL;

echo "get_post() 包含 min-width: 0:"
    . (strpos($content, 'min-width: 0') !== false ? 'YES' : 'NO')
    . PHP_EOL;

echo "get_post() 包含 min-width: 250px:"
    . (strpos($content, 'min-width: 250px') !== false ? 'YES' : 'NO')
    . PHP_EOL;
PHP

输出:

Plaintext
已清理对象缓存。
get_post() 包含 min-width: 0:YES
get_post() 包含 min-width: 250px:NO

这说明 W3TC Object Cache 中旧的 wp_global_styles 文章对象已经被清理,WordPress 重新读取到了数据库中的新内容。

【图 8,执行 clean_post_cache 后 get_post() 已读取新版 CSS】
【图 8,执行 clean_post_cache 后 get_post() 已读取新版 CSS】

需要注意:

Plaintext
17390

只是我当前网站中对应的 wp_global_styles 记录 ID。

其他网站不能直接照搬这个 ID,必须先查询自己站点数据库中的实际记录。


十六、再次验证公网英文页面

最后,在本地电脑中重新请求英文页面:

Bash
u="https://en.shuijingwanwq.com/2026/07/11/19327/?_csscheck=$(date +%s)"

curl -ksS "$u" |
python3 -c '
import sys

html = sys.stdin.read()

print("包含 min-width: 0:", "min-width: 0" in html)
print("包含 min-width: 250px:", "min-width: 250px" in html)
'

输出:

Plaintext
包含 min-width: 0:True
包含 min-width: 250px:False

重新打开英文文章页面后,底部的横向滚动条已经消失。

【图 9,英文文章横向滚动条消失,页面恢复正常】
【图 9,英文文章横向滚动条消失,页面恢复正常】

十七、这次问题实际包含两层原因

这次故障不能简单归结为语言切换器样式,也不能简单归结为缓存没有清理。

它实际上包含两层相互叠加的问题。

第一层:CSS 布局确实存在问题

页头搜索框原来设置了:

CSS
min-width: 250px;

在父级 Flex 容器空间不足时,搜索框无法继续缩小,最终把英文语言切换器推出页面。

正确修复方式是:

CSS
min-width: 0;

而不是使用:

CSS
transform: translateX(-17px);

强行把语言切换器向左移动。

第二层:W3TC Object Cache 仍然返回旧 CSS 对象

虽然后台已经保存了新的额外 CSS,数据库中的 wp_global_styles 也已经更新,但 W3TC Object Cache 仍然保存着旧的文章对象。

因此:

Plaintext
CSS 已经修改正确

数据库已经更新

get_post() 仍然返回旧对象

WordPress 继续生成旧 CSS

英文前台仍然存在横向滚动条

只有执行:

PHP
clean_post_cache(17390);

清理对应的对象缓存后,真正的 CSS 修复才开始生效。


十八、与上一篇 Polylang 多域名迁移问题的联系

这并不是我第一次在 Polylang 多域名模式下遇到 W3TC 缓存异常。

上一篇文章记录的迁移过程中,实际上连续出现了两个问题。

第一个问题:W3TC Page Cache 将英文域名识别成 foreign domain

当时访问:

Plaintext
https://en.shuijingwanwq.com/

会被 301 跳转回:

Plaintext
https://www.shuijingwanwq.com/

最终确认跳转来自:

Plaintext
W3TC\PgCache_Plugin->redirect_on_foreign_domain

也就是说,W3 Total Cache Page Cache 按照 WordPress 主站域名判断请求,没有正确把 Polylang 配置的英文语言域名视为合法域名。

最终,我通过一个 mu-plugin,为 Polylang 已配置的语言域名移除了这项 foreign domain 跳转。

修复后:

Plaintext
en.shuijingwanwq.com

不再跳回:

Plaintext
www.shuijingwanwq.com

第二个问题:W3TC Object Cache 缓存了旧的 Polylang 配置

301 问题解决后,访问英文子域名时又出现了另一个问题:

HTML
<html lang="zh-CN">

Canonical 也仍然指向:

Plaintext
https://www.shuijingwanwq.com/

这说明请求虽然已经进入:

Plaintext
en.shuijingwanwq.com

但 WordPress 运行时仍然把它识别成中文主站。

数据库中的 Polylang 配置已经更新为:

Plaintext
force_lang = 3
domains[en] = https://en.shuijingwanwq.com
domains[zh] = https://www.shuijingwanwq.com

但 WordPress 运行时读取到的仍然是旧配置:

Plaintext
force_lang = 1
domains[en] =
domains[zh] = https://www.shuijingwanwq.com
pll_home_url_en = https://www.shuijingwanwq.com/en/

这说明 W3TC Object Cache 中仍然保存着旧的 Polylang option。

当时我直接执行:

Bash
redis-cli FLUSHALL

输出:

Plaintext
OK

清理后,WordPress 运行时才恢复为正确配置:

Plaintext
force_lang = 3
domains[en] = https://en.shuijingwanwq.com
domains[zh] = https://www.shuijingwanwq.com
pll_home_url_en = https://en.shuijingwanwq.com/
pll_home_url_zh = https://www.shuijingwanwq.com/

需要特别说明:

如果一个 Redis 实例同时被多个网站或多个业务使用,不建议直接执行 FLUSHALL

FLUSHALL 会清空整个 Redis 实例中的所有数据库和缓存。

我当时这样处理,是因为该 Redis 实例主要用于当前 WordPress 网站,对其他业务的影响可控。


十九、两次故障的准确关系

因此,不能把两次故障简单概括为:

Plaintext
第一次:Page Cache 问题
第二次:Object Cache 问题

更准确的关系是:

Plaintext
第一次迁移过程中:

1. W3TC Page Cache
   将 Polylang 英文域名识别成 foreign domain。

2. W3TC Object Cache
   保留了旧的 Polylang option。


这一次横向滚动条问题:

3. W3TC Object Cache
   保留了旧的 wp_global_styles post 对象。

两次 W3TC Object Cache 异常影响的是不同类型的数据:

Plaintext
第一次:
Polylang option

这一次:
wp_global_styles post object

但它们的证据链高度相似:

Plaintext
数据库原始内容已经更新

WordPress API 运行时仍然返回旧内容

清理 W3TC Object Cache 后恢复正常

第一次导致的是:

Plaintext
英文域名识别错误
<html lang> 错误
Canonical 错误
英文首页 URL 错误

这一次导致的是:

Plaintext
全局 CSS 继续使用旧版本
搜索框 min-width 修改不生效
英文文章持续出现横向滚动条

二十、当前能够确认的 W3TC 缓存问题

结合前后两次排查,目前已经能够确认三个具体问题。

1. Page Cache 对 Polylang 语言域名识别异常

Plaintext
en.shuijingwanwq.com

redirect_on_foreign_domain

www.shuijingwanwq.com

该问题最终通过 mu-plugin 补丁解决。

2. Object Cache 保留旧的 Polylang option

Plaintext
数据库:
force_lang = 3

WordPress 运行时:
force_lang = 1

清理 Redis 后:
force_lang = 3

3. Object Cache 保留旧的 wp_global_styles 对象

Plaintext
数据库 post_content:
min-width: 0

get_post(17390):
min-width: 250px

clean_post_cache(17390) 后:
min-width: 0

这里不再需要继续假设 Redis 缓存键是否包含 Host,也不需要把问题归因于独立的 Redis Object Cache 插件。

当前站点启用的是:

Plaintext
W3 Total Cache Object Cache

Redis 是其缓存后端。

而这两次对象缓存问题的共同事实是:

WordPress 数据库已经更新,但 W3TC Object Cache 仍然返回旧的 option 或 post 对象。

至于为什么正常保存操作没有让对应对象及时失效,仍然需要结合 W3TC 的对象缓存实现继续分析。

但对于这次故障的定位和修复来说,证据已经足够完整。


二十一、排查这类问题时的建议顺序

以后如果再次遇到“后台已经修改,但前台始终不更新”的问题,可以按照下面的顺序排查。

1. 先确认真实的前端问题

不要一开始就用:

CSS
overflow-x: hidden;

或者:

CSS
transform: translateX(...);

掩盖布局问题。

应该先通过浏览器控制台找到真正超出视口的元素及其父级容器。

2. 查看前台实际输出的源代码

确认浏览器当前拿到的是旧 CSS,还是新 CSS。

3. 排除 CDN 边缘缓存

查看:

Plaintext
cf-cache-status
age
cache-control

必要时添加随机查询参数。

4. 直接请求源站

使用:

Bash
curl --resolve

区分 CDN 问题与源站问题。

5. 对比数据库和 WordPress API 返回值

例如:

Plaintext
数据库 SQL 原始值
get_option() 返回值
get_post() 返回值
wp_get_global_stylesheet() 返回值

如果数据库是新值,但 WordPress API 返回旧值,就应该重点检查 Object Cache。

6. 优先精确清理对象

已知具体文章 ID 时,可以使用:

PHP
clean_post_cache($id);

而不是直接执行:

Bash
redis-cli FLUSHALL

只有在无法确定具体缓存对象,并且 Redis 实例影响范围可控时,才考虑清空整个 Redis。


总结

这次英文文章横向滚动条问题的完整链路是:

Plaintext
页头搜索框 min-width: 250px

搜索框和英文语言切换器
无法同时装进父级 Flex 容器

英文页面出现横向滚动条

将搜索框改为 min-width: 0

数据库中的 wp_global_styles 更新成功

W3TC Object Cache 仍然返回旧 post 对象

get_post() 继续得到 min-width: 250px

英文前台继续输出旧 CSS

clean_post_cache() 清理指定对象

WordPress 重新读取数据库中的新 CSS

英文页面横向滚动条消失

结合上一篇 Polylang 多域名迁移排查,目前已经遇到:

Plaintext
W3TC Page Cache 多域名识别问题
W3TC Object Cache 缓存旧 Polylang option
W3TC Object Cache 缓存旧 wp_global_styles post

这也说明,在 WordPress 多域名环境中,看到前台内容没有更新时,不能只检查:

Plaintext
浏览器缓存
Cloudflare 缓存
Nginx 缓存
W3TC Page Cache

还应该继续比较:

Plaintext
数据库原始内容
get_option() 返回内容
get_post() 返回内容
wp_get_global_stylesheet() 生成内容
W3TC Object Cache 中的对象

尤其是在下面这种架构中:

Plaintext
后台固定使用主域名
不同语言使用独立域名
多个域名共享同一个 WordPress
多个域名共享同一个数据库
多个域名共享同一个 W3TC Object Cache

“数据库已经更新”和“所有语言页面运行时都已经读取到新数据”,并不是同一件事。

WordPress 英文站日历链接变成双重 URL:Polylang 子域名迁移后的 WPCode 与 W3TC Object Cache 排查 WordPress Polylang 英文子域名迁移后链接仍指向中文站:分类下拉、面包屑与区块模板的完整修复

WordPress 网站维护、性能优化与博客运营咨询

本站已持续运营超过 10 年,累计发布 1000+ 篇原创技术文章,长期实践 WordPress 网站建设、CDN / Cloudflare 配置、缓存优化、Google SEO、广告变现和多语言网站运营。

如果你的 WordPress 网站遇到访问慢、缓存异常、插件冲突、广告不显示、SEO 基础结构混乱、CDN 配置不确定等问题,可以联系我做一次远程技术排查。

适合以下用户:
✅ 个人博客站长
✅ WordPress 网站运营者
✅ 独立开发者与内容创作者
✅ SaaS 产品官网运营团队
✅ 希望优化网站速度与稳定性的站点

服务内容:
✅ WordPress 速度优化
✅ Cloudflare / CDN / 缓存配置排查
✅ 插件冲突与页面异常排查
✅ AdSense 广告显示问题排查
✅ SEO 基础结构检查
✅ 博客运营与商业化咨询

如需了解方案或交流相关问题,请直接联系我,并注明:WordPress 维护咨询

联系方式:
Telegram:@shuijingwan
微信:13980074657
邮箱:shuijingwanwq@gmail.com