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

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

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

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 系列文章

WordPress 经典编辑器代码块无损迁移:批量转换 SyntaxHighlighter 短代码为 Gutenberg 区块

(9) WordPress 经典编辑器代码块无损迁移:批量转换 SyntaxHighlighter 短代码为 Gutenberg 区块

点击翻译按钮后,遇到一个弹窗“神秘”闪退问题及解决方法

(10) 点击翻译按钮后,遇到一个弹窗“神秘”闪退问题及解决方法

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

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

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

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

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

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

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

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

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

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

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

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

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

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

截图 5:验证 301 跳转

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

脚本显示处理了 8324 个标签

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

图5:DeepL API 注册页面,国家或地区列表中没有中国大陆

(20) 中国大陆 WordPress 博客想提升英文翻译质量,为什么这么麻烦?一次 Polylang + AutoPoly + DeepL API 排查复盘

图5:DeepSeek 的评分结果截图

(21) Chrome Built-in AI、Yandex Translate、ChatGPT Plus 翻译质量对比:我为什么决定重点英文文章改用 ChatGPT Plus

[截图 2:浏览器 Network 中直接请求 translate.yandex.net]

(22) 从 AutoPoly 到 Yandex 网页版:一次 WordPress 英文自动翻译质量验证

【图1:AutoPoly Free 与 Pro 功能对比截图】

(23) AutoPoly Pro + OpenAI API 能否自动化 WordPress 英文翻译?一次从技术到支付的可行性分析

【图2:点击“立即试用”后,ChatGPT 输入框中出现“代理模式”】

(24) ChatGPT Agent 能否替代 ChatGPT Plus 完成 WordPress 英文重译?

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

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

这篇文章是此前 Polylang 多域名与 W3 Total Cache 对象缓存问题的后续记录。

上一篇排查记录:

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

上一篇文章中,WordPress 数据库已经保存了新的 CSS,但英文域名仍然读取旧对象。最终确认,在当前 Polylang 多域名环境中,W3TC Object Cache 使用 Redis 作为存储后端时,wwwen 两个 Host 可能出现代码或对象更新状态不一致的问题。

这一次出现的现象更加明显:英文首页中的日历日期链接,被拼接成了包含两个完整域名的错误 URL。


一、问题现象:日历链接中出现两个完整 URL

当前网站的语言结构是:

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

访问英文首页后,将鼠标移动到日历中的日期链接上,可以看到链接变成:

Plaintext
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/07/08/

正确链接本应是:

Plaintext
https://en.shuijingwanwq.com/2026/07/08/

错误 URL 同时包含:

Plaintext
旧英文目录结构:
https://www.shuijingwanwq.com/en/

当前英文子域名结构:
https://en.shuijingwanwq.com/

也就是说,一个完整的英文子域名 URL,被再次当成相对路径拼接到了旧的 /en/ 地址后面。

中文站中的日历链接正常,只有英文子域名受到影响。

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

二、最开始怀疑 W3 Total Cache 或 Polylang

由于当前网站同时使用:

Plaintext
Polylang 多域名模式
W3 Total Cache Page Cache
W3 Total Cache Object Cache
Redis 对象缓存后端
Cloudflare

而且最近刚刚将英文站从:

Plaintext
https://www.shuijingwanwq.com/en/

迁移到:

Plaintext
https://en.shuijingwanwq.com/

最开始看到这种链接时,很容易怀疑是:

Plaintext
Polylang 日期归档链接生成错误
W3TC 缓存了迁移前的语言配置
Redis 中仍然保存旧的首页 URL
Cloudflare 返回旧页面

不过,检查之前为日历编写的 WPCode 代码片段后,很快找到了第一个直接原因。


三、真正的第一层原因:旧 WPCode 片段仍按 /en/ 模式拼接链接

为了修复 Polylang 不同语言文章日期不一致的问题,我之前添加过一个日历修复代码片段。

它会完成以下操作:

  1. 查询当前语言当月有文章的日期;
  2. 为应该有文章的日期补充链接;
  3. 移除只属于其他语言的错误链接;
  4. 重建上一月和下一月导航;
  5. 修正日期归档链接的语言前缀。

