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

WordPress English-Site Calendar Links Turned into Double URLs: Troubleshooting WPCode and W3TC Object Cache After a Polylang Subdomain Migration

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

This article follows up on my earlier investigation into Polylang multi-domain behavior and W3 Total Cache object caching.

Previous troubleshooting record:

Horizontal Scrollbar on a WordPress English Site: Troubleshooting Stale CSS in W3TC Object Cache with Polylang Multi-Domain Mode

In the previous article, the new CSS had already been saved to the WordPress database, but the English domain continued to read a stale object. I eventually confirmed that, in this Polylang multi-domain setup, the www and en Hosts could end up with inconsistent code or object update states when W3TC Object Cache used Redis as its storage backend.

This time, the symptom was even more obvious: date links in the calendar on the English homepage were being assembled into malformed URLs containing two complete domain names.


1. Symptom: Two Complete URLs in a Calendar Link

The site’s current language structure is:

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

After opening the English homepage and hovering over a date in the calendar, I could see that the link had become:

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

The correct link should have been:

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

The malformed URL contained both:

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

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

In other words, a complete English-subdomain URL was treated as a relative path and appended to the old /en/ address.

Calendar links on the Chinese site were correct; only the English subdomain was affected.

Figure 1. A calendar date link on the English homepage assembled into a double URL
Figure 1. A calendar date link on the English homepage assembled into a double URL

2. Initial Suspicions: W3 Total Cache or Polylang

The site currently uses all of the following:

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

I had also recently migrated the English site from:

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

to:

Plaintext
https://en.shuijingwanwq.com/

When I first saw this link, the obvious possibilities were:

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

However, after inspecting the WPCode snippet I had previously written for the calendar, I quickly found the first direct cause.


3. The First Root Cause: The Old WPCode Snippet Still Built Links Around /en/

I had previously added a calendar-fix snippet to address inconsistent post dates across languages in Polylang.

It performs the following tasks:

  1. Query the dates with posts in the current language for the selected month;
  2. Add links to dates that should have posts;
  3. Remove incorrect links that belong only to other languages;
  4. Rebuild the previous-month and next-month navigation;
  5. Correct the language prefix in date archive links.

The old version constructed URLs for non-default languages as follows:

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;
};

This code was written when the English site still used the /en/ path.

At that time, the URL structure was:

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

The following concatenation logic therefore made sense in the old architecture:

PHP
$home_url . $current_lang . '/'

It generated:

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

After the migration to a dedicated subdomain, however, Polylang could already return:

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

The old code still continued to execute:

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

At this point:

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

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

Because the two URLs used different Hosts, str_replace() could not remove the primary domain.

As a result, $relative remained a complete absolute URL:

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

The code then continued concatenating the value:

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

The final result was:

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

This was the malformed link that actually appeared on the page:

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


4. Why the Chinese Site Was Not Affected

The old code contains the following condition:

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

Chinese is the current default language, so the function immediately returns the original URL for the Chinese site.

Only non-default languages such as English continue into the /en/ concatenation logic.

The resulting behavior was therefore:

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

This matched the observed behavior exactly.


5. The Fix: Stop Hard-Coding /en/ and Use Polylang’s Actual Language Homepage

The existing logic for querying posts by language, adding missing date links, removing incorrect links, and rebuilding month navigation was still useful.

What needed to be removed was:

PHP
$home_url . $current_lang . '/'

The new version uses:

PHP
pll_home_url( $current_lang )

to retrieve the homepage actually configured for the current language.

This supports all of the following configurations:

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

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

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

The core URL normalization function used in this revision is shown below:

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 );
}

Date links were changed to:

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

The previous-month link was changed to:

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

The next-month link was changed to:

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

After saving and enabling the new WPCode snippet, the English date links should theoretically have been fixed immediately.

In practice, it was not that simple.


6. The English Site Still Ran the Old Version After the New Code Was Saved

The new code had been saved and enabled successfully in WPCode.

I then ran the following action in the WordPress dashboard:

Plaintext
性能 → 清除所有缓存

To check whether the language selected in the Polylang dashboard affected cache purging, I switched the top toolbar between:

Plaintext
中文(中国)
English

I then ran “Purge All Caches” again.

Neither attempt had any effect.

The date links on the English homepage were still:

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

The result was unchanged in a private browsing window.

Even when I opened a URL with a query parameter:

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

the page continued to output the old links.

This ruled out a simple browser-cache explanation.


7. The Malformed Links Persisted When Accessing the Origin Directly

To rule out Cloudflare, I used --resolve to map the English domain directly to 127.0.0.1 on the origin server:

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"

I then checked for malformed links:

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

The command returned 12 malformed URLs, including:

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/

This showed that:

Plaintext
Cloudflare 不是本次错误链接的直接来源
源站返回的 HTML 本身就是错误的
Figure 2. Twelve double URLs still detected when accessing the origin directly
Figure 2. Twelve double URLs still detected when accessing the origin directly

8. Using a POST Request to Further Rule Out Origin Page Cache

To avoid the current GET page-cache path as much as possible, I also sent a POST request to the English origin:

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"

I counted the malformed links again:

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

The result was still:

Plaintext
12

At this point, I could reasonably conclude:

The English Host was most likely still running a previously cached version of the code rather than the V3 code I had just saved.


9. Confirming Different Code Loads Between the Two Hosts with a Temporary Response Header

To confirm whether the new code was actually being loaded, I temporarily added a response header to the WPCode snippet:

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

I then accessed the same origin directly using the www and en Hosts:

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

The responses were:

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

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

The result was unambiguous:

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

Both domains used:

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

However, the two Hosts were actually loading different versions of the code.

