While continuing to verify WordPress multilingual and multi-domain cache compatibility recently, I discovered a new issue:
The English translation had already been published, and the relationship between the Chinese and English articles had been established in Polylang, but the corresponding Chinese article detail page still did not display the language switcher, and the English hreflang was also missing from the page.
This investigation ultimately confirmed that the problem was not simply a failure to clear the W3 Total Cache Page Cache. Rather, the Object Cache corresponding to the Polylang translation relationship failed to synchronize invalidation across the three Hosts: www, en, and admin.
However, considering that this issue currently mainly causes a delayed appearance of the language switcher and hreflang, and that implementing truly reliable cross-Host precise cache purging would require further reliance on W3TC’s internal mechanisms, I did not continue adding production fix logic at this stage.
This article records the confirmed symptoms, root cause, and temporary trade-offs, making it easier to pick up right where I left off if optimization is needed in the future.
1. The Issue Occurs After the English Translation Is Published
The Chinese article tested this time was 21027, and the English translation was 21034.
The English article had been published normally, and querying via WP-CLI showed that the Polylang relationship was correct:
21027 → 21034
Therefore, one scenario could initially be ruled out: the English article was not missing the language switcher because the translation relationship had not been established.
My initial suspicion was the Page Cache for the Chinese article detail page.
Because Chinese articles are usually published first, at which point the English translation does not exist yet. If the detail page cache had already been generated, the HTML naturally would not contain the English language switcher. Later, when the English translation is published, if this Page Cache is not invalidated, it will continue to look like there is “no English version.”
However, an actual inspection quickly ruled out this assumption.
2. Without Page Cache, the Origin Still Outputs Incorrectly
When checking the W3TC Page Cache file corresponding to 21027, I first found that the cache file did not exist.
I then bypassed the CDN to directly request the local Origin. The page returned HTTP 200, but in the HTML:
- The URL for the English article
21034appeared 0 times; hreflangappeared 0 times.
This meant the problem was occurring before Page Cache generation.
In other words:
When WordPress dynamically generated the Chinese article page, it did not recognize the corresponding English translation.
Public requests also returned eo-cache-status: MISS at the time, with the same output result, so the old EdgeOne cache was not the source of this issue either.
The scope of the problem was thus narrowed down to WordPress, Polylang, and the Object Cache.
3. Normal Old Articles and Abnormal New Articles Formed a Clear Contrast
To confirm that the language switcher itself was not broken, I found an older article, 20612, that could display the language switcher normally; its corresponding English article was 20617.
In the Origin HTML of the normal article:
- The corresponding English article URL appeared 2 times;
hreflangappeared 3 times;- The page could output the English link for
20617normally.
Whereas the abnormal article 21027:
- The corresponding
21034URL was 0; hreflangwas 0.
I subsequently found that more than ten recent Chinese articles had the same situation.
This indicated that the entire Polylang output mechanism had not failed; it was more likely that recently established translation relationships were not being detected by a certain cache layer in time.
4. The Crucial Evidence: WP-CLI Is Normal, www HTTP Runtime Is Incorrect
When querying 21027 in WP-CLI, I could normally obtain:
21027 → 21034
However, WP-CLI and actual PHP-FPM HTTP requests do not necessarily use the exact same Object Cache runtime environment, so I also called pll_get_post() via a real www Origin HTTP request.
The runtime was confirmed to be using W3 Total Cache Redis Object Cache:
W3TC\ObjectCache_WpObjectCache
The normal old article still returned:
20612 → 20617
But the abnormal article returned:
21027 → NONE
This step essentially confirmed the problem:
The Polylang translation relationship in the database was already correct, but the persistent Object Cache on the
wwwHost was still storing the old result that “21027 has no English translation.”
5. Immediate Recovery After Precisely Clearing the Relationship Cache
Instead of flushing Redis or performing a global Object Cache flush, I then executed clean_object_term_cache() on the associated article within the real www HTTP Runtime.
Before execution:
pll_get_post( 21027, 'en' ) = 0
After execution:
pll_get_post( 21027, 'en' ) = 21034
To further narrow down the scope, I also performed a precise test using another set of abnormal articles, 21018 → 21022, deleting only the corresponding post_translations_relationships cache:
wp_cache_delete(
21018,
'post_translations_relationships'
);
wp_cache_delete(
21022,
'post_translations_relationships'
);
The result similarly went from:
before: 0
To recovered:
after: 21022
Thus, it was confirmed that what was truly stale was:
post_translations_relationships
That is, the Object Cache corresponding to the Polylang Post translation relationship.
6. Why It Ultimately Manifests as the Page Lacking the Language Switcher for an Extended Period
Object Cache is only the first layer.
When the www Runtime reads “no English translation” from the old post_translations_relationships, it generates an HTML without the English language switcher and hreflang.
If W3TC Page Cache subsequently caches this HTML, even if the Object Cache naturally expires later, users may still continue to see the incorrect page until the Page Cache itself expires or is purged.
This time, after clearing the relationship cache, I also precisely cleared the detail page Page Cache for 21027.
After regenerating the Origin page:
21034URL appeared 2 times;hreflangappeared 3 times;- The Chinese and English alternate links returned to normal.
Therefore, the complete chain was confirmed:
Old Polylang relationship Object Cache → pll_get_post() returns no translation → generates HTML lacking the language switcher → W3TC Page Cache caches this HTML again.
7. Why It Only Became Obvious After Migrating to Three Hosts
Previously, the English site was located at www.shuijingwanwq.com/en/.
Although the Chinese and English paths were different, they both ran under the www.shuijingwanwq.com Host.
Now it has changed to:
- Chinese:
www.shuijingwanwq.com - English:
en.shuijingwanwq.com - Backend:
admin.shuijingwanwq.com
English translations are usually created and saved from the admin Host, and W3 Total Cache Redis Object Cache causes specific cache items to be affected by the Host namespace.
As a result, the following can occur:
When the Chinese article does not yet have an English translation, www has already cached the “no translation” relationship; subsequently, admin creates the English translation and updates the database, but the original relationship cache in www is not synchronously invalidated.
This also explains why there were no obvious issues in the previous single-Host environment, and the problems only surfaced collectively after migrating to multiple domains.
8. Why a Cross-Host Fix Was Not Directly Implemented
After confirming the root cause, I had Codex further check whether the current W3TC 2.10.3 had a suitable Host-aware Object Cache API.
The result was that no public, stable interface was found that could specify from a admin request:
“Delete a certain post_translations_relationships key under the www Host.”
Experiments also proved that after executing wp_cache_flush_group( 'post_translations_relationships' ) on the admin Host, admin itself could read the new relationship, but www still read the old data.
Therefore, the previous handling approach for the posts group could not be simply copied.
Theoretically, it is possible to continue relying on W3TC’s internal Redis key construction rules to implement precise cross-Host deletion, but this would bind the MU Plugin to the private implementation of W3TC 2.10.3.
To solve a delayed language switcher issue, I believe this maintenance cost and risk are not worth it at present.
9. The Object Cache Strategy Was Not Modified for This Issue Either
The current actual W3TC configuration is:
Object Cache uses Redis, with a lifetime of 86400 seconds, which is 24 hours.
Page Cache uses file_generic, with a lifetime of 345600 seconds, which is 4 days.
This means that old translation relationships might be retained in Redis for a long time, and the incorrectly generated detail page HTML could continue to be stored by the Page Cache.
Several options were considered:
Shortening the Object Cache TTL would allow old relationships to naturally expire faster, but it would affect the entire Object Cache, not just the Polylang translation relationships.
Setting post_translations_relationships as non-persistent would completely avoid the cross-Host old relationships in Redis, but it would mean that these types of translation relationships would read from the database more frequently in the future.
Extending the Object Cache TTL to 2 or 4 days could reduce the natural expiration frequency, but it would also keep this kind of incorrectly invalidated old data around for longer.
Overall, none of these are necessary right now.
Therefore, I am keeping:
Object Cache TTL = 1 天
Page Cache TTL = 4 天
10. Why I Chose to Accept Natural Expiration for Now
This issue does exist, but its scope of impact must be considered alongside the cost of fixing it.
Currently, it is more important to ensure that after an article is published:
- The Chinese and English homepages can be updated in a timely manner;
- The Sitemap can reflect new content in a timely manner;
- The article body is correct;
- The page language is correct;
- The canonical is correct;
- Old article content is not displayed for an extended period due to caching.
In comparison, the language switcher and hreflang on the Chinese article detail page appearing a bit later, while not ideal, is currently still a low-priority issue.
Therefore, at this stage, I have chosen not to continue adding production logic.
I will not use the W3TC private Redis API, will not directly construct physical keys, will not execute Redis DEL, and will not change the persistence strategy for the entire relationship group.
I will let the existing cache expire naturally for now.
11. Key Lessons Learned from This Investigation
The most valuable part of this investigation was not whether an MU Plugin fix was ultimately written, but rather further confirming some boundaries when troubleshooting multi-Host Object Caches.
The database having been updated does not mean that all Hosts’ PHP Runtimes have seen the latest results.
WP-CLI returning correct results also cannot directly prove that the real HTTP Runtime is correct.
Within the same WordPress installation, the following can occur:
admin is correct, en is correct, but www still reads the old Object Cache.
Additionally, while fully flushing the Object Cache can easily make the problem temporarily disappear, it also deletes the incident scene and may produce a noticeable cold cache period.
A full Object Cache flush was executed that day, followed by a few CPU alerts. There is currently insufficient evidence to prove a direct causal relationship between the two, so the TTL was not modified because of this.
If similar issues are encountered in the future, it is better to preserve the scene first and start confirming from the specific Host, specific article, and specific group or key, rather than directly purging all caches.
12. Conclusion for the Current Stage
This time it has been confirmed that:
Polylang has established the Chinese-English article relationship in the database, but in a multi-domain environment, the post_translations_relationships in the W3 Total Cache Redis Object Cache may leave different states across different Hosts.
After the English translation is created from the independent backend domain, the previously cached “no English translation” relationship in the www Host may continue to exist.
This causes the Chinese article to not output the English language switcher and hreflang when dynamically generated, and the W3TC Page Cache may then continue to store this HTML.
Actual testing has proven that after precisely deleting post_translations_relationships in the corresponding www Runtime, pll_get_post() recovers immediately; after cleaning the article detail page Page Cache, the language switcher and hreflang can also recover.
Therefore, the root cause is clear.
It is just that no sufficiently simple and stable cross-Host precise deletion solution that does not rely on W3TC’s private internal implementation has been found yet.
Considering the limited actual impact of this issue, the current decision is to temporarily stop modifying the production environment, maintaining the existing configuration of 1 day for Object Cache and 4 days for Page Cache, allowing the language switcher to recover naturally.
If this issue becomes more important in the future, I can continue researching directly from the cross-Host precise invalidation of post_translations_relationships without needing to investigate from scratch again.
需要长期技术维护或远程问题排查?
我是拥有 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


发表回复