其中,旧版本使用下面的方式构造非默认语言 URL:

PHP
$home_url = home_url( '/' );

$fix_url = function( $url ) use (
    $current_lang,
    $default_lang,
    $home_url
) {
    if ( $current_lang === $default_lang ) {
        return $url;
    }

    if (
        preg_match(
            '#^' . preg_quote( $home_url, '#' ) . '[a-z]{2}/#i',
            $url
        )
    ) {
        $url = preg_replace(
            '#^' . preg_quote( $home_url, '#' ) . '[a-z]{2}/#i',
            '',
            $url
        );

        $url = $home_url . $url;
    }

    if (
        strpos(
            $url,
            $home_url . $current_lang . '/'
        ) === false
    ) {
        $relative = str_replace(
            $home_url,
            '',
            $url
        );

        return $home_url .
            $current_lang .
            '/' .
            $relative;
    }

    return $url;
};

这段代码是在英文站仍使用 /en/ 路径时编写的。

当时的 URL 结构是:

Plaintext
https://www.shuijingwanwq.com/en/2026/07/08/

因此,下面的拼接逻辑在旧架构中是合理的:

PHP
$home_url . $current_lang . '/'

它生成的是:

Plaintext
https://www.shuijingwanwq.com/ + en/

但是迁移到独立子域名后,Polylang 已经能够返回:

Plaintext
https://en.shuijingwanwq.com/2026/07/08/

旧代码仍会继续执行:

PHP
$relative = str_replace(
    $home_url,
    '',
    $url
);

此时:

Plaintext
$home_url:
https://www.shuijingwanwq.com/

$url:
https://en.shuijingwanwq.com/2026/07/08/

由于两个 URL 的 Host 不同,str_replace() 无法移除主域名。

所以 $relative 仍然是完整绝对地址:

Plaintext
https://en.shuijingwanwq.com/2026/07/08/

最后继续拼接:

PHP
return $home_url .
    $current_lang .
    '/' .
    $relative;

最终就变成:

Plaintext
https://www.shuijingwanwq.com/
+
en/
+
https://en.shuijingwanwq.com/2026/07/08/

也就是页面中实际出现的错误链接:

Plaintext
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/07/08/


四、为什么中文站没有出现问题

旧代码中包含下面的判断:

PHP
if ( $current_lang === $default_lang ) {
    return $url;
}

当前默认语言是中文,因此中文站进入函数后会直接返回原始 URL。

只有英文等非默认语言,才会继续执行 /en/ 拼接逻辑。

所以最终表现为:

Plaintext
www.shuijingwanwq.com:正常
en.shuijingwanwq.com:错误

这与页面实际现象完全吻合。


五、调整方案:不再写死 /en/,改用 Polylang 的真实语言首页

原有代码中,按语言查询文章、补全日期链接、移除错误链接和重建月份导航的逻辑仍然有用。

真正需要移除的是:

PHP
$home_url . $current_lang . '/'

新的版本通过:

PHP
pll_home_url( $current_lang )

获取当前语言实际配置的首页。

这样可以同时适配:

Plaintext
语言目录:
https://example.com/en/

语言子域名:
https://en.example.com/

独立语言域名:
https://example-en.com/

本次修改使用的核心 URL 归一化函数如下:

PHP
/**
 * 将日期归档 URL 归一化到指定语言的真实首页。
 */