This closely resembled the behavior documented in the previous article: the database had been updated, but the English Host continued to read a stale object.

Figure 3. The www response included the V3 marker, while the en response did not
Figure 3. The www response included the V3 marker, while the en response did not

10. Bootstrapping WordPress Under the English Host and Flushing W3TC Object Cache

I did not use the following command directly:

Bash
redis-cli FLUSHDB

Flushing the entire Redis database would have had too broad an impact, and the current Redis instance might also contain other cached data.

Instead, I used the following approach:

  1. Simulate an en.shuijingwanwq.com request environment on the command line;
  2. Load WordPress;
  3. Retrieve W3TC’s CacheFlush component;
  4. Call objectcache_flush().

Command:

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);
}
'

Result:

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

NULL did not mean the operation had failed, because no exception was thrown during execution.

The only way to confirm that it had worked was to check the English Host again.

Figure 4. Flushing W3TC Object Cache under the English Host
Figure 4. Flushing W3TC Object Cache under the English Host

11. The English Host Loaded the New Code Immediately After the Flush

I checked www and en again:

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

The responses were:

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/"

Before the flush:

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

After the flush:

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

The calendar on the English homepage was now correct in the browser as well.

Figure 5. Calendar date links on the English homepage restored to the en subdomain
Figure 5. Calendar date links on the English homepage restored to the en subdomain

12. Saving the Flush Command as a Reusable Server Script

This was not the first time I had encountered:

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

Rather than copying a long PHP command each time, it made more sense to save it as a server script.

Create a directory:

Bash
mkdir -p /root/bin

Create the script:

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

Make it executable:

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

If a similar issue affects the English Host in the future, run:

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

If the primary domain is affected, run:

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

The purpose of this script is not to delete the cache for a single page. It is to:

Bootstrap WordPress under a specified Host and invoke the Object Cache flush method provided by the current W3 Total Cache environment.


13. This Script Should Not Be Run Routinely

It is important to clarify that this is not a command to run after every post is published.

It is better suited to cases where:

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

If an ordinary page has not updated, check the following layers first:

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

Only consider running this script after confirming that WordPress on the origin is loading inconsistent object states under different Hosts.

It is also inadvisable to run the following without first confirming the scope of impact:

Bash
redis-cli FLUSHALL

or:

Bash
redis-cli FLUSHDB

These methods flush a broader scope of data, making it harder to determine whether other data in the same Redis instance will be affected.


14. Remember to Remove the Temporary Debug Response Header

After resolving the issue, remove the temporary debug code from the WPCode snippet:

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

The debug response header was used only to confirm:

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

There is no reason to keep it after the issue has been confirmed.

After deleting and saving it, if the English Host still outputs the old debug response header, run the following again:

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

15. The Complete Chain of Causes

This incident was not caused by a single issue, but by two overlapping layers.

Layer 1: The Old Code Was Incompatible with the New Language-Domain Structure

The original WPCode snippet was written for:

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

英文站to:

Plaintext
https://en.shuijingwanwq.com/

the old code continued to force the following prefix:

PHP
$home_url . $current_lang . '/'

It therefore appended a complete English-subdomain URL to /en/ again.

Layer 2: The New Code Did Not Reach the English Host Immediately

Even after the code had been upgraded to a multi-domain-compatible V3, the English Host continued to run the old version.

The test results were:

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

After flushing W3TC Object Cache under the English Host, the result changed to:

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

A more precise conclusion is therefore:

The malformed calendar links were initially generated by old WPCode logic that was incompatible with the subdomain-based language setup. After the corrected code was saved, a stale W3TC Object Cache state in the current multi-Host environment was not invalidated promptly, causing the English site to continue running the old code.


16. Conclusion

This investigation ultimately established three key points.

First, a complete absolute URL returned by WordPress or Polylang should not be processed with simple string replacement and /en/ concatenation.

In environments that support language directories, subdomains, or separate domains, URLs should be rebuilt from the actual homepage configured for the current language.

Second, with Polylang multi-domain mode, W3 Total Cache Object Cache, and Redis, a successful save in the dashboard does not necessarily mean that every Host has loaded the same updated object.

In this environment, at least, I observed:

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

Third, when “Purge All Caches” in the dashboard has no effect, repeatedly clicking the same button is not a useful troubleshooting strategy.

A more effective approach is to verify each layer in sequence:

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

For now, I have a reusable preliminary solution:

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

It does not yet explain why W3TC produced different code-loading states across Hosts in this environment. It does, however, mean that the next occurrence will not require repeating the entire investigation or flushing the whole Redis database.

For this site, that is already a controlled and verifiable response—and a more explicit one than repeatedly clicking “Purge All Caches” in the dashboard.

系列导航

需要长期技术维护或远程问题排查?

我是拥有 15+ 年经验的 PHP / Go 后端工程师,长期关注已有系统维护、Bug 修复、性能优化、服务器排查、WordPress 网站维护和小功能迭代。

如果你的项目遇到以下情况,可以先从一次小问题排查开始合作:

  • ✅ PHP / Laravel / Yii2 老项目无人维护
  • ✅ Go / Gin 后端接口需要排查或优化
  • ✅ WordPress 网站访问慢、报错或插件冲突
  • ✅ Nginx / MySQL / Redis / Linux 服务器异常
  • ✅ CDN / Cloudflare / DNS / HTTPS 配置问题
  • ✅ 需要长期远程技术支持或兼职维护

更多介绍请查看:关于我 & 合作

微信:13980074657
邮箱:shuijingwanwq@gmail.com
Telegram:@shuijingwan
GitHub:https://github.com/shuijingwan

评论

发表回复

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

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