function swq_polylang_calendar_language_url(
    $url,
    $lang
) {
    if ( ! function_exists( 'pll_home_url' ) ) {
        return $url;
    }

    $language_home = pll_home_url( $lang );

    if ( empty( $language_home ) ) {
        return $url;
    }

    $source_parts = wp_parse_url( $url );
    $target_parts = wp_parse_url( $language_home );

    if (
        ! is_array( $source_parts ) ||
        ! is_array( $target_parts ) ||
        empty( $target_parts['host'] )
    ) {
        return $url;
    }

    $target_scheme = ! empty(
        $target_parts['scheme']
    )
        ? $target_parts['scheme']
        : 'https';

    $target_origin =
        $target_scheme .
        '://' .
        $target_parts['host'];

    if ( ! empty( $target_parts['port'] ) ) {
        $target_origin .=
            ':' .
            (int) $target_parts['port'];
    }

    $source_path = ! empty(
        $source_parts['path']
    )
        ? '/' . ltrim(
            $source_parts['path'],
            '/'
        )
        : '/';

    $target_base_path = ! empty(
        $target_parts['path']
    )
        ? '/' . trim(
            $target_parts['path'],
            '/'
        )
        : '';

    if ( '/' === $target_base_path ) {
        $target_base_path = '';
    }

    /*
     * 收集所有语言首页中的路径部分。
     * 如果来源 URL 仍然包含旧语言目录,
     * 先将其移除。
     */
    $known_language_paths = array();

    if ( function_exists( 'pll_languages_list' ) ) {
        $languages = pll_languages_list(
            array(
                'hide_empty' => 0,
                'fields'     => 'slug',
            )
        );

        foreach (
            (array) $languages as $language_slug
        ) {
            $home_parts = wp_parse_url(
                pll_home_url( $language_slug )
            );

            if (
                ! is_array( $home_parts ) ||
                empty( $home_parts['path'] )
            ) {
                continue;
            }

            $language_path =
                '/' .
                trim(
                    $home_parts['path'],
                    '/'
                );

            if ( '/' !== $language_path ) {
                $known_language_paths[] =
                    $language_path;
            }
        }
    }

    $known_language_paths = array_values(
        array_unique( $known_language_paths )
    );

    usort(
        $known_language_paths,
        static function ( $a, $b ) {
            return strlen( $b ) <=> strlen( $a );
        }
    );

    foreach (
        $known_language_paths as $language_path
    ) {
        if (
            $source_path === $language_path ||
            0 === strpos(
                $source_path,
                $language_path . '/'
            )
        ) {
            $source_path = substr(
                $source_path,
                strlen( $language_path )
            );

            $source_path =
                '/' .
                ltrim(
                    (string) $source_path,
                    '/'
                );

            break;
        }
    }

    $had_trailing_slash =
        '/' === substr( $source_path, -1 );

    $final_path =
        '/' .
        trim(
            trim(
                $target_base_path,
                '/'
            ) .
            '/' .
            ltrim(
                $source_path,
                '/'
            ),
            '/'
        );

    if (
        '/' !== $final_path &&
        $had_trailing_slash
    ) {
        $final_path = trailingslashit(
            $final_path
        );
    }

    $final_url = $target_origin . $final_path;

    if ( ! empty( $source_parts['query'] ) ) {
        $source_query_args = array();

        wp_parse_str(
            $source_parts['query'],
            $source_query_args
        );

        if ( ! empty( $source_query_args ) ) {
            $final_url = add_query_arg(
                $source_query_args,
                $final_url
            );
        }
    }

    return esc_url_raw( $final_url );
}

日期链接改为:

PHP
$correct_url =
    swq_polylang_calendar_language_url(
        get_day_link(
            $calendar_year,
            $calendar_month,
            $day_num
        ),
        $current_lang
    );

上一月链接改为:

PHP
$previous_link =
    swq_polylang_calendar_language_url(
        get_month_link(
            $previous_year,
            $previous_month
        ),
        $current_lang
    );

下一月链接改为:

PHP
$next_link =
    swq_polylang_calendar_language_url(
        get_month_link(
            $next_year,
            $next_month
        ),
        $current_lang
    );

保存并启用新的 WPCode 代码片段后,理论上英文日期链接应该立即恢复正常。

但是实际情况并没有这么简单。


六、保存新代码后,英文站仍然执行旧版本

WPCode 中的新代码已经成功保存并启用。

随后在 WordPress 后台执行了:

Plaintext
性能 → 清除所有缓存

为了确认 Polylang 后台语言是否影响缓存清理,我分别在顶部切换到:

Plaintext
中文(中国)
English

然后再次执行“清除所有缓存”。

结果均没有效果。

英文首页中的日期链接仍然是:

Plaintext
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/07/08/

隐私窗口也没有变化。

即使访问带查询参数的地址:

Plaintext
https://en.shuijingwanwq.com/?cache=1

页面仍然输出旧链接。

这说明问题不能简单归因于浏览器缓存。


七、直接访问源站,错误链接仍然存在

为了排除 Cloudflare,我通过 --resolve 将英文域名直接解析到源站的 127.0.0.1

Bash
url="https://en.shuijingwanwq.com/?calendar_origin_v3=$(date +%s%N)"

curl -sS \
  --resolve en.shuijingwanwq.com:443:127.0.0.1 \
  -H "Cache-Control: no-cache" \
  -H "Pragma: no-cache" \
  -D /tmp/en-calendar-origin.headers \
  -o /tmp/en-calendar-origin.html \
  "$url"

检查错误链接:

Bash
grep -oE \
  'https://www\.shuijingwanwq\.com/en/https://en\.shuijingwanwq\.com[^"< ]*' \
  /tmp/en-calendar-origin.html \
  | sort -u

返回了 12 条错误 URL,包括:

Plaintext
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/06/
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/07/01/
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/07/02/
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/07/03/
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/07/04/
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/07/05/
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/07/06/
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/07/07/
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/07/08/
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/07/09/
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/07/10/
https://www.shuijingwanwq.com/en/https://en.shuijingwanwq.com/2026/07/11/

这说明:

Plaintext
Cloudflare 不是本次错误链接的直接来源
源站返回的 HTML 本身就是错误的
【图 2,直接访问源站后仍然检测到 12 条双重 URL】
【图 2,直接访问源站后仍然检测到 12 条双重 URL】

八、使用 POST 请求继续排除源站页面缓存

为了尽量避开当前 GET 页面缓存路径,又使用 POST 请求访问英文源站:

Bash
url="https://en.shuijingwanwq.com/?calendar_post_v3=$(date +%s%N)"

curl -sS \
  --resolve en.shuijingwanwq.com:443:127.0.0.1 \
  -X POST \
  --data "swq_calendar_test=$(date +%s%N)" \
  -D /tmp/en-calendar-post.headers \
  -o /tmp/en-calendar-post.html \
  "$url"

再次统计错误链接:

Bash
grep -o \
  'https://www\.shuijingwanwq\.com/en/https://en\.shuijingwanwq\.com' \
  /tmp/en-calendar-post.html \
  | wc -l

结果仍然是:

Plaintext
12

这时已经可以判断:

英文 Host 实时运行的代码,很可能仍然不是刚刚保存的 V3,而是之前缓存的旧版本。


九、通过临时响应头确认两个 Host 加载的代码不同

为了确认新版代码是否真正加载,我在 WPCode 片段中临时添加了一个响应头:

PHP
add_action(
    'send_headers',
    function () {
        header(
            'X-SWQ-Calendar-V3-Loaded: yes'
        );
    },
    999
);

然后分别以 wwwen 两个 Host 直接访问同一台源站:

Bash
for host in \
"www.shuijingwanwq.com" \
"en.shuijingwanwq.com"
do
  file_key="$(echo "$host" | tr '.' '_')"

  url="https://${host}/?calendar_v3_host_test=$(date +%s%N)"

  curl -sS \
    --resolve "${host}:443:127.0.0.1" \
    -X POST \
    --data "swq_test=$(date +%s%N)" \
    -D "/tmp/${file_key}.headers" \
    -o /dev/null \
    "$url"

  echo
  echo "========== ${host} =========="

  grep -iE \
    '^HTTP/|^x-swq-calendar-v3-loaded:' \
    "/tmp/${file_key}.headers"
done

返回结果:

Plaintext
========== www.shuijingwanwq.com ==========
HTTP/2 200
x-swq-calendar-v3-loaded: yes

========== en.shuijingwanwq.com ==========
HTTP/2 200

结果非常明确:

Plaintext
www Host 已经加载 V3
en Host 没有加载 V3

两个域名使用的是:

Plaintext
同一个 WordPress 根目录
同一套插件
同一个数据库
同一个 Redis
同一个 WPCode 片段

但两个 Host 实际加载到的代码版本不同。

这与上一篇文章中出现的“数据库已经更新,但英文 Host 仍读取旧对象”的现象非常接近。

【图 3,www 响应头包含 V3 标记,但 en 响应头中没有】
【图 3,www 响应头包含 V3 标记,但 en 响应头中没有】

十、按英文 Host 启动 WordPress,并调用 W3TC Object Cache 清理

这次没有直接使用:

Bash
redis-cli FLUSHDB

因为直接清空整个 Redis 数据库影响范围过大,而且当前 Redis 中可能还保存着其他缓存数据。

最终采用的方式是:

  1. 在命令行中模拟 en.shuijingwanwq.com 请求环境;
  2. 加载 WordPress;
  3. 获取 W3TC 的 CacheFlush 组件;
  4. 调用 objectcache_flush()

执行命令:

Bash
WP_LOAD="$(find /data/wwwroot -maxdepth 3 \
  -type f \
  -path '*/www.shuijingwanwq.com/wp-load.php' \
  -print -quit)"

if [ -z "$WP_LOAD" ]; then
  echo "未找到 wp-load.php"
  exit 1
fi

cd "$(dirname "$WP_LOAD")" || exit 1

php -d display_errors=1 -r '
$_SERVER["HTTP_HOST"]       = "en.shuijingwanwq.com";
$_SERVER["SERVER_NAME"]     = "en.shuijingwanwq.com";
$_SERVER["SERVER_PORT"]     = "443";
$_SERVER["HTTPS"]           = "on";
$_SERVER["REQUEST_METHOD"]  = "GET";
$_SERVER["REQUEST_URI"]     = "/";
$_SERVER["SERVER_PROTOCOL"] = "HTTP/1.1";

define("WP_USE_THEMES", false);

require getcwd() . "/wp-load.php";

if (!class_exists("\W3TC\Dispatcher")) {
    fwrite(
        STDERR,
        "错误:W3 Total Cache 未加载\n"
    );
    exit(1);
}

try {
    $cache_flush =
        \W3TC\Dispatcher::component(
            "CacheFlush"
        );

    $result =
        $cache_flush->objectcache_flush();

    echo "英文 Host 的 W3TC Object Cache 清理已调用\n";
    echo "返回值:";

    var_export($result);

    echo PHP_EOL;
} catch (\Throwable $exception) {
    fwrite(
        STDERR,
        "清理失败:" .
        $exception->getMessage() .
        PHP_EOL
    );

    exit(1);
}
'

执行结果:

Plaintext
英文 Host 的 W3TC Object Cache 清理已调用
返回值:NULL

NULL 并不代表执行失败,因为整个过程没有抛出异常。

是否真正生效,需要重新检查英文 Host 的运行结果。

【图 4,按英文 Host 调用 W3TC Object Cache 清理】
【图 4,按英文 Host 调用 W3TC Object Cache 清理】

十一、清理后,英文 Host 立即加载到新代码

再次同时检查 wwwen

Bash
for host in \
"www.shuijingwanwq.com" \
"en.shuijingwanwq.com"
do
  key="$(echo "$host" | tr '.' '_')"

  url="https://${host}/?calendar_after_object_flush=$(date +%s%N)"

  curl -sS \
    --resolve "${host}:443:127.0.0.1" \
    -X POST \
    --data "swq_test=$(date +%s%N)" \
    -D "/tmp/${key}-after-flush.headers" \
    -o "/tmp/${key}-after-flush.html" \
    "$url"

  echo
  echo "========== ${host} =========="

  echo "-- V3 Header --"

  grep -iE \
    '^HTTP/|^x-swq-calendar-v3-loaded:' \
    "/tmp/${key}-after-flush.headers"

  echo "-- Malformed URL Count --"

  grep -o \
    'https://www\.shuijingwanwq\.com/en/https://en\.shuijingwanwq\.com' \
    "/tmp/${key}-after-flush.html" \
    | wc -l

  echo "-- Correct July 8 Link --"

  grep -o \
    'href="https://en\.shuijingwanwq\.com/2026/07/08/"' \
    "/tmp/${key}-after-flush.html" \
    | head -1
done

返回结果:

Plaintext
========== www.shuijingwanwq.com ==========
-- V3 Header --
HTTP/2 200
x-swq-calendar-v3-loaded: yes
-- Malformed URL Count --
0
-- Correct July 8 Link --

========== en.shuijingwanwq.com ==========
-- V3 Header --
HTTP/2 200
x-swq-calendar-v3-loaded: yes
-- Malformed URL Count --
0
-- Correct July 8 Link --
href="https://en.shuijingwanwq.com/2026/07/08/"

清理前:

Plaintext
en Host 没有 V3 响应头
错误 URL 数量:12

清理后:

Plaintext
en Host 已加载 V3
错误 URL 数量:0
日期链接已经恢复正常

浏览器中的英文首页日历也已经正常。

【图 5,英文首页日历日期链接恢复为 en 子域名】
【图 5,英文首页日历日期链接恢复为 en 子域名】

十二、将清理命令保存为可重复使用的服务器脚本

这已经不是第一次遇到:

Plaintext
www 已加载新内容
en 仍然读取旧对象
后台清除全部缓存无效

因此,与其每次复制一大段 PHP 命令,不如将其保存为服务器脚本。

创建目录:

Bash
mkdir -p /root/bin

创建脚本:

Bash
cat > /root/bin/w3tc-flush-host-object-cache <<'BASH'
#!/usr/bin/env bash

set -Eeuo pipefail

HOST="${1:-}"
WP_ROOT="/data/wwwroot/www.shuijingwanwq.com"

case "$HOST" in
    www.shuijingwanwq.com|en.shuijingwanwq.com)
        ;;
    *)
        echo "用法:"
        echo "  $0 www.shuijingwanwq.com"
        echo "  $0 en.shuijingwanwq.com"
        exit 2
        ;;
esac

if [ ! -f "$WP_ROOT/wp-load.php" ]; then
    echo "错误:未找到 $WP_ROOT/wp-load.php"
    exit 1
fi

php -d display_errors=1 -r '
$host    = $argv[1];
$wp_root = $argv[2];

$_SERVER["HTTP_HOST"]       = $host;
$_SERVER["SERVER_NAME"]     = $host;
$_SERVER["SERVER_PORT"]     = "443";
$_SERVER["HTTPS"]           = "on";
$_SERVER["REQUEST_METHOD"]  = "GET";
$_SERVER["REQUEST_URI"]     = "/";
$_SERVER["SERVER_PROTOCOL"] = "HTTP/1.1";

define("WP_USE_THEMES", false);

chdir($wp_root);

require $wp_root . "/wp-load.php";

if (!class_exists("\W3TC\Dispatcher")) {
    fwrite(
        STDERR,
        "错误:W3 Total Cache 未加载\n"
    );

    exit(1);
}

try {
    $cache_flush =
        \W3TC\Dispatcher::component(
            "CacheFlush"
        );

    $cache_flush->objectcache_flush();

    echo "已按以下 Host 启动 WordPress,";
    echo "并调用 W3TC Object Cache 清理:\n";
    echo $host . PHP_EOL;
} catch (\Throwable $exception) {
    fwrite(
        STDERR,
        "清理失败:" .
        $exception->getMessage() .
        PHP_EOL
    );

    exit(1);
}
' "$HOST" "$WP_ROOT"
BASH

设置执行权限:

Bash
chmod 700 /root/bin/w3tc-flush-host-object-cache

以后英文 Host 出现类似问题时,可以执行:

Bash
/root/bin/w3tc-flush-host-object-cache \
  en.shuijingwanwq.com

主域名出现类似问题时,可以执行:

Bash
/root/bin/w3tc-flush-host-object-cache \
  www.shuijingwanwq.com

这个脚本的意义不是只删除某一个页面,而是:

按指定 Host 启动 WordPress,并调用当前 W3 Total Cache 环境中的 Object Cache 清理方法。


十三、这个脚本不应该作为日常操作频繁执行

需要特别说明,这并不是一个每次发布文章后都应该运行的命令。

更适合使用它的场景是:

Plaintext
后台已经保存新代码或新配置
数据库中的数据已经更新
www Host 已经读取到新内容
en Host 仍然读取旧内容
后台“清除所有缓存”没有效果
直接访问源站时仍然可以复现

如果只是普通页面没有更新,应当先依次检查:

Plaintext
浏览器缓存
Cloudflare 或 EdgeOne 缓存
W3TC Page Cache
页面是否真正回源

只有确认源站 WordPress 在不同 Host 下加载的对象状态不一致时,才需要考虑调用这个脚本。

同时,不建议在没有确认影响范围时直接执行:

Bash
redis-cli FLUSHALL

或者:

Bash
redis-cli FLUSHDB

因为这种方式清理范围更大,也更难判断是否会影响同一 Redis 实例中的其他数据。


十四、记得删除临时调试响应头

问题解决后,需要从 WPCode 代码片段中删除临时调试代码:

PHP
add_action(
    'send_headers',
    function () {
        header(
            'X-SWQ-Calendar-V3-Loaded: yes'
        );
    },
    999
);

调试响应头只用于确认:

Plaintext
www 是否加载了 V3
en 是否加载了 V3

问题确认后没有继续保留的必要。

删除并保存后,如果英文 Host 仍然输出旧的调试响应头,可以再次执行:

Bash
/root/bin/w3tc-flush-host-object-cache \
  en.shuijingwanwq.com

十五、本次问题的完整原因链

这次问题并不是单一原因造成的,而是两层问题叠加。

第一层:旧代码不兼容新的语言域名结构

原 WPCode 片段是为:

Plaintext
https://www.shuijingwanwq.com/en/

编写的。

英文站迁移到:

Plaintext
https://en.shuijingwanwq.com/

后,旧代码仍然强制拼接:

PHP
$home_url . $current_lang . '/'

于是把完整英文子域名 URL 再次追加到 /en/ 后面。

第二层:新版代码没有立即进入英文 Host

即使已经将代码升级为兼容多域名的 V3,英文 Host 仍然继续运行旧版本。

实际检测结果是:

Plaintext
www Host:已加载 V3
en Host:未加载 V3

在按英文 Host 调用 W3TC Object Cache 清理后,结果变为:

Plaintext
www Host:已加载 V3
en Host:已加载 V3
错误链接数量:0

因此,这次更准确的结论是:

日历错误链接最初由不兼容子域名模式的旧 WPCode 代码生成;修复代码保存后,又因为当前多 Host 环境中的 W3TC Object Cache 旧状态没有及时失效,导致英文站继续执行旧代码。


十六、总结

这次排查最终确认了三个关键点。

第一,WordPress 或 Polylang 输出的完整绝对 URL,不能再通过简单的字符串替换和 /en/ 拼接来处理。

在支持语言目录、子域名和独立域名的环境中,应当根据当前语言的真实首页重建 URL。

第二,在 Polylang 多域名、W3 Total Cache Object Cache 和 Redis 组合下,后台保存成功并不一定代表每一个 Host 都已经加载到相同的新对象。

至少在当前环境中,曾经出现:

Plaintext
www 已更新
en 仍然读取旧版本

第三,后台“清除所有缓存”无效时,不能无限重复点击清理按钮。

更有效的做法是逐层验证:

Plaintext
浏览器
CDN
源站 Page Cache
WordPress 实时输出
不同 Host 是否加载相同代码

目前已经得到一个可以重复使用的初步解决方案:

Bash
/root/bin/w3tc-flush-host-object-cache \
  en.shuijingwanwq.com

它还不能解释 W3TC 内部为什么在当前环境中出现不同 Host 的代码加载状态差异,但至少在再次遇到类似问题时,不需要重新进行整套排查,也不必直接清空整个 Redis 数据库。

对于当前网站而言,这已经是一个可控、可验证,并且比反复点击后台“清除所有缓存”更明确的处理方法。

ChatGPT Agent 能否替代 ChatGPT Plus 完成 WordPress 英文重译?

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

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理