<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="/wp-content/plugins/rss-beauty/pink.xsl"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>WordPress Archives - obaby 𝐢‍𝐧⃝ void</title>
	<atom:link href="https://zhongxiaojie.cn/tag/wordpress/feed/" rel="self" type="application/rss+xml" />
	<link>https://zhongxiaojie.cn/tag/wordpress/</link>
	<description>程序媛 / 独立开发者 / 智商不稳定的女神经</description>
	<lastBuildDate>Sun, 08 Feb 2026 06:20:27 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://zhongxiaojie.cn/wp-content/uploads/2026/01/uugai.com-166111691272754-100x100.png</url>
	<title>WordPress Archives - obaby 𝐢‍𝐧⃝ void</title>
	<link>https://zhongxiaojie.cn/tag/wordpress/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>WP 访客信息插件 v16.06.99</title>
		<link>https://zhongxiaojie.cn/2026/02/400/</link>
					<comments>https://zhongxiaojie.cn/2026/02/400/#comments</comments>
		
		<dc:creator><![CDATA[obaby]]></dc:creator>
		<pubDate>Sun, 08 Feb 2026 06:20:27 +0000</pubDate>
				<category><![CDATA[程序媛]]></category>
		<category><![CDATA[czdb]]></category>
		<category><![CDATA[IP]]></category>
		<category><![CDATA[IP2Location]]></category>
		<category><![CDATA[ip2region]]></category>
		<category><![CDATA[IP归属地]]></category>
		<category><![CDATA[qqwry]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[纯真]]></category>
		<guid isPermaLink="false">https://zhongxiaojie.cn/?p=400</guid>

					<description><![CDATA[### v16.06.99 - 版本号更新至 16.06.99 - 新增 **纯真QQWRY** IP 查询方 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://zhongxiaojie.cn/wp-content/uploads/2026/02/330A2666tuya.png" data-lbwps-width="1920" data-lbwps-height="1280" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/02/330A2666tuya-665x443.png" data-lightbox="gal[400]"><img fetchpriority="high" decoding="async" class="alignnone size-full wp-image-410" src="https://zhongxiaojie.cn/wp-content/uploads/2026/02/330A2666tuya.png" alt="" width="1920" height="1280" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/02/330A2666tuya.png?v=1770531616 1920w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/330A2666tuya-300x200.png?v=1770531616 300w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/330A2666tuya-1024x683.png?v=1770531616 1024w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/330A2666tuya-150x100.png?v=1770531616 150w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/330A2666tuya-768x512.png?v=1770531616 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/330A2666tuya-1536x1024.png?v=1770531616 1536w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/330A2666tuya-665x443.png?v=1770531616 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/330A2666tuya-390x260.png?v=1770531616 390w" sizes="(max-width: 1920px) 100vw, 1920px" /></a></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">### v16.06.99

- 版本号更新至 16.06.99
- 新增 **纯真QQWRY** IP 查询方式（qqwry_api：qqwry.dat / ipv6wry.db），无需 Composer
- 新增 **显示协议类型** 选项：在国旗与国家文本后、IP 地址前显示 IPv4/IPv6 图标（img/ipv4.png、ipv6.png）
- QQWRY（IPv4/IpLocation、IPv6/ipdbv6）与 ip2region（xdb）返回完整字段，支持国旗与完整归属地显示
- 图标样式统一：IP 版本图标与 IP 地址图标使用与浏览器/系统图标相同的 vertical-align 等样式</pre>
<p>昨天发布了之后，<a href="https://www.hxy.cc" target="_blank" rel="noopener">hary</a>反馈说，<a href="https://zhongxiaojie.cn/2026/02/391/comment-page-1/#comment-645" target="_blank" rel="noopener">有更精准的纯真的数据库</a>。</p>
<p><span id="more-400"></span></p>
<p><a style="cursor: pointer !important; user-select: none !important;" href="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140208.png" data-lbwps-width="977" data-lbwps-height="1651" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140208.png" data-lightbox="gal[400]"><img decoding="async" class="alignnone size-full wp-image-401" src="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140208.png" alt="" width="977" height="1651" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140208.png?v=1770530667 977w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140208-178x300.png?v=1770530667 178w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140208-606x1024.png?v=1770530667 606w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140208-89x150.png?v=1770530667 89w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140208-768x1298.png?v=1770530667 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140208-909x1536.png?v=1770530667 909w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140208-665x1124.png?v=1770530667 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140208-154x260.png?v=1770530667 154w" sizes="(max-width: 977px) 100vw, 977px" /></a></p>
<p>当然啦，除此之外，其他的宝子的反馈我也看了，包括对于归属地准确性讨论的。</p>
<p><a href="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140456.png" data-lbwps-width="1030" data-lbwps-height="412" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140456-665x266.png" data-lightbox="gal[400]"><img decoding="async" class="alignnone size-full wp-image-402" src="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140456.png" alt="" width="1030" height="412" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140456.png?v=1770530756 1030w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140456-300x120.png?v=1770530756 300w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140456-1024x410.png?v=1770530756 1024w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140456-150x60.png?v=1770530756 150w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140456-768x307.png?v=1770530756 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140456-665x266.png?v=1770530756 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140456-650x260.png?v=1770530756 650w" sizes="(max-width: 1030px) 100vw, 1030px" /></a> <a href="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140501.png" data-lbwps-width="1040" data-lbwps-height="314" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140501-665x201.png" data-lightbox="gal[400]"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-403" src="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140501.png" alt="" width="1040" height="314" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140501.png?v=1770530757 1040w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140501-300x91.png?v=1770530757 300w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140501-1024x309.png?v=1770530757 1024w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140501-150x45.png?v=1770530757 150w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140501-768x232.png?v=1770530757 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140501-665x201.png?v=1770530757 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140501-861x260.png?v=1770530757 861w" sizes="auto, (max-width: 1040px) 100vw, 1040px" /></a> <a href="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140508.png" data-lbwps-width="1011" data-lbwps-height="560" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140508-665x368.png" data-lightbox="gal[400]"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-404" src="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140508.png" alt="" width="1011" height="560" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140508.png?v=1770530758 1011w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140508-300x166.png?v=1770530758 300w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140508-150x83.png?v=1770530758 150w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140508-768x425.png?v=1770530758 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140508-665x368.png?v=1770530758 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140508-469x260.png?v=1770530758 469w" sizes="auto, (max-width: 1011px) 100vw, 1011px" /></a></p>
<p><a href="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140522.png" data-lbwps-width="957" data-lbwps-height="599" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140522-665x416.png" data-lightbox="gal[400]"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-405" src="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140522.png" alt="" width="957" height="599" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140522.png?v=1770530759 957w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140522-300x188.png?v=1770530759 300w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140522-150x94.png?v=1770530759 150w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140522-768x481.png?v=1770530759 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140522-665x416.png?v=1770530759 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140522-415x260.png?v=1770530759 415w" sizes="auto, (max-width: 957px) 100vw, 957px" /></a></p>
<p>有的宝子的评论还没来的及回复，后面会回复的哈。在忙着测试hary提供的版本，不过整体测试下来，这个版本的qqwry版本的数据v6归属地精度，的确提高了一大截。</p>
<p><a href="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140948.png" data-lbwps-width="1808" data-lbwps-height="848" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140948-665x312.png" data-lightbox="gal[400]"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-407" src="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140948.png" alt="" width="1808" height="848" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140948.png?v=1770531098 1808w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140948-300x141.png?v=1770531098 300w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140948-1024x480.png?v=1770531098 1024w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140948-150x70.png?v=1770531098 150w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140948-768x360.png?v=1770531098 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140948-1536x720.png?v=1770531098 1536w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140948-665x312.png?v=1770531098 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-140948-554x260.png?v=1770531098 554w" sizes="auto, (max-width: 1808px) 100vw, 1808px" /></a></p>
<p>在集成了qqwry版本的数据库之后，目前已经有四种归属地查询方法，大家可以选择自己喜欢的。</p>
<p><a href="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141301.png" data-lbwps-width="1360" data-lbwps-height="370" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141301-665x181.png" data-lightbox="gal[400]"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-408" src="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141301.png" alt="" width="1360" height="370" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141301.png?v=1770531190 1360w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141301-300x82.png?v=1770531190 300w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141301-1024x279.png?v=1770531190 1024w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141301-150x41.png?v=1770531190 150w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141301-768x209.png?v=1770531190 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141301-665x181.png?v=1770531190 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141301-956x260.png?v=1770531190 956w" sizes="auto, (max-width: 1360px) 100vw, 1360px" /></a></p>
<p>并且添加了是否显示协议版本图标的功能，可以显示访客的网络类型。</p>
<p>图标位于插件的img目录下，如果想使用其他图标，直接替换目录下对应的ipv4.png 和ipv6的png即可。</p>
<p><a href="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141402.png" data-lbwps-width="1894" data-lbwps-height="262" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141402-665x92.png" data-lightbox="gal[400]"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-409" src="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141402.png" alt="" width="1894" height="262" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141402.png?v=1770531269 1894w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141402-300x41.png?v=1770531269 300w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141402-1024x142.png?v=1770531269 1024w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141402-150x21.png?v=1770531269 150w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141402-768x106.png?v=1770531269 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141402-1536x212.png?v=1770531269 1536w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141402-665x92.png?v=1770531269 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/02/Screenshot-2026-02-08-141402-1880x260.png?v=1770531269 1880w" sizes="auto, (max-width: 1894px) 100vw, 1894px" /></a></p>
<p>已经内置几组图标，如果要想只显示ipv6的图标，可以用一个空白图标来替换ipv4图标即可。</p>
<p>插件代码：</p>
<p><a href="https://cnb.cool/oba.by/wp-useragent" target="_blank" rel="noopener">https://cnb.cool/oba.by/wp-useragent</a></p>
<p>最新版(<span style="color: #ff0000;"><strong>插件包含多个ip数据库，尽量使用ftp或者scp上传，wp后台上传可能失败</strong></span>)：</p>
<p><a href="https://cnb.cool/oba.by/wp-useragent/-/releases/tag/16.06.99" target="_blank" rel="noopener">https://cnb.cool/oba.by/wp-useragent/-/releases/tag/16.06.99</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://zhongxiaojie.cn/2026/02/400/feed/</wfw:commentRss>
			<slash:comments>34</slash:comments>
		
		
			</item>
		<item>
		<title>浅谈WordPress静态化</title>
		<link>https://zhongxiaojie.cn/2026/01/270/</link>
					<comments>https://zhongxiaojie.cn/2026/01/270/#comments</comments>
		
		<dc:creator><![CDATA[obaby]]></dc:creator>
		<pubDate>Tue, 27 Jan 2026 12:59:14 +0000</pubDate>
				<category><![CDATA[程序媛]]></category>
		<category><![CDATA[btop]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[openresty]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[php-fpm]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[缓存]]></category>
		<guid isPermaLink="false">https://zhongxiaojie.cn/?p=270</guid>

					<description><![CDATA[以前在使用php 7.4的时候，并没有刻意尝试将页面静态化。然而，在升级到8.4之后由于一系列的问题，导致ph [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://zhongxiaojie.cn/wp-content/uploads/2026/01/30A4104tuya.jpg" data-lbwps-width="2560" data-lbwps-height="3840" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/01/30A4104tuya.jpg" data-lightbox="gal[270]" data-lbwps-exif="&lt;span class=&quot;pswp__caption__exif_camera&quot;&gt;Canon EOS R6m2&lt;/span&gt; &lt;span class=&quot;pswp__caption__exif_focal&quot;&gt;85mm&lt;/span&gt; &lt;span class=&quot;pswp__caption__exif_fstop&quot;&gt;𝑓/4&lt;/span&gt; &lt;span class=&quot;pswp__caption__exif_shutter&quot;&gt;1/160s&lt;/span&gt; &lt;span class=&quot;pswp__caption__exif_iso&quot;&gt;ISO320&lt;/span&gt; &lt;span class=&quot;pswp__caption__exif_datetime&quot;&gt;2025年11月10日&lt;/span&gt; "><img loading="lazy" decoding="async" class="alignnone size-full wp-image-276" src="https://zhongxiaojie.cn/wp-content/uploads/2026/01/30A4104tuya-scaled.jpg" alt="" width="1707" height="2560" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/01/30A4104tuya-scaled.jpg?v=1769518740 1707w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/30A4104tuya-200x300.jpg?v=1769518740 200w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/30A4104tuya-683x1024.jpg?v=1769518740 683w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/30A4104tuya-100x150.jpg?v=1769518740 100w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/30A4104tuya-768x1152.jpg?v=1769518740 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/30A4104tuya-1024x1536.jpg?v=1769518740 1024w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/30A4104tuya-1365x2048.jpg?v=1769518740 1365w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/30A4104tuya-665x998.jpg?v=1769518740 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/30A4104tuya-173x260.jpg?v=1769518740 173w" sizes="auto, (max-width: 1707px) 100vw, 1707px" /></a></p>
<p>以前在使用php 7.4的时候，并没有刻意尝试将页面静态化。然而，在升级到8.4之后由于一系列的问题，导致php响应异常缓慢，哪怕是开启了object cache。这就让人挺奇怪。不过在后期解决掉配置文件长度异常以及主题频繁的更新提示之后页面恢复正常了。</p>
<p><span id="more-270"></span></p>
<p>测速的时候，多数测速点基本都在1s以内。然而，与旧版一样，扛不住快速测试，快速测试的情况下，前面测速点的正常，后面的就会让php-fpm跑满cpu。于是，在<a href="https://zhongxiaojie.cn/2026/01/197/" target="_blank" rel="noopener">之前的一篇文章提到了解决wp健康问题(响应超时)</a>的问题，也是为了解决速度太慢的问题。本质上也是用的静态化的处理逻辑。</p>
<p>然而，这个东西存在问题，那就是我有多个域名，在数据更新之后怎么同时更新所有域名的缓存数据就成了问题。之前想的是通过wp插件，检测到变化删除缓存文件的方式。但是php-fpm由于权限问题导致删除失败。我又不想给php进程太高的权限，所以，后来尝试在nginx中进行操作，不过需要编译lua模块。</p>
<p>我不想编译，于是干脆换了 openresty:</p>
<blockquote><p>OpenResty<sup>®</sup> 是一个基于 <a title="Nginx" href="https://openresty.org/cn/nginx.html">Nginx</a> 与 Lua 的高性能 Web 平台，其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。</p>
<p>OpenResty<sup>®</sup> 通过汇聚各种设计精良的 <a title="Nginx" href="https://openresty.org/cn/nginx.html">Nginx</a> 模块（主要由 OpenResty 团队自主开发），从而将 <a title="Nginx" href="https://openresty.org/cn/nginx.html">Nginx</a> 有效地变成一个强大的通用 Web 应用平台。这样，Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 <a title="Nginx" href="https://openresty.org/cn/nginx.html">Nginx</a> 支持的各种 C 以及 Lua 模块，快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。</p></blockquote>
<p>这么一来就简单了，安装openresty替换掉原来的nginx:</p>
<p>1.更新缓存配置，所有的域名使用同一个缓存配置wordpress-php-with-cache-auto-purge-allinone.conf：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="nginx"># WordPress PHP 处理 + FastCGI 页面缓存 + 自动清除缓存（无需插件）
# 基于 wordpress-php-with-cache.conf，添加自动缓存清除功能
# 每个站点独立缓存（缓存键包含 $host）

location ~ [^/]\.php(/|$)
{
    try_files $uri =404;

    fastcgi_pass unix:/run/php/php8.4-fpm.sock;

    # ------ 页面缓存：跳过后台、登录、订阅等 ------
    set $skip_cache 0;
    if ($request_uri ~* "/wp-admin/|/wp-login\.php|/xmlrpc\.php|wp-.*\.php|/feed/|sitemap(_index)?.xml|/cart/|/checkout/|/my-account/") {
        set $skip_cache 1;
    }
    if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in|woocommerce_") {
        set $skip_cache 1;
    }
    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache $skip_cache;

    # ------ FastCGI 缓存（依赖 nginx.conf 中 fastcgi_cache_path ALLINONE）------
    # 注意：缓存键包含 $host，每个站点独立缓存
    fastcgi_cache ALLINONE;
    fastcgi_cache_key $scheme$request_method$host$request_uri;
    fastcgi_cache_valid 200 301 302 60m;
    fastcgi_cache_use_stale error timeout updating http_500 http_503;
    fastcgi_cache_lock on;
    fastcgi_cache_lock_timeout 5s;

    fastcgi_index index.php;
    include fastcgi.conf;

    # ------ 检测工具要求的客户端缓存响应头 ------
    add_header X-Cache-Status $upstream_cache_status;
    add_header X-Cache-Enabled "1";
    add_header Cache-Control "public, max-age=3600";


}
</pre>
<p>缓存清理配置cache-purge-lua-allinone.conf：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="nginx"># OpenResty Lua 缓存清除配置（ALLINONE 缓存版本）
# 在 server 块中添加此配置以启用缓存清除功能
# 适用于 ALLINONE 缓存（缓存键不包含 host，所有域名共享）

# 缓存清除 location（使用 Lua 脚本）
# 支持两种方式：
# 1. /purge/路径 - 清除指定路径的缓存
# 2. /purge-all - 清除全部缓存
# 3. PURGE 方法 - 使用 HTTP PURGE 方法清除当前请求的缓存
location ~ ^/purge(/.*)$ {
    # 只允许本地或内网访问（安全考虑）
    allow 127.0.0.1;
    allow ::1;
    allow 10.0.0.0/8;
    allow 172.16.0.0/12;
    allow 192.168.0.0/16;
    deny all;
    
    # 使用 Lua 脚本处理
    content_by_lua_block {
        local cache_purge = require "cache_purge_allinone"
        cache_purge.handle_purge_request()
    }
    
    access_log off;
}

# 清除全部缓存 location
location = /purge-all {
    # 只允许本地或内网访问（安全考虑）
    allow 127.0.0.1;
    allow ::1;
    allow 10.0.0.0/8;
    allow 172.16.0.0/12;
    allow 192.168.0.0/16;
    deny all;

    # 使用 Lua 脚本处理
    content_by_lua_block {
        local cache_purge = require "cache_purge_allinone"
        cache_purge.handle_purge_request()
    }

    access_log off;
}

# 获取缓存路径列表 location
location = /cache-paths {
    # 只允许本地或内网访问（安全考虑）
    allow 127.0.0.1;
    allow ::1;
    allow 10.0.0.0/8;
    allow 172.16.0.0/12;
    allow 192.168.0.0/16;
    deny all;

    # 使用 Lua 脚本处理
    content_by_lua_block {
        local cache_purge = require "cache_purge_allinone"
        cache_purge.handle_cache_paths_request()
    }

    access_log off;
}

</pre>
<p>2.lua脚本部分：auto_cache_purge_allinone.lua</p>
<pre class="EnlighterJSRAW" data-enlighter-language="lua">-- OpenResty 自动缓存清除脚本（ALLINONE 缓存）
-- 自动检测评论提交并清除缓存
-- 缓存规则：fastcgi_cache_key $scheme$request_method$host$request_uri（包含 host，每个站点独立缓存）
-- 缓存目录：/var/cache/nginx/allinone/

local _M = {}

-- 统一的缓存目录
local cache_path = '/var/cache/nginx/allinone'

-- 计算缓存文件路径
local function get_cache_file_path(cache_key_md5)
    local level1 = string.sub(cache_key_md5, -1)
    local level2 = string.sub(cache_key_md5, -3, -2)
    return cache_path .. '/' .. level1 .. '/' .. level2 .. '/' .. cache_key_md5
end

-- 计算缓存键的 MD5（包含 host）
-- 缓存键格式：$scheme$request_method$host$request_uri
local function get_cache_key_md5(scheme, method, host, uri)
    local cache_key_string = scheme .. method .. host .. uri
    return ngx.md5(cache_key_string)
end

-- 删除缓存文件
local function delete_cache_file(file_path)
    local command = 'rm -f "' .. file_path .. '" 2&gt;/dev/null'
    local ok = os.execute(command)
    return ok == 0
end

-- 清除指定 URL 的缓存
local function purge_url(scheme, host, uri)
    local method = 'GET'
    local cache_key_md5 = get_cache_key_md5(scheme, method, host, uri)
    local cache_file = get_cache_file_path(cache_key_md5)
    
    local deleted = delete_cache_file(cache_file)
    
    -- 也尝试删除匹配的文件（处理查询参数等情况）
    local level1 = string.sub(cache_key_md5, -1)
    local level2 = string.sub(cache_key_md5, -3, -2)
    local cache_dir = cache_path .. '/' .. level1 .. '/' .. level2
    local command = 'find "' .. cache_dir .. '" -name "' .. cache_key_md5 .. '*" -delete 2&gt;/dev/null'
    os.execute(command)
    
    return deleted
end

-- 从评论提交请求中提取文章 ID
local function extract_post_id_from_request()
    -- 尝试从请求体中获取 post_id
    ngx.req.read_body()
    local body = ngx.req.get_body_data()
    
    if body then
        -- 解析 POST 数据
        local post_id = string.match(body, ".*comment_post_ID=(%d+)")
        if post_id then
            return tonumber(post_id)
        end
    end
    
    -- 尝试从 referer 中提取
    local referer = ngx.var.http_referer
    if referer then
        -- 从 URL 中提取文章 ID（WordPress 的 URL 结构）
        local post_id = string.match(referer, ".*/%?p=(%d+)")
        if post_id then
            return tonumber(post_id)
        end
    end
    
    return nil
end

-- 从 referer 中提取文章路径
local function extract_post_path()
    -- 从 referer 获取（评论来源页面就是文章页面）
    local referer = ngx.var.http_referer
    if referer then
        local path = string.match(referer, ".*https?://[^/]+(.+)")
        if path then
            -- 移除查询参数和锚点
            path = string.match(path, "^([^?#]+)")
            -- 移除尾部斜杠（除了根路径）
            if path ~= "/" then
                path = string.match(path, "^(.+)/$") or path
            end
            if path and path ~= "" then
                return path
            end
        end
    end
    
    return nil
end

-- 实际的缓存清除操作（在异步定时器中执行）
local function do_purge_cache(scheme, host, post_path, request_uri)
    -- 如果获取到文章信息，清除文章缓存
    if post_path and post_path ~= "" and post_path ~= "/" then
        local deleted = purge_url(scheme, host, post_path)
        if deleted then
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 已清除文章缓存: ", post_path)
        end
        
        -- 也尝试带尾部斜杠的版本
        purge_url(scheme, host, post_path .. "/")
    else
        -- 如果获取不到文章信息，记录日志
        ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 无法获取文章信息，仅清除首页缓存")
    end
    
    -- 无论是否获取到文章信息，都清除首页缓存
    local home_deleted = purge_url(scheme, host, "/")
    if home_deleted then
        ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 已清除首页缓存")
    end
end

-- 自动检测评论相关操作并清除缓存
function _M.auto_purge_on_comment()
    ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] log 阶段开始执行")
    
    -- 检查是否已标记为跳过（例如在 header_filter 阶段已检测到需要忽略的请求）
    if ngx.ctx and ngx.ctx.skip_cache_purge then
        ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 跳过：已标记为 skip_cache_purge")
        return
    end
    
    -- 只处理 POST 请求
    if ngx.var.request_method ~= "POST" then
        ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 跳过：不是 POST 请求，method=", ngx.var.request_method)
        return
    end
    
    local request_uri = ngx.var.request_uri or ""
    ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] log 阶段，request_uri: ", request_uri)
    
    local is_comment_action = false
    local comment_id = nil
    local post_id = nil
    local body = nil
    
    -- 检测前端评论提交
    if string.find(request_uri, "wp%-comments%-post%.php") then
        -- 检查状态码（302 重定向表示评论提交成功）
        local status = ngx.status
        ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] wp-comments-post.php 请求，status: ", status)
        if status ~= 302 and status ~= 200 then
            return
        end
        is_comment_action = true
    end
    
    -- 检测后台 AJAX 评论操作（删除、批准、垃圾评论、回复评论等）
    if string.find(request_uri, "admin%-ajax%.php") then
        ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 检测到 admin-ajax.php 请求")
        
        -- 首先尝试从上下文获取请求体（可能在 header_filter 阶段已读取）
        if ngx.ctx and ngx.ctx.request_body then
            body = ngx.ctx.request_body
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 从上下文获取请求体，长度: ", string.len(body))
        else
            -- 安全地读取请求体（避免重复读取）
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 尝试读取请求体")
            local ok, err = pcall(function()
                ngx.req.read_body()
                body = ngx.req.get_body_data()
            end)
            
            if not ok then
                ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 读取请求体失败: ", err or "unknown error")
                return
            end
            
            if body then
                ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 成功读取请求体，长度: ", string.len(body))
            else
                ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 请求体为空")
            end
        end
        
        -- 检查 referer 和 body，如果不包含 /wp-admin/ 则跳过
        local referer = ngx.var.http_referer or ""
        ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] referer: ", referer)
        if not string.match(referer, ".*/wp%-admin/") then
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 跳过：referer 不包含 /wp-admin/, referer=", referer)
            return
        end
        
        if not body then
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 跳过：body 为空")
            return
        end
        
        -- 记录 body 内容（用于调试，只记录前 500 个字符）
        local body_preview = string.sub(body, 1, 500)
        ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] admin-ajax.php 请求，body 预览: ", body_preview)
        
        -- 检测请求动作，忽略 php_probe_realtime
        local action_match = string.match(body, ".*action=([^&amp;]+)")
        ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] action_match: ", action_match or "nil")
        if action_match == "php_probe_realtime" then
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 跳过：php_probe_realtime 请求")
            return
        end
        
        -- 检测评论相关操作
        local action_detected = false
        if string.match(body, ".*action=delete%-comment") then
            action_detected = true
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 检测到 action=delete-comment")
        elseif string.match(body, ".*action=trash%-comment") then
            action_detected = true
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 检测到 action=trash-comment")
        elseif string.match(body, ".*action=untrash%-comment") then
            action_detected = true
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 检测到 action=untrash-comment")
        elseif string.match(body, ".*action=spam%-comment") then
            action_detected = true
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 检测到 action=spam-comment")
        elseif string.match(body, ".*action=unspam%-comment") then
            action_detected = true
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 检测到 action=unspam-comment")
        elseif string.match(body, ".*action=approve%-comment") then
            action_detected = true
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 检测到 action=approve-comment")
        elseif string.match(body, ".*action=unapprove%-comment") then
            action_detected = true
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 检测到 action=unapprove-comment")
        elseif string.match(body, ".*action=replyto%-comment") then
            action_detected = true
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 检测到 action=replyto-comment")
        end
        
        if action_detected then
            is_comment_action = true
            
            -- 提取评论 ID 和文章 ID
            comment_id = string.match(body, ".*comment_ID=(%d+)") or string.match(body, ".*id=(%d+)")
            post_id = string.match(body, ".*comment_post_ID=(%d+)")
            
            -- 记录检测到的评论操作
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 检测到后台评论操作: action=", action_match or "unknown", ", comment_id=", comment_id or "nil", ", post_id=", post_id or "nil")
        else
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 未检测到评论相关操作")
        end
    end
    
    if not is_comment_action then
        ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 跳过：不是评论相关操作")
        return
    end
    
    local scheme = ngx.var.scheme or "https"
    local host = ngx.var.host or ngx.var.http_host or ""
    
    -- 提取文章路径
    local post_path = nil
    
    -- 如果是后台 AJAX 操作，尝试从请求参数或 referer 中提取文章信息
    if string.find(request_uri, "admin%-ajax%.php") and (comment_id or post_id) then
        -- 使用之前读取的 body（避免重复读取）
        if body then
            -- 尝试从 _url 参数中提取文章路径
            local url_param = string.match(body, ".*_url=([^&amp;]+)")
            if url_param then
                url_param = ngx.unescape_uri(url_param)
                -- 提取路径部分
                local extracted_path = string.match(url_param, ".*https?://[^/]+(.+)")
                if extracted_path then
                    extracted_path = string.match(extracted_path, "^([^?#]+)")
                    -- 如果不是后台页面，使用这个路径
                    if extracted_path and not string.match(extracted_path, ".*wp%-admin") then
                        post_path = extracted_path
                    end
                end
            end
            
            -- 如果还没有找到路径，尝试从其他参数中提取
            if not post_path or post_path == "" then
                -- 尝试从 referer 中提取（如果 referer 是文章页面）
                local referer = ngx.var.http_referer
                if referer and not string.match(referer, ".*wp%-admin") then
                    post_path = string.match(referer, ".*https?://[^/]+(.+)")
                    if post_path then
                        post_path = string.match(post_path, "^([^?#]+)")
                    end
                end
            end
        end
    else
        -- 前端评论提交，从 referer 提取文章路径
        post_path = extract_post_path()
    end
    
    -- 使用异步定时器执行缓存清除操作
    -- ngx.timer.at(0, handler) 会在当前请求处理完成后立即在后台执行
    local ok, err = ngx.timer.at(0, function(premature, scheme_arg, host_arg, post_path_arg, request_uri_arg)
        if premature then
            -- 定时器被提前取消（不应该发生，因为 delay=0）
            ngx.log(ngx.WARN, "[Auto Cache Purge ALLINONE] 定时器被提前取消")
            return
        end
        
        -- 在异步上下文中执行实际的缓存清除
        local purge_ok, purge_err = pcall(do_purge_cache, scheme_arg, host_arg, post_path_arg, request_uri_arg)
        if not purge_ok then
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 异步清除缓存错误: ", purge_err)
        end
    end, scheme, host, post_path, request_uri)
    
    if not ok then
        -- 如果创建定时器失败（例如定时器池已满），回退到同步执行
        ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] 无法创建异步定时器，使用同步执行: ", err)
        do_purge_cache(scheme, host, post_path, request_uri)
    end
end

-- 在 rewrite 阶段执行（可以读取请求体）
-- 提前读取请求体并保存到上下文，供 log 阶段使用
function _M.rewrite()
    -- 只处理 POST 请求
    if ngx.var.request_method ~= "POST" then
        return
    end
    
    local request_uri = ngx.var.request_uri or ""
    
    -- 只处理 admin-ajax.php 和 wp-comments-post.php 请求
    if string.find(request_uri, "admin%-ajax%.php") or string.find(request_uri, "wp%-comments%-post%.php") then
        -- 检查 referer（对于 admin-ajax.php）
        if string.find(request_uri, "admin%-ajax%.php") then
            local referer = ngx.var.http_referer or ""
            if not string.find(referer, "/wp%-admin/") then
                ngx.ctx.skip_cache_purge = true
                return
            end
        end
        
        -- 在 rewrite 阶段读取请求体（这个阶段允许读取）
        local ok, err = pcall(function()
            ngx.req.read_body()
            local body = ngx.req.get_body_data()
            
            if body then
                -- 对于 admin-ajax.php，检查是否为需要忽略的请求
                if string.find(request_uri, "admin%-ajax%.php") then
                    local action_match = string.match(body, ".*action=([^&amp;]+)")
                    if action_match == "php_probe_realtime" then
                        ngx.ctx.skip_cache_purge = true
                        return
                    end
                end
                
                -- 保存到上下文，供 log 阶段使用
                ngx.ctx.request_body = body
                ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] rewrite 阶段已保存请求体，长度: ", string.len(body))
            else
                ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] rewrite 阶段：请求体为空")
            end
        end)
        
        if not ok then
            ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] rewrite 阶段读取请求体出错: ", err or "unknown error")
        end
    end
end

-- 在响应头过滤阶段执行（可以访问响应头）
-- 注意：header_filter 阶段无法读取请求体，只能做简单的标记
function _M.header_filter()
    -- header_filter 阶段不再需要做任何处理
    -- 所有检查都在 rewrite 阶段完成
end

-- 在日志阶段执行（确保在响应完成后）
function _M.log()
    -- 使用 pcall 包装，避免错误导致请求失败
    local ok, err = pcall(function()
        _M.auto_purge_on_comment()
    end)
    
    if not ok then
        ngx.log(ngx.ERR, "[Auto Cache Purge ALLINONE] log 阶段执行错误: ", err)
    end
end

return _M

</pre>
<p>cache_purge_allinone.lua：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="lua">-- OpenResty Lua 缓存清除脚本（ALLINONE 缓存版本，支持独立缓存）
-- 支持独立缓存清除（缓存键包含 host，每个站点独立缓存）
-- 缓存键格式：$scheme$request_method$host$request_uri

local _M = {}

-- 统一的缓存目录（ALLINONE 缓存）
local cache_path = "/var/cache/nginx/allinone"

-- 计算缓存文件路径（基于 Nginx levels=1:2）
local function get_cache_file_path(cache_key_md5)
    local level1 = string.sub(cache_key_md5, -1)  -- 最后1位
    local level2 = string.sub(cache_key_md5, -3, -2)  -- 倒数第3-2位
    return cache_path .. "/" .. level1 .. "/" .. level2 .. "/" .. cache_key_md5
end

-- 计算缓存键的 MD5（包含 host，每个站点独立缓存）
-- 缓存键格式：$scheme$request_method$host$request_uri
local function get_cache_key_md5(scheme, method, host, uri)
    local cache_key_string = scheme .. method .. host .. uri
    return ngx.md5(cache_key_string)
end

-- 删除缓存文件
local function delete_cache_file(file_path)
    -- 使用 shell 命令删除（更可靠）
    local command = "rm -f \"" .. file_path .. "\" 2&gt;/dev/null"
    local ok = os.execute(command)
    if ok == 0 then
        return true
    else
        ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 删除缓存文件失败: ", file_path)
        return false
    end
end

-- 清除指定 URL 的缓存（包含 host，每个站点独立缓存）
function _M.purge_url(scheme, host, uri)
    -- 计算缓存键（包含 host）：$scheme$request_method$host$request_uri
    local method = "GET"
    local cache_key_md5 = get_cache_key_md5(scheme, method, host, uri)
    
    -- 获取缓存文件路径
    local cache_file = get_cache_file_path(cache_key_md5)
    
    -- 删除缓存文件
    local deleted = delete_cache_file(cache_file)
    
    -- 也尝试删除匹配的文件（处理查询参数等情况）
    local level1 = string.sub(cache_key_md5, -1)
    local level2 = string.sub(cache_key_md5, -3, -2)
    local cache_dir = cache_path .. "/" .. level1 .. "/" .. level2
    
    -- 使用 shell 命令删除匹配的文件
    local command = "find \"" .. cache_dir .. "\" -name \"" .. cache_key_md5 .. "*\" -delete 2&gt;/dev/null"
    local result = os.execute(command)
    if result == 0 then
        deleted = true
    end
    
    -- 也尝试带尾部斜杠的版本（如果原路径没有）
    if uri ~= "/" and not string.match(uri, "/$") then
        local uri_with_slash = uri .. "/"
        local cache_key_md5_slash = get_cache_key_md5(scheme, method, host, uri_with_slash)
        local cache_file_slash = get_cache_file_path(cache_key_md5_slash)
        delete_cache_file(cache_file_slash)
        
        local level1_slash = string.sub(cache_key_md5_slash, -1)
        local level2_slash = string.sub(cache_key_md5_slash, -3, -2)
        local cache_dir_slash = cache_path .. "/" .. level1_slash .. "/" .. level2_slash
        local command_slash = "find \"" .. cache_dir_slash .. "\" -name \"" .. cache_key_md5_slash .. "*\" -delete 2&gt;/dev/null"
        os.execute(command_slash)
    end
    
    -- 也尝试不带尾部斜杠的版本（如果原路径有）
    if uri ~= "/" and string.match(uri, "/$") then
        local uri_without_slash = string.match(uri, "^(.+)/$") or uri
        local cache_key_md5_no_slash = get_cache_key_md5(scheme, method, host, uri_without_slash)
        local cache_file_no_slash = get_cache_file_path(cache_key_md5_no_slash)
        delete_cache_file(cache_file_no_slash)
        
        local level1_no_slash = string.sub(cache_key_md5_no_slash, -1)
        local level2_no_slash = string.sub(cache_key_md5_no_slash, -3, -2)
        local cache_dir_no_slash = cache_path .. "/" .. level1_no_slash .. "/" .. level2_no_slash
        local command_no_slash = "find \"" .. cache_dir_no_slash .. "\" -name \"" .. cache_key_md5_no_slash .. "*\" -delete 2&gt;/dev/null"
        os.execute(command_no_slash)
    end
    
    return deleted
end

-- 清除全部缓存
function _M.purge_all()
    ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 开始清除全部缓存，目录: " .. cache_path)

    -- 测试：先检查当前用户和权限
    local whoami_cmd = "whoami 2&gt;&amp;1"
    local whoami_handle = io.popen(whoami_cmd)
    if whoami_handle then
        local user = whoami_handle:read("*a")
        whoami_handle:close()
        ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 当前用户: " .. (user or "unknown"))
    end

    -- 测试：检查缓存目录权限
    local ls_cmd = "ls -la '" .. cache_path .. "' 2&gt;&amp;1"
    local ls_handle = io.popen(ls_cmd)
    if ls_handle then
        local ls_output = ls_handle:read("*a")
        ls_handle:close()
        ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 目录权限: " .. ls_output)
    end

    -- 方法1: 直接使用 rm 命令
    local cmd = "rm -rf '" .. cache_path .. "'/* 2&gt;&amp;1"
    ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 执行命令: " .. cmd)

    local handle = io.popen(cmd)
    if handle then
        local output = handle:read("*a")
        local success, exit_reason, exit_code = handle:close()

        ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 命令输出: " .. (output or "无输出"))
        ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 命令结果: success=" .. tostring(success) .. ", exit_code=" .. tostring(exit_code))

        if exit_code == 0 then
            ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 成功清除全部缓存")
            return true, 1
        end
    else
        ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 无法执行命令: io.popen 失败")
    end

    -- 方法2: 如果上面的失败了，尝试使用 os.execute
    ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 尝试 os.execute 方法")
    local result = os.execute(cmd)
    ngx.log(ngx.ERR, "[Cache Purge ALLINONE] os.execute 结果: " .. tostring(result))

    if result == 0 then
        ngx.log(ngx.ERR, "[Cache Purge ALLINONE] os.execute 成功清除全部缓存")
        return true, 1
    end

    ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 所有删除方法都失败了")
    return false, 0
end

-- 处理缓存路径请求
function _M.handle_cache_paths_request()
    local success, result = _M.get_cache_paths()

    if success then
        ngx.status = 200
        ngx.header["Content-Type"] = "application/json"

        -- 限制返回数量
        local limit = 100
        local limited_paths = {}
        for i = 1, math.min(limit, #result) do
            table.insert(limited_paths, result[i])
        end

        -- 构建简单的 JSON 响应
        local json_paths = {}
        for _, path in ipairs(limited_paths) do
            table.insert(json_paths, string.format('{"path":"%s","size":%d,"mtime":%d,"md5":"%s"}',
                path.path,
                path.size,
                path.mtime,
                path.md5
            ))
        end

        local response = string.format('{"status":"success","paths":[%s],"total_count":%d,"has_more":%s,"limit":%d}',
            table.concat(json_paths, ','),
            #result,
            #result &gt; limit and "true" or "false",
            limit
        )

        ngx.say(response)
    else
        ngx.status = 500
        ngx.header["Content-Type"] = "application/json"
        local error_msg = type(result) == "string" and result or "获取缓存路径失败"
        ngx.say(string.format('{"status":"error","message":"%s"}', error_msg))
        ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 获取缓存路径失败: " .. error_msg)
    end

    ngx.exit(ngx.status)
end

-- 处理 PURGE 请求
function _M.handle_purge_request()
    local request_uri = ngx.var.request_uri or ""

    -- 检查是否是清除全部缓存的请求
    if string.match(request_uri, "^/purge%-all") then
        local success, file_count = _M.purge_all()

        if success then
            ngx.status = 200
            ngx.header["Content-Type"] = "application/json"
            ngx.say("{\"status\":\"success\",\"message\":\"已清除全部缓存\",\"deleted_count\":" .. file_count .. "}")
        else
            ngx.status = 500
            ngx.header["Content-Type"] = "application/json"
            ngx.say("{\"status\":\"error\",\"message\":\"清除全部缓存失败，请检查 Nginx 错误日志获取详细信息\"}")
        end

        ngx.exit(ngx.status)
        return
    end

    -- 从请求 URI 中提取路径（/purge/xxx -&gt; xxx）
    local uri = string.match(request_uri, "^/purge(/.*)$")

    if not uri or uri == "" then
        uri = "/"
    end

    -- 移除查询参数
    uri = string.match(uri, "^([^?]+)") or uri

    -- 强制使用 https（因为实际缓存都是 https 的）
    local scheme = "https"

    -- 获取 host（从 Host 头或 server_name 获取）
    local host = ngx.var.http_host or ngx.var.host or ngx.var.server_name or ""

    -- 如果 host 为空，记录错误
    if host == "" then
        ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 无法获取 host，清除失败")
        ngx.status = 400
        ngx.header["Content-Type"] = "application/json"
        ngx.say("{\"status\":\"error\",\"message\":\"无法获取 host 信息\"}")
        ngx.exit(ngx.status)
    end

    -- 清除缓存（包含 host，每个站点独立缓存）
    local success = _M.purge_url(scheme, host, uri)

    if success then
        ngx.status = 200
        ngx.header["Content-Type"] = "application/json"
        ngx.say("{\"status\":\"success\",\"message\":\"已清除缓存: " .. scheme .. "://" .. host .. uri .. " (独立缓存)\"}")
        ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 成功清除缓存: " .. scheme .. "://" .. host .. uri)
    else
        ngx.status = 404
        ngx.header["Content-Type"] = "application/json"
        ngx.say("{\"status\":\"error\",\"message\":\"清除失败: " .. scheme .. "://" .. host .. uri .. " (可能缓存不存在)\"}")
        ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 清除缓存失败: " .. scheme .. "://" .. host .. uri)
    end

    ngx.exit(ngx.status)
end

-- 获取缓存路径列表
function _M.get_cache_paths()
    ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 开始获取缓存路径列表，目录: " .. cache_path)

    local paths = {}
    local total_count = 0

    -- 直接尝试使用 find 命令，如果目录不存在，find 会返回错误
    -- 使用 -maxdepth 限制深度，提高性能
    local cmd = "find '" .. cache_path .. "' -maxdepth 3 -type f 2&gt;&amp;1 | head -100"
    ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 执行命令: " .. cmd)

    local handle = io.popen(cmd)
    if not handle then
        ngx.log(ngx.ERR, "[Cache Purge ALLINONE] io.popen 执行失败")
        return false, "无法执行 find 命令"
    end

    local output = handle:read("*a")
    local success, exit_reason, exit_code = handle:close()

    ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 命令输出长度: " .. (output and string.len(output) or 0))
    ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 命令结果: success=" .. tostring(success) .. ", exit_code=" .. tostring(exit_code))

    -- 检查输出中是否包含错误信息
    if output and string.find(output, "No such file or directory") then
        ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 缓存目录不存在")
        return false, "缓存目录不存在: " .. cache_path
    end

    if not output or output == "" then
        ngx.log(ngx.ERR, "[Cache Purge ALLINONE] find 命令无输出，可能是目录为空或不存在")
        -- 返回空列表而不是错误
        return true, {}
    end

    -- 解析输出
    for line in output:gmatch("[^\r\n]+") do
        if line ~= "" and not string.find(line, "^find:") then -- 跳过错误行
            local full_path = line:gsub("^%s+", ""):gsub("%s+$", "") -- 去除首尾空格
            if full_path ~= "" and string.find(full_path, cache_path) then
                -- 提取相对路径和文件名
                local relative_path = full_path:gsub("^" .. cache_path:gsub("%-", "%%-"):gsub("%.", "%%.") .. "/", "")
                local md5 = relative_path:match("([^/]+)$") or relative_path

                -- 尝试获取文件大小和修改时间
                local stat_cmd = "stat -c '%s %Y' '" .. full_path .. "' 2&gt;/dev/null"
                local stat_handle = io.popen(stat_cmd)
                local size = 0
                local mtime = 0
                if stat_handle then
                    local stat_output = stat_handle:read("*a")
                    stat_handle:close()
                    if stat_output then
                        local stat_size, stat_mtime = stat_output:match("(%d+)%s+(%d+)")
                        if stat_size then size = tonumber(stat_size) or 0 end
                        if stat_mtime then mtime = tonumber(stat_mtime) or 0 end
                    end
                end

                table.insert(paths, {
                    path = full_path,
                    size = size,
                    mtime = mtime,
                    md5 = md5
                })

                total_count = total_count + 1

                -- 限制返回数量
                if total_count &gt;= 100 then
                    break
                end
            end
        end
    end

    -- 按修改时间倒序排序
    table.sort(paths, function(a, b) return a.mtime &gt; b.mtime end)

    ngx.log(ngx.ERR, "[Cache Purge ALLINONE] 成功获取 " .. #paths .. " 个缓存文件路径")
    return true, paths
end

return _M
</pre>
<p>3.openresty nginx.conf 配置添加下面的内容 在http中添加：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="nginx">http
    {
        # Lua 模块路径配置（必须）
        lua_package_path "/usr/local/openresty/nginx/conf/lua/?.lua;;";


    # ALLINONE
    fastcgi_cache_path /var/cache/nginx/allinone
    	levels=1:2
    	keys_zone=ALLINONE:64m
    	max_size=512m
    	inactive=60m
    	use_temp_path=off;
</pre>
<p>4.修改vhost配置文件添加conf引入zhongxiaojie.com.conf：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="nginx">include cache-purge-lua-allinone.conf;
include wordpress-php-with-cache-auto-purge-allinone.conf;</pre>
<p>5.wp插件nginx-cache-purge-multi-domain.php 如果只有一个域名，就保留一个就可以了：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="php">&lt;?php
/**
 * Plugin Name: Nginx FastCGI Cache Purge Multi-Domain
 * Description: 当有新评论提交或文章更新时，自动清除多个域名的 Nginx FastCGI 缓存（每个站点独立缓存）
 * Version: 3.1
 * Author: obaby
 */

// 防止直接访问
if (!defined("ABSPATH")) {
    exit;
}

class Nginx_Cache_Purge_Multi_Domain {
    
    // 统一的缓存目录（ALLINONE 缓存，但每个站点独立缓存）
    private $cache_path = "/var/cache/nginx/allinone";
    
    // 需要清除缓存的所有域名列表（可通过 WordPress 选项覆盖）
    private $default_domains = array(
        "zhongxiaojie.com",
        "oba.by",
    );
    
    // 选项名称
    private $option_name = "nginx_cache_purge_multi_domain";
    
    // 是否启用调试日志
    private $debug = true;
    
    // 异步清除队列
    private $purge_queue = array();
    
    // 缓存键计算缓存（避免重复计算）
    private $cache_key_cache = array();
    
    // 文件系统访问检查缓存
    private $fs_access_cache = null;
    
    public function __construct() {
        // 注册 hooks
        add_action("comment_post", array($this, "purge_cache_on_comment"), 10, 2);
        add_action("wp_set_comment_status", array($this, "purge_cache_on_comment_status"), 10, 2);
        add_action("save_post", array($this, "purge_cache_on_post_save"), 10, 1);
        
        // 异步处理队列（在请求结束后执行）
        add_action("shutdown", array($this, "process_purge_queue"), 999);
        
        // 添加管理菜单
        add_action("admin_menu", array($this, "add_admin_menu"));
        
        // 处理 AJAX 请求
        add_action("wp_ajax_nginx_cache_purge_all", array($this, "ajax_purge_all_cache"));
        add_action("wp_ajax_nginx_cache_get_info", array($this, "ajax_get_cache_info"));
        add_action("wp_ajax_nginx_cache_get_paths", array($this, "ajax_get_cache_paths"));
        
        if ($this-&gt;debug) {
            $this-&gt;log("插件初始化完成");
        }
    }
    
    /**
     * 获取配置的域名列表
     */
    private function get_domains() {
        $options = get_option($this-&gt;option_name, array());
        if (isset($options["domains"]) &amp;&amp; is_array($options["domains"]) &amp;&amp; !empty($options["domains"])) {
            return $options["domains"];
        }
        return $this-&gt;default_domains;
    }
    
    /**
     * 添加到清除队列（异步处理）
     */
    private function add_to_purge_queue($scheme, $host, $path) {
        $key = $scheme . "|" . $host . "|" . $path;
        if (!isset($this-&gt;purge_queue[$key])) {
            $this-&gt;purge_queue[$key] = array(
                "scheme" =&gt; $scheme,
                "host" =&gt; $host,
                "path" =&gt; $path,
            );
        }
    }
    
    /**
     * 批量添加到清除队列
     */
    private function add_paths_to_queue($paths, $scheme = "https") {
        $domains = $this-&gt;get_domains();
        foreach ($domains as $domain) {
            foreach ($paths as $path) {
                $this-&gt;add_to_purge_queue($scheme, $domain, $path);
            }
        }
    }
    
    /**
     * 处理清除队列（异步执行）
     */
    public function process_purge_queue() {
        if (empty($this-&gt;purge_queue)) {
            return;
        }
        
        // 如果支持 fastcgi_finish_request，立即结束响应，在后台处理
        if (function_exists("fastcgi_finish_request")) {
            fastcgi_finish_request();
        }
        
        if ($this-&gt;debug) {
            $this-&gt;log(sprintf("开始异步处理清除队列，共 %d 个任务", count($this-&gt;purge_queue)));
        }
        
        $success_count = 0;
        $fail_count = 0;
        
        foreach ($this-&gt;purge_queue as $item) {
            $result = $this-&gt;purge_url_for_domain($item["scheme"], $item["host"], $item["path"]);
            if ($result) {
                $success_count++;
            } else {
                $fail_count++;
            }
        }
        
        if ($this-&gt;debug) {
            $this-&gt;log(sprintf("清除队列处理完成：成功 %d，失败 %d", $success_count, $fail_count));
        }
        
        // 清空队列
        $this-&gt;purge_queue = array();
    }
    
    /**
     * 评论提交后清除缓存
     */
    public function purge_cache_on_comment($comment_id, $comment_approved) {
        if ($this-&gt;debug) {
            $this-&gt;log(sprintf("Hook 触发: comment_id=%d, approved=%s", $comment_id, $comment_approved));
        }
        
        // 只处理已批准的评论
        if ($comment_approved != 1) {
            return;
        }
        
        $comment = get_comment($comment_id);
        if (!$comment) {
            return;
        }
        
        $post_id = $comment-&gt;comment_post_ID;
        $post = get_post($post_id);
        if (!$post) {
            return;
        }
        
        // 获取需要清除的路径
        $paths = $this-&gt;get_post_purge_paths($post_id);
        
        // 添加到异步队列
        $this-&gt;add_paths_to_queue($paths);
        
        if ($this-&gt;debug) {
            $this-&gt;log(sprintf("评论提交：已添加到清除队列，post_id=%d，路径数=%d", $post_id, count($paths)));
        }
    }
    
    /**
     * 评论状态变更时清除缓存
     */
    public function purge_cache_on_comment_status($comment_id, $status) {
        if (!in_array($status, array("approve", "spam", "trash"))) {
            return;
        }
        
        $comment = get_comment($comment_id);
        if (!$comment) {
            return;
        }
        
        $post_id = $comment-&gt;comment_post_ID;
        $paths = $this-&gt;get_post_purge_paths($post_id);
        $this-&gt;add_paths_to_queue($paths);
    }
    
    /**
     * 文章保存时清除缓存
     */
    public function purge_cache_on_post_save($post_id) {
        // 跳过自动保存和修订
        if (defined("DOING_AUTOSAVE") &amp;&amp; DOING_AUTOSAVE) {
            return;
        }
        
        if (wp_is_post_revision($post_id)) {
            return;
        }
        
        $post = get_post($post_id);
        if (!$post || $post-&gt;post_status != "publish") {
            return;
        }
        
        $paths = $this-&gt;get_post_purge_paths($post_id);
        $this-&gt;add_paths_to_queue($paths);
        
        if ($this-&gt;debug) {
            $this-&gt;log(sprintf("文章保存：已添加到清除队列，post_id=%d，路径数=%d", $post_id, count($paths)));
        }
    }
    
    /**
     * 获取文章相关的清除路径列表
     */
    private function get_post_purge_paths($post_id) {
        $paths = array();
        
        // 文章页面
        $post_url = get_permalink($post_id);
        if ($post_url) {
            $parsed = parse_url($post_url);
            if (isset($parsed["path"])) {
                $paths[] = $parsed["path"];
            }
        }
        
        // 首页
        $paths[] = "/";
        
        // 分类页
        $categories = get_the_category($post_id);
        foreach ($categories as $category) {
            $cat_url = get_category_link($category-&gt;term_id);
            if ($cat_url) {
                $parsed = parse_url($cat_url);
                if (isset($parsed["path"])) {
                    $paths[] = $parsed["path"];
                }
            }
        }
        
        return array_unique($paths);
    }
    
    /**
     * 清除指定域名和路径的缓存
     */
    private function purge_url_for_domain($scheme, $host, $path) {
        if (empty($host) || empty($path)) {
            return false;
        }
        
        // 方法1：尝试 HTTP PURGE（优先，更快）
        if ($this-&gt;purge_via_http($scheme, $host, $path)) {
            return true;
        }
        
        // 方法2：直接删除缓存文件
        return $this-&gt;purge_via_file_delete($scheme, $host, $path);
    }
    
    /**
     * 通过 HTTP 请求清除缓存
     */
    private function purge_via_http($scheme, $host, $path) {
        $purge_url = "http://127.0.0.1/purge" . $path; // 使用本地回环，避免外部网络开销
        
        $args = array(
            "method" =&gt; "GET",
            "timeout" =&gt; 1, // 减少超时时间，快速失败
            "headers" =&gt; array(
                "Host" =&gt; $host,
            ),
            "sslverify" =&gt; false,
            "blocking" =&gt; true, // 必须阻塞，否则无法判断结果
        );
        
        $response = wp_remote_request($purge_url, $args);
        
        if (is_wp_error($response)) {
            if ($this-&gt;debug) {
                $this-&gt;log(sprintf("HTTP PURGE 失败: %s - %s", $purge_url, $response-&gt;get_error_message()));
            }
            return false;
        }
        
        $code = wp_remote_retrieve_response_code($response);
        // 200 或 404 都算成功
        if ($code == 200 || $code == 404) {
            if ($this-&gt;debug) {
                $this-&gt;log(sprintf("HTTP PURGE 成功: %s (code=%d)", $purge_url, $code));
            }
            return true;
        }
        
        return false;
    }
    
    /**
     * 通过删除缓存文件清除缓存
     */
    private function purge_via_file_delete($scheme, $host, $path) {
        // 检查文件系统访问权限（缓存结果）
        if ($this-&gt;fs_access_cache === null) {
            $this-&gt;fs_access_cache = $this-&gt;check_fs_access();
        }
        
        if (!$this-&gt;fs_access_cache) {
            // 无法访问文件系统，尝试 shell 命令
            return $this-&gt;purge_via_shell($scheme, $host, $path);
        }
        
        $deleted = false;
        $path_variants = $this-&gt;get_path_variants($path);
        
        foreach ($path_variants as $path_variant) {
            $cache_files = $this-&gt;get_cache_files($scheme, $host, $path_variant);
            
            foreach ($cache_files as $cache_file) {
                if (@file_exists($cache_file) &amp;&amp; @unlink($cache_file)) {
                    $deleted = true;
                    if ($this-&gt;debug) {
                        $this-&gt;log(sprintf("文件删除成功: %s", $cache_file));
                    }
                }
            }
            
            // 也尝试删除匹配的文件（处理查询参数等情况）
            $cache_dir = dirname($cache_files[0]);
            if (@is_dir($cache_dir)) {
                $md5 = $this-&gt;get_cache_key_md5($scheme, $host, $path_variant);
                $files = @glob($cache_dir . "/" . $md5 . "*");
                if ($files &amp;&amp; is_array($files)) {
                    foreach ($files as $file) {
                        if (@is_file($file) &amp;&amp; @unlink($file)) {
                            $deleted = true;
                        }
                    }
                }
            }
        }
        
        // 如果文件删除失败，尝试 shell 命令
        if (!$deleted) {
            $deleted = $this-&gt;purge_via_shell($scheme, $host, $path);
        }
        
        return $deleted;
    }
    
    /**
     * 检查文件系统访问权限
     */
    private function check_fs_access() {
        $old_error_handler = set_error_handler(function($errno, $errstr) {
            if (strpos($errstr, "open_basedir") !== false) {
                return true;
            }
            return false;
        }, E_WARNING);
        
        $can_access = @is_dir($this-&gt;cache_path);
        restore_error_handler();
        
        return $can_access;
    }
    
    /**
     * 获取路径变体（处理尾部斜杠等）
     */
    private function get_path_variants($path) {
        $variants = array(
            $path,
            rtrim($path, "/"),
            $path . "/",
        );
        
        // 去重
        return array_unique($variants);
    }
    
    /**
     * 计算缓存键的 MD5（带缓存）
     */
    private function get_cache_key_md5($scheme, $host, $path) {
        $key = $scheme . "|" . $host . "|" . $path;
        
        if (!isset($this-&gt;cache_key_cache[$key])) {
            $cache_key_string = $scheme . "GET" . $host . $path;
            $this-&gt;cache_key_cache[$key] = md5($cache_key_string);
        }
        
        return $this-&gt;cache_key_cache[$key];
    }
    
    /**
     * 获取缓存文件路径列表
     */
    private function get_cache_files($scheme, $host, $path) {
        $md5 = $this-&gt;get_cache_key_md5($scheme, $host, $path);
        $level1 = substr($md5, -1);
        $level2 = substr($md5, -3, 2);
        $cache_file = $this-&gt;cache_path . "/" . $level1 . "/" . $level2 . "/" . $md5;
        
        return array($cache_file);
    }
    
    /**
     * 通过 shell 命令删除缓存文件
     */
    private function purge_via_shell($scheme, $host, $path) {
        // 检查 shell 命令是否可用
        if (!function_exists("exec")) {
            return false;
        }
        
        $disabled_functions = explode(",", ini_get("disable_functions"));
        if (in_array("exec", $disabled_functions)) {
            return false;
        }
        
        $deleted = false;
        $path_variants = $this-&gt;get_path_variants($path);
        
        foreach ($path_variants as $path_variant) {
            $md5 = $this-&gt;get_cache_key_md5($scheme, $host, $path_variant);
            $level1 = substr($md5, -1);
            $level2 = substr($md5, -3, 2);
            $cache_file = $this-&gt;cache_path . "/" . $level1 . "/" . $level2 . "/" . $md5;
            
            // 删除精确匹配的文件
            $command = sprintf("rm -f %s 2&gt;/dev/null", escapeshellarg($cache_file));
            @exec($command, $output, $return_var);
            
            if ($return_var === 0) {
                $deleted = true;
            }
            
            // 删除匹配的文件（处理查询参数）
            $cache_dir = dirname($cache_file);
            $glob_command = sprintf("rm -f %s/%s* 2&gt;/dev/null", escapeshellarg($cache_dir), escapeshellarg($md5));
            @exec($glob_command, $glob_output, $glob_return_var);
            
            if ($glob_return_var === 0) {
                $deleted = true;
            }
        }
        
        return $deleted;
    }
    
    /**
     * 日志记录（统一入口）
     */
    private function log($message) {
        if ($this-&gt;debug &amp;&amp; function_exists("error_log")) {
            error_log("[Nginx Cache Purge Multi-Domain] " . $message);
        }
    }
    
    /**
     * 添加管理菜单
     */
    public function add_admin_menu() {
        add_management_page(
            "Nginx 缓存管理",
            "Nginx 缓存管理",
            "manage_options",
            "nginx-cache-purge-tools",
            array($this, "render_tools_page")
        );
    }
    
    /**
     * 渲染工具页面
     */
    public function render_tools_page() {
        if (!current_user_can("manage_options")) {
            wp_die("您没有权限访问此页面");
        }
        
        // 获取缓存信息
        $cache_info = $this-&gt;get_cache_info();
        $sample_md5 = $this-&gt;get_sample_cache_md5();
        
        ?&gt;
        &lt;div class="wrap"&gt;
            &lt;h1&gt;Nginx 缓存管理工具&lt;/h1&gt;
            
            &lt;div class="card" style="max-width: 800px;"&gt;
                &lt;h2&gt;缓存信息&lt;/h2&gt;
                &lt;table class="form-table"&gt;
                    &lt;tr&gt;
                        &lt;th scope="row"&gt;缓存目录&lt;/th&gt;
                        &lt;td&gt;
                            &lt;code&gt;&lt;?php echo esc_html($this-&gt;cache_path); ?&gt;&lt;/code&gt;
                            &lt;?php if (!$cache_info["accessible"]): ?&gt;
                                &lt;span style="color: red;"&gt;（无法访问）&lt;/span&gt;
                            &lt;?php endif; ?&gt;
                        &lt;/td&gt;
                    &lt;/tr&gt;
                    &lt;tr&gt;
                        &lt;th scope="row"&gt;缓存大小&lt;/th&gt;
                        &lt;td&gt;
                            &lt;?php if ($cache_info["accessible"]): ?&gt;
                                &lt;strong&gt;&lt;?php echo esc_html($this-&gt;format_bytes($cache_info["size"])); ?&gt;&lt;/strong&gt;
                            &lt;?php else: ?&gt;
                                &lt;span style="color: red;"&gt;无法获取&lt;/span&gt;
                            &lt;?php endif; ?&gt;
                        &lt;/td&gt;
                    &lt;/tr&gt;
                    &lt;tr&gt;
                        &lt;th scope="row"&gt;缓存文件数&lt;/th&gt;
                        &lt;td&gt;
                            &lt;?php if ($cache_info["accessible"]): ?&gt;
                                &lt;strong&gt;&lt;?php echo number_format($cache_info["file_count"]); ?&gt;&lt;/strong&gt; 个文件
                            &lt;?php else: ?&gt;
                                &lt;span style="color: red;"&gt;无法获取&lt;/span&gt;
                            &lt;?php endif; ?&gt;
                        &lt;/td&gt;
                    &lt;/tr&gt;
                    &lt;tr&gt;
                        &lt;th scope="row"&gt;缓存 MD5 示例&lt;/th&gt;
                        &lt;td&gt;
                            &lt;code&gt;&lt;?php echo esc_html($sample_md5["md5"]); ?&gt;&lt;/code&gt;
                            &lt;br&gt;
                            &lt;small style="color: #666;"&gt;
                                缓存键: &lt;code&gt;&lt;?php echo esc_html($sample_md5["key"]); ?&gt;&lt;/code&gt;
                                &lt;br&gt;
                                文件路径: &lt;code&gt;&lt;?php echo esc_html($sample_md5["path"]); ?&gt;&lt;/code&gt;
                            &lt;/small&gt;
                        &lt;/td&gt;
                    &lt;/tr&gt;
                &lt;/table&gt;
                
                &lt;p&gt;
                    &lt;button type="button" id="refresh-cache-info" class="button"&gt;刷新信息&lt;/button&gt;
                    &lt;button type="button" id="purge-all-cache" class="button button-primary" style="margin-left: 10px;"&gt;清除全部缓存&lt;/button&gt;
                    &lt;button type="button" id="show-cache-paths" class="button" style="margin-left: 10px;"&gt;查看缓存文件&lt;/button&gt;
                &lt;/p&gt;

                &lt;div id="cache-action-message" style="margin-top: 15px;"&gt;&lt;/div&gt;
            &lt;/div&gt;

            &lt;div class="card" id="cache-paths-section" style="max-width: 800px; display: none; margin-top: 20px;"&gt;
                &lt;h2&gt;缓存文件列表&lt;/h2&gt;
                &lt;div id="cache-paths-controls" style="margin-bottom: 15px;"&gt;
                    &lt;label for="cache-paths-limit"&gt;显示数量:&lt;/label&gt;
                    &lt;select id="cache-paths-limit"&gt;
                        &lt;option value="20"&gt;20&lt;/option&gt;
                        &lt;option value="50" selected&gt;50&lt;/option&gt;
                        &lt;option value="100"&gt;100&lt;/option&gt;
                        &lt;option value="200"&gt;200&lt;/option&gt;
                    &lt;/select&gt;
                    &lt;button type="button" id="refresh-cache-paths" class="button" style="margin-left: 10px;"&gt;刷新列表&lt;/button&gt;
                &lt;/div&gt;
                &lt;div id="cache-paths-content"&gt;
                    &lt;p class="description"&gt;正在加载缓存文件列表...&lt;/p&gt;
                &lt;/div&gt;
                &lt;div id="cache-paths-message" style="margin-top: 15px;"&gt;&lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        
        &lt;script type="text/javascript"&gt;
        jQuery(document).ready(function($) {
            // 刷新缓存信息
            $('#refresh-cache-info').on('click', function() {
                var $btn = $(this);
                var $msg = $('#cache-action-message');
                
                $btn.prop('disabled', true).text('刷新中...');
                $msg.html('&lt;div class="notice notice-info"&gt;&lt;p&gt;正在刷新缓存信息...&lt;/p&gt;&lt;/div&gt;');
                
                $.ajax({
                    url: ajaxurl,
                    type: 'POST',
                    data: {
                        action: 'nginx_cache_get_info',
                        nonce: '&lt;?php echo wp_create_nonce("nginx_cache_tools"); ?&gt;'
                    },
                    success: function(response) {
                        if (response.success) {
                            var info = response.data;
                            $msg.html('&lt;div class="notice notice-success"&gt;&lt;p&gt;刷新成功！缓存大小: ' + info.size_formatted + ', 文件数: ' + info.file_count_formatted + '&lt;/p&gt;&lt;/div&gt;');
                            // 更新页面显示
                            if (info.accessible) {
                                $('th:contains("缓存大小")').next('td').html('&lt;strong&gt;' + info.size_formatted + '&lt;/strong&gt;');
                                $('th:contains("缓存文件数")').next('td').html('&lt;strong&gt;' + info.file_count_formatted + '&lt;/strong&gt; 个文件');
                            }
                        } else {
                            $msg.html('&lt;div class="notice notice-error"&gt;&lt;p&gt;刷新失败: ' + (response.data || '未知错误') + '&lt;/p&gt;&lt;/div&gt;');
                        }
                    },
                    error: function() {
                        $msg.html('&lt;div class="notice notice-error"&gt;&lt;p&gt;请求失败，请重试&lt;/p&gt;&lt;/div&gt;');
                    },
                    complete: function() {
                        $btn.prop('disabled', false).text('刷新信息');
                    }
                });
            });
            
            // 清除全部缓存
            $('#purge-all-cache').on('click', function() {
                if (!confirm('确定要清除全部缓存吗？此操作不可恢复！')) {
                    return;
                }

                var $btn = $(this);
                var $msg = $('#cache-action-message');

                $btn.prop('disabled', true).text('清除中...');
                $msg.html('&lt;div class="notice notice-info"&gt;&lt;p&gt;正在清除全部缓存，请稍候...&lt;/p&gt;&lt;/div&gt;');

                $.ajax({
                    url: ajaxurl,
                    type: 'POST',
                    data: {
                        action: 'nginx_cache_purge_all',
                        nonce: '&lt;?php echo wp_create_nonce("nginx_cache_tools"); ?&gt;'
                    },
                    success: function(response) {
                        if (response.success) {
                            $msg.html('&lt;div class="notice notice-success"&gt;&lt;p&gt;' + (response.data.message || '缓存清除成功！') + '&lt;/p&gt;&lt;/div&gt;');
                            // 自动刷新缓存信息
                            setTimeout(function() {
                                $('#refresh-cache-info').trigger('click');
                            }, 1000);
                        } else {
                            $msg.html('&lt;div class="notice notice-error"&gt;&lt;p&gt;清除失败: ' + (response.data || '未知错误') + '&lt;/p&gt;&lt;/div&gt;');
                        }
                    },
                    error: function() {
                        $msg.html('&lt;div class="notice notice-error"&gt;&lt;p&gt;请求失败，请重试&lt;/p&gt;&lt;/div&gt;');
                    },
                    complete: function() {
                        $btn.prop('disabled', false).text('清除全部缓存');
                    }
                });
            });

            // 显示缓存文件列表
            $('#show-cache-paths').on('click', function() {
                var $section = $('#cache-paths-section');
                if ($section.is(':visible')) {
                    $section.hide();
                    $(this).text('查看缓存文件');
                } else {
                    $section.show();
                    $(this).text('隐藏缓存文件');
                    loadCachePaths();
                }
            });

            // 刷新缓存文件列表
            $('#refresh-cache-paths').on('click', function() {
                loadCachePaths();
            });

            // 当限制数量改变时重新加载
            $('#cache-paths-limit').on('change', function() {
                loadCachePaths();
            });

            function loadCachePaths() {
                var $content = $('#cache-paths-content');
                var $msg = $('#cache-paths-message');
                var limit = $('#cache-paths-limit').val();

                $content.html('&lt;p class="description"&gt;正在加载缓存文件列表...&lt;/p&gt;');
                $msg.empty();

                $.ajax({
                    url: ajaxurl,
                    type: 'POST',
                    data: {
                        action: 'nginx_cache_get_paths',
                        limit: limit,
                        nonce: '&lt;?php echo wp_create_nonce("nginx_cache_tools"); ?&gt;'
                    },
                    success: function(response) {
                        if (response.success) {
                            var data = response.data;
                            var html = '';

                            if (data.paths &amp;&amp; data.paths.length &gt; 0) {
                                html += '&lt;table class="widefat striped"&gt;';
                                html += '&lt;thead&gt;&lt;tr&gt;';
                                html += '&lt;th&gt;缓存文件路径&lt;/th&gt;';
                                html += '&lt;th&gt;文件大小&lt;/th&gt;';
                                html += '&lt;th&gt;修改时间&lt;/th&gt;';
                                html += '&lt;th&gt;MD5&lt;/th&gt;';
                                html += '&lt;/tr&gt;&lt;/thead&gt;';
                                html += '&lt;tbody&gt;';

                                data.paths.forEach(function(item) {
                                    html += '&lt;tr&gt;';
                                    html += '&lt;td&gt;&lt;code style="word-break: break-all;"&gt;' + item.path + '&lt;/code&gt;&lt;/td&gt;';
                                    html += '&lt;td&gt;' + formatBytes(item.size) + '&lt;/td&gt;';
                                    html += '&lt;td&gt;' + new Date(item.mtime * 1000).toLocaleString() + '&lt;/td&gt;';
                                    html += '&lt;td&gt;&lt;code&gt;' + item.md5 + '&lt;/code&gt;&lt;/td&gt;';
                                    html += '&lt;/tr&gt;';
                                });

                                html += '&lt;/tbody&gt;&lt;/table&gt;';

                                if (data.has_more) {
                                    html += '&lt;p class="description"&gt;显示前 ' + data.limit + ' 个文件，共 ' + data.total_count + ' 个缓存文件。&lt;/p&gt;';
                                } else {
                                    html += '&lt;p class="description"&gt;共 ' + data.total_count + ' 个缓存文件。&lt;/p&gt;';
                                }
                            } else {
                                html = '&lt;p class="description"&gt;没有找到缓存文件。&lt;/p&gt;';
                            }

                            $content.html(html);
                        } else {
                            $content.html('&lt;p class="description"&gt;加载失败。&lt;/p&gt;');
                            $msg.html('&lt;div class="notice notice-error"&gt;&lt;p&gt;加载缓存路径失败: ' + (response.data || '未知错误') + '&lt;/p&gt;&lt;/div&gt;');
                        }
                    },
                    error: function() {
                        $content.html('&lt;p class="description"&gt;请求失败。&lt;/p&gt;');
                        $msg.html('&lt;div class="notice notice-error"&gt;&lt;p&gt;请求失败，请重试&lt;/p&gt;&lt;/div&gt;');
                    }
                });
            }

            function formatBytes(bytes) {
                if (bytes === 0) return '0 B';
                var k = 1024;
                var sizes = ['B', 'KB', 'MB', 'GB'];
                var i = Math.floor(Math.log(bytes) / Math.log(k));
                return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
            }
        });
        &lt;/script&gt;
        &lt;?php
    }
    
    /**
     * AJAX: 获取缓存信息
     */
    public function ajax_get_cache_info() {
        check_ajax_referer("nginx_cache_tools", "nonce");
        
        if (!current_user_can("manage_options")) {
            wp_send_json_error("权限不足");
        }
        
        $info = $this-&gt;get_cache_info();
        
        wp_send_json_success(array(
            "accessible" =&gt; $info["accessible"],
            "size" =&gt; $info["size"],
            "size_formatted" =&gt; $this-&gt;format_bytes($info["size"]),
            "file_count" =&gt; $info["file_count"],
            "file_count_formatted" =&gt; number_format($info["file_count"]),
        ));
    }
    
    /**
     * AJAX: 清除全部缓存
     */
    public function ajax_purge_all_cache() {
        check_ajax_referer("nginx_cache_tools", "nonce");

        if (!current_user_can("manage_options")) {
            wp_send_json_error("权限不足");
        }

        $result = $this-&gt;purge_all_cache();

        if ($result["success"]) {
            wp_send_json_success(array(
                "message" =&gt; $result["message"],
                "deleted_count" =&gt; $result["deleted_count"],
            ));
        } else {
            wp_send_json_error($result["message"]);
        }
    }

    /**
     * AJAX: 获取缓存路径列表
     */
    public function ajax_get_cache_paths() {
        check_ajax_referer("nginx_cache_tools", "nonce");

        if (!current_user_can("manage_options")) {
            wp_send_json_error("权限不足");
        }

        $paths = $this-&gt;get_cache_paths();

        if (isset($paths['error'])) {
            wp_send_json_error($paths['error']);
        }

        // 数据已经由 Lua 脚本处理，直接返回
        wp_send_json_success(array(
            "paths" =&gt; $paths,
            "total_count" =&gt; count($paths),
            "has_more" =&gt; false, // Lua 脚本已经限制了数量
            "limit" =&gt; 100,
        ));
    }
    
    /**
     * 获取缓存信息
     */
    private function get_cache_info() {
        $info = array(
            "accessible" =&gt; false,
            "size" =&gt; 0,
            "file_count" =&gt; 0,
        );
        
        // 检查文件系统访问权限
        if ($this-&gt;fs_access_cache === null) {
            $this-&gt;fs_access_cache = $this-&gt;check_fs_access();
        }
        
        if (!$this-&gt;fs_access_cache) {
            // 尝试使用 shell 命令获取信息
            return $this-&gt;get_cache_info_via_shell();
        }
        
        // 使用 PHP 文件系统函数
        if (@is_dir($this-&gt;cache_path)) {
            $info["accessible"] = true;
            $info["size"] = $this-&gt;get_dir_size($this-&gt;cache_path);
            $info["file_count"] = $this-&gt;count_files($this-&gt;cache_path);
        }
        
        return $info;
    }
    
    /**
     * 通过 shell 命令获取缓存信息
     */
    private function get_cache_info_via_shell() {
        $info = array(
            "accessible" =&gt; false,
            "size" =&gt; 0,
            "file_count" =&gt; 0,
        );
        
        if (!function_exists("exec")) {
            return $info;
        }
        
        $disabled_functions = explode(",", ini_get("disable_functions"));
        if (in_array("exec", $disabled_functions)) {
            return $info;
        }
        
        // 获取目录大小
        $size_command = sprintf("du -sb %s 2&gt;/dev/null | cut -f1", escapeshellarg($this-&gt;cache_path));
        @exec($size_command, $size_output, $size_return);
        if ($size_return === 0 &amp;&amp; !empty($size_output)) {
            $info["size"] = intval($size_output[0]);
            $info["accessible"] = true;
        }
        
        // 获取文件数量
        $count_command = sprintf("find %s -type f 2&gt;/dev/null | wc -l", escapeshellarg($this-&gt;cache_path));
        @exec($count_command, $count_output, $count_return);
        if ($count_return === 0 &amp;&amp; !empty($count_output)) {
            $info["file_count"] = intval(trim($count_output[0]));
        }
        
        return $info;
    }
    
    /**
     * 获取目录大小（递归）
     */
    private function get_dir_size($dir) {
        $size = 0;
        
        if (!@is_dir($dir)) {
            return 0;
        }
        
        $files = @scandir($dir);
        if ($files === false) {
            return 0;
        }
        
        foreach ($files as $file) {
            if ($file === "." || $file === "..") {
                continue;
            }
            
            $path = $dir . "/" . $file;
            
            if (@is_file($path)) {
                $size += @filesize($path);
            } elseif (@is_dir($path)) {
                $size += $this-&gt;get_dir_size($path);
            }
        }
        
        return $size;
    }
    
    /**
     * 统计文件数量（递归）
     */
    private function count_files($dir) {
        $count = 0;
        
        if (!@is_dir($dir)) {
            return 0;
        }
        
        $files = @scandir($dir);
        if ($files === false) {
            return 0;
        }
        
        foreach ($files as $file) {
            if ($file === "." || $file === "..") {
                continue;
            }
            
            $path = $dir . "/" . $file;
            
            if (@is_file($path)) {
                $count++;
            } elseif (@is_dir($path)) {
                $count += $this-&gt;count_files($path);
            }
        }
        
        return $count;
    }
    
    /**
     * 获取示例缓存 MD5
     */
    private function get_sample_cache_md5() {
        $scheme = "https";
        $method = "GET";
        $host = $this-&gt;get_domains()[0] ?? "example.com";
        $path = "/";

        $cache_key_string = $scheme . $method . $host . $path;
        $md5 = md5($cache_key_string);

        $level1 = substr($md5, -1);
        $level2 = substr($md5, -3, 2);
        $file_path = $this-&gt;cache_path . "/" . $level1 . "/" . $level2 . "/" . $md5;

        return array(
            "key" =&gt; $cache_key_string,
            "md5" =&gt; $md5,
            "path" =&gt; $file_path,
        );
    }

    /**
     * 获取缓存路径列表（通过 Lua 脚本）
     */
    private function get_cache_paths() {
        // 获取域名列表，使用第一个域名作为 Host
        $domains = $this-&gt;get_domains();
        if (empty($domains)) {
            return array("error" =&gt; "未配置域名");
        }

        $host = $domains[0];
        $url = "http://127.0.0.1/cache-paths";

        $args = array(
            "method" =&gt; "GET",
            "timeout" =&gt; 30,
            "headers" =&gt; array(
                "Host" =&gt; $host,
            ),
            "sslverify" =&gt; false,
            "blocking" =&gt; true,
        );

        $response = wp_remote_request($url, $args);

        if (is_wp_error($response)) {
            return array("error" =&gt; "HTTP 请求失败: " . $response-&gt;get_error_message());
        }

        $code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);

        if ($code != 200) {
            return array("error" =&gt; "请求失败 (HTTP {$code}): " . $body);
        }

        $json = json_decode($body, true);
        if (!$json || !isset($json["status"]) || $json["status"] != "success") {
            return array("error" =&gt; "响应格式错误: " . $body);
        }

        return $json["paths"] ?? array();
    }
    
    /**
     * 清除全部缓存
     */
    private function purge_all_cache() {
        $result = array(
            "success" =&gt; false,
            "message" =&gt; "",
            "deleted_count" =&gt; 0,
        );
        
        // 优先使用 HTTP 请求调用 Lua 脚本（与现有机制一致，避免权限问题）
        $http_result = $this-&gt;purge_all_cache_via_http();
        if ($http_result["success"]) {
            return $http_result;
        }
        
        // 如果 HTTP 请求失败，回退到直接文件操作
        if ($this-&gt;debug) {
            $this-&gt;log("HTTP 清除全部缓存失败，尝试使用文件系统方式: " . $http_result["message"]);
        }
        
        // 检查文件系统访问权限
        if ($this-&gt;fs_access_cache === null) {
            $this-&gt;fs_access_cache = $this-&gt;check_fs_access();
        }
        
        // 使用 shell 命令（更快更可靠）
        if (function_exists("exec")) {
            $disabled_functions = explode(",", ini_get("disable_functions"));
            if (!in_array("exec", $disabled_functions)) {
                return $this-&gt;purge_all_cache_via_shell();
            }
        }
        
        // 回退到 PHP 文件系统函数
        if ($this-&gt;fs_access_cache) {
            return $this-&gt;purge_all_cache_via_php();
        }
        
        $result["message"] = "无法清除缓存，请检查权限或 Nginx 配置";
        return $result;
    }
    
    /**
     * 通过 HTTP 请求调用 Lua 脚本清除全部缓存
     */
    private function purge_all_cache_via_http() {
        $result = array(
            "success" =&gt; false,
            "message" =&gt; "",
            "deleted_count" =&gt; 0,
        );
        
        // 获取域名列表，使用第一个域名作为 Host（清除全部缓存只需要调用一次）
        $domains = $this-&gt;get_domains();
        if (empty($domains)) {
            $result["message"] = "未配置域名";
            return $result;
        }
        
        $host = $domains[0]; // 使用第一个域名
        $purge_url = "http://127.0.0.1/purge-all"; // 使用本地回环，避免外部网络开销
        
        $args = array(
            "method" =&gt; "GET",
            "timeout" =&gt; 30, // 清除全部缓存可能需要较长时间
            "headers" =&gt; array(
                "Host" =&gt; $host,
            ),
            "sslverify" =&gt; false,
            "blocking" =&gt; true, // 必须阻塞，否则无法判断结果
        );
        
        $response = wp_remote_request($purge_url, $args);
        
        if (is_wp_error($response)) {
            $result["message"] = "HTTP 请求失败: " . $response-&gt;get_error_message();
            if ($this-&gt;debug) {
                $this-&gt;log(sprintf("HTTP PURGE-ALL 失败: %s - %s", $purge_url, $response-&gt;get_error_message()));
            }
            return $result;
        }
        
        $code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        
        // 解析 JSON 响应
        $json = json_decode($body, true);
        
        if ($code == 200 &amp;&amp; $json &amp;&amp; isset($json["status"]) &amp;&amp; $json["status"] == "success") {
            $result["success"] = true;
            $result["deleted_count"] = isset($json["deleted_count"]) ? intval($json["deleted_count"]) : 0;
            $result["message"] = isset($json["message"]) ? $json["message"] : "缓存清除成功";
            
            if ($this-&gt;debug) {
                $this-&gt;log(sprintf("HTTP PURGE-ALL 成功: %s (code=%d, deleted=%d)", $purge_url, $code, $result["deleted_count"]));
            }
        } else {
            $error_msg = isset($json["message"]) ? $json["message"] : "未知错误";
            $result["message"] = sprintf("清除失败 (HTTP %d): %s", $code, $error_msg);
            
            if ($this-&gt;debug) {
                $this-&gt;log(sprintf("HTTP PURGE-ALL 失败: %s (code=%d) - %s", $purge_url, $code, $error_msg));
            }
        }
        
        return $result;
    }
    
    /**
     * 通过 shell 命令清除全部缓存
     */
    private function purge_all_cache_via_shell() {
        $result = array(
            "success" =&gt; false,
            "message" =&gt; "",
            "deleted_count" =&gt; 0,
        );
        
        // 先统计文件数量
        $count_command = sprintf("find %s -type f 2&gt;/dev/null | wc -l", escapeshellarg($this-&gt;cache_path));
        @exec($count_command, $count_output, $count_return);
        $file_count_before = ($count_return === 0 &amp;&amp; !empty($count_output)) ? intval(trim($count_output[0])) : 0;
        
        // 删除所有缓存文件
        $delete_command = sprintf("find %s -type f -delete 2&gt;/dev/null", escapeshellarg($this-&gt;cache_path));
        @exec($delete_command, $delete_output, $delete_return);
        
        if ($delete_return === 0) {
            $result["success"] = true;
            $result["deleted_count"] = $file_count_before;
            $result["message"] = sprintf("成功清除 %d 个缓存文件", $file_count_before);
            
            if ($this-&gt;debug) {
                $this-&gt;log(sprintf("清除全部缓存成功，删除 %d 个文件", $file_count_before));
            }
        } else {
            $result["message"] = "清除缓存失败，请检查权限";
        }
        
        return $result;
    }
    
    /**
     * 通过 PHP 文件系统函数清除全部缓存
     */
    private function purge_all_cache_via_php() {
        $result = array(
            "success" =&gt; false,
            "message" =&gt; "",
            "deleted_count" =&gt; 0,
        );
        
        if (!@is_dir($this-&gt;cache_path)) {
            $result["message"] = "缓存目录不存在";
            return $result;
        }
        
        $deleted_count = 0;
        $this-&gt;delete_dir_contents($this-&gt;cache_path, $deleted_count);
        
        $result["success"] = true;
        $result["deleted_count"] = $deleted_count;
        $result["message"] = sprintf("成功清除 %d 个缓存文件", $deleted_count);
        
        if ($this-&gt;debug) {
            $this-&gt;log(sprintf("清除全部缓存成功，删除 %d 个文件", $deleted_count));
        }
        
        return $result;
    }
    
    /**
     * 递归删除目录内容（保留目录结构）
     */
    private function delete_dir_contents($dir, &amp;$deleted_count) {
        if (!@is_dir($dir)) {
            return;
        }
        
        $files = @scandir($dir);
        if ($files === false) {
            return;
        }
        
        foreach ($files as $file) {
            if ($file === "." || $file === "..") {
                continue;
            }
            
            $path = $dir . "/" . $file;
            
            if (@is_file($path)) {
                if (@unlink($path)) {
                    $deleted_count++;
                }
            } elseif (@is_dir($path)) {
                $this-&gt;delete_dir_contents($path, $deleted_count);
                // 删除空目录
                @rmdir($path);
            }
        }
    }
    
    /**
     * 格式化字节大小
     */
    private function format_bytes($bytes, $precision = 2) {
        $units = array("B", "KB", "MB", "GB", "TB");
        
        $bytes = max($bytes, 0);
        $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
        $pow = min($pow, count($units) - 1);
        
        $bytes /= pow(1024, $pow);
        
        return round($bytes, $precision) . " " . $units[$pow];
    }
}

// 初始化插件
if (class_exists("Nginx_Cache_Purge_Multi_Domain")) {
    new Nginx_Cache_Purge_Multi_Domain();
}
</pre>
<p>6.重启openresty ，启用wp插件：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">systemclt reload openresty</pre>
<p>实际效果 <a href="https://www.itdog.cn/http/https://zhongxiaojie.cn" target="_blank" rel="noopener">快速测试</a>：</p>
<p><a href="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205053.png" data-lbwps-width="1890" data-lbwps-height="1357" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205053-665x477.png" data-lightbox="gal[270]"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-271" src="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205053.png" alt="" width="1890" height="1357" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205053.png?v=1769518277 1890w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205053-300x215.png?v=1769518277 300w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205053-1024x735.png?v=1769518277 1024w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205053-150x108.png?v=1769518277 150w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205053-768x551.png?v=1769518277 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205053-1536x1103.png?v=1769518277 1536w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205053-665x477.png?v=1769518277 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205053-362x260.png?v=1769518277 362w" sizes="auto, (max-width: 1890px) 100vw, 1890px" /></a></p>
<p>系统负载 btop：</p>
<p><a href="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205217.png" data-lbwps-width="3075" data-lbwps-height="1890" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205217-665x409.png" data-lightbox="gal[270]"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-272" src="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205217-scaled.png" alt="" width="2560" height="1573" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205217-scaled.png?v=1769518357 2560w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205217-300x184.png?v=1769518357 300w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205217-1024x629.png?v=1769518357 1024w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205217-150x92.png?v=1769518357 150w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205217-768x472.png?v=1769518357 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205217-1536x944.png?v=1769518357 1536w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205217-2048x1259.png?v=1769518357 2048w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205217-665x409.png?v=1769518357 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205217-423x260.png?v=1769518357 423w" sizes="auto, (max-width: 2560px) 100vw, 2560px" /></a></p>
<p>lighthouse:</p>
<p><a href="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205341.png" data-lbwps-width="3766" data-lbwps-height="1888" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205341-665x333.png" data-lightbox="gal[270]"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-273" src="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205341-scaled.png" alt="" width="2560" height="1283" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205341-scaled.png?v=1769518449 2560w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205341-300x150.png?v=1769518449 300w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205341-1024x513.png?v=1769518449 1024w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205341-150x75.png?v=1769518449 150w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205341-768x385.png?v=1769518449 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205341-1536x770.png?v=1769518449 1536w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205341-2048x1027.png?v=1769518449 2048w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205341-665x333.png?v=1769518449 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205341-519x260.png?v=1769518449 519w" sizes="auto, (max-width: 2560px) 100vw, 2560px" /></a></p>
<p>缓存管理：</p>
<p><a href="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205448.png" data-lbwps-width="1472" data-lbwps-height="1848" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205448.png" data-lightbox="gal[270]"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-274" src="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205448.png" alt="" width="1472" height="1848" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205448.png?v=1769518502 1472w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205448-239x300.png?v=1769518502 239w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205448-816x1024.png?v=1769518502 816w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205448-119x150.png?v=1769518502 119w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205448-768x964.png?v=1769518502 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205448-1223x1536.png?v=1769518502 1223w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205448-665x835.png?v=1769518502 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Screenshot-2026-01-27-205448-207x260.png?v=1769518502 207w" sizes="auto, (max-width: 1472px) 100vw, 1472px" /></a></p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://zhongxiaojie.cn/2026/01/270/feed/</wfw:commentRss>
			<slash:comments>68</slash:comments>
		
		
			</item>
		<item>
		<title>彻底解决WordPress站点健康问题</title>
		<link>https://zhongxiaojie.cn/2026/01/197/</link>
					<comments>https://zhongxiaojie.cn/2026/01/197/#comments</comments>
		
		<dc:creator><![CDATA[obaby]]></dc:creator>
		<pubDate>Wed, 21 Jan 2026 01:29:18 +0000</pubDate>
				<category><![CDATA[程序媛]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[缓存]]></category>
		<guid isPermaLink="false">https://zhongxiaojie.cn/?p=197</guid>

					<description><![CDATA[再次建站的时候，虽然没有重装系统，但是却把系统的php版本升级到了8.4。也放弃了之前编译的php7.4版本的 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://zhongxiaojie.cn/wp-content/uploads/2026/01/downloaded-image.jpg" data-lbwps-width="1080" data-lbwps-height="1440" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/01/downloaded-image.jpg" data-lightbox="gal[197]"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-199" src="https://zhongxiaojie.cn/wp-content/uploads/2026/01/downloaded-image.jpg" alt="" width="1080" height="1440" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/01/downloaded-image.jpg 1080w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/downloaded-image-225x300.jpg 225w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/downloaded-image-768x1024.jpg 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/downloaded-image-113x150.jpg 113w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/downloaded-image-665x887.jpg 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/downloaded-image-195x260.jpg 195w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/downloaded-image-750x1000.jpg 750w" sizes="auto, (max-width: 1080px) 100vw, 1080px" /></a></p>
<p>再次建站的时候，虽然没有重装系统，但是却把系统的php版本升级到了8.4。也放弃了之前编译的php7.4版本的结巴分词，而是直接启了个<a href="https://gitee.com/obaby/baby-jb-server" target="_blank" rel="noopener">python的服务</a>。</p>
<p><span id="more-197"></span></p>
<p>不过，有的东西确一直没解决，那就是从之前的7.4，到现在的8.4版本，站点健康在响应速度慢的时候就会显示：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">页面缓存通过保存和提供静态页面使得用户访问时不需要每次都调用页面，进而改善了您站点的速度和性能。

页面缓存会通过查找已启用的页面缓存插件的同时向主页发起三次请求并查找一个或多个下列的 HTTP 客户端响应标头，来确定页面缓存的存在。

cache-control, expires, age, last-modified, etag, x-cache-enabled, x-cache-disabled, x-srcache-store-status, x-srcache-fetch-status.
 服务器响应时间的中位数是 718 毫秒，其应当小于推荐的 600 毫秒临界值。
 未检测到客户端缓存响应标头。
 未检测到页面缓存插件。</pre>
<p>虽然，已经开启了redis object cache，但是这个东西时常出现，看着总是不爽。于是下定决心要解决这个问题，其实主要就是nginx的配置问题。通过下面的方法配置就ok了。</p>
<p>1.在 nginx.conf 的 http {} 块中添加（仅需一次）</p>
<pre class="EnlighterJSRAW" data-enlighter-language="nginx">fastcgi_cache_path /var/cache/nginx/wordpress
    levels=1:2
    keys_zone=WORDPRESS:64m
    max_size=256m
    inactive=60m
    use_temp_path=off;</pre>
<p>如果目录不存在，先创建：/var/cache/nginx/wordpress</p>
<p>2.创建wordpress-php-with-cache.conf</p>
<pre class="EnlighterJSRAW" data-enlighter-language="nginx"># 1. 复制本文件为 wordpress-php-with-cache.conf（去掉 .example），并放到与 zhongxiaojie.conf 同一目录（如 vhost/）
# 2. 若 PHP 版本或路径不同，请修改 fastcgi_pass（见下方说明）
# 3. 在 zhongxiaojie.conf 的 443 server 块中，用 include wordpress-php-with-cache.conf 替换 include enable-php-pathinfo.conf 或 include enable-php.conf
# 4. 若 fastcgi.conf 找不到，请将 include 改为 Nginx 配置目录下的绝对路径，如 /etc/nginx/fastcgi.conf
# 5. 执行：mkdir -p /var/cache/nginx/wordpress &amp;&amp; chown -R www:www /var/cache/nginx/wordpress
# 6. nginx -t &amp;&amp; nginx -s reload

# fastcgi_pass 常见取值（按实际修改）：
#   PHP 8.4 FPM（常见）：unix:/run/php/php8.4-fpm.sock
#   PHP 8.2 FPM：        unix:/run/php/php8.2-fpm.sock
#   宝塔：               unix:/tmp/php-cgi-74.sock 等

location ~ [^/]\.php(/|$)
{
    try_files $uri =404;

    fastcgi_pass unix:/run/php/php8.4-fpm.sock;

    # ------ 页面缓存：跳过后台、登录、订阅等 ------
    set $skip_cache 0;
    if ($request_uri ~* "/wp-admin/|/wp-login\.php|/xmlrpc\.php|wp-.*\.php|/feed/|sitemap(_index)?.xml|/cart/|/checkout/|/my-account/") {
        set $skip_cache 1;
    }
    if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in|woocommerce_") {
        set $skip_cache 1;
    }
    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache $skip_cache;

    # ------ FastCGI 缓存（依赖 nginx.conf 中 fastcgi_cache_path WORDPRESS）------
    fastcgi_cache WORDPRESS;
    fastcgi_cache_key $scheme$request_method$host$request_uri;
    fastcgi_cache_valid 200 301 302 60m;
    fastcgi_cache_use_stale error timeout updating http_500 http_503;
    fastcgi_cache_lock on;
    fastcgi_cache_lock_timeout 5s;

    fastcgi_index index.php;
    include fastcgi.conf;

    # ------ 检测工具要求的客户端缓存响应头 ------
    add_header X-Cache-Status $upstream_cache_status;
    add_header X-Cache-Enabled "1";
    add_header Cache-Control "public, max-age=3600";
}
</pre>
<p>3.在网站的配置文件中引入上面的配置信息vim /usr/local/nginx/conf/vhost/zhongxiaojie.com.conf</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">include wordpress-php-with-cache.conf;</pre>
<p>重启nginx 就ok啦</p>
<p><a href="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Jietu20260121-092417.jpg" data-lbwps-width="1799" data-lbwps-height="1451" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Jietu20260121-092417-665x536.jpg" data-lightbox="gal[197]"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-198" src="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Jietu20260121-092417.jpg" alt="" width="1799" height="1451" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/01/Jietu20260121-092417.jpg?v=1768958675 1799w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Jietu20260121-092417-300x242.jpg?v=1768958675 300w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Jietu20260121-092417-1024x826.jpg?v=1768958675 1024w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Jietu20260121-092417-150x121.jpg?v=1768958675 150w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Jietu20260121-092417-768x619.jpg?v=1768958675 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Jietu20260121-092417-1536x1239.jpg?v=1768958675 1536w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Jietu20260121-092417-665x536.jpg?v=1768958675 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/Jietu20260121-092417-322x260.jpg?v=1768958675 322w" sizes="auto, (max-width: 1799px) 100vw, 1799px" /></a></p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://zhongxiaojie.cn/2026/01/197/feed/</wfw:commentRss>
			<slash:comments>37</slash:comments>
		
		
			</item>
		<item>
		<title>警告（禁止）	unserialize(): Error at offset 4351 of 4374 bytes</title>
		<link>https://zhongxiaojie.cn/2026/01/73/</link>
					<comments>https://zhongxiaojie.cn/2026/01/73/#comments</comments>
		
		<dc:creator><![CDATA[obaby]]></dc:creator>
		<pubDate>Fri, 16 Jan 2026 02:44:15 +0000</pubDate>
				<category><![CDATA[程序媛]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[WP]]></category>
		<guid isPermaLink="false">https://zhongxiaojie.cn/?p=73</guid>

					<description><![CDATA[解决数据库替换 url 导致的错误。 警告（禁止） unserialize(): Error at offse [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://zhongxiaojie.cn/wp-content/uploads/2026/01/微信图片_20260116104025_529_42.jpg" data-lbwps-width="1280" data-lbwps-height="1920" data-lbwps-srcsmall="https://zhongxiaojie.cn/wp-content/uploads/2026/01/微信图片_20260116104025_529_42.jpg" data-lightbox="gal[73]"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-74" src="https://zhongxiaojie.cn/wp-content/uploads/2026/01/微信图片_20260116104025_529_42.jpg" alt="" width="1280" height="1920" srcset="https://zhongxiaojie.cn/wp-content/uploads/2026/01/微信图片_20260116104025_529_42.jpg?v=1768531341 1280w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/微信图片_20260116104025_529_42-200x300.jpg?v=1768531341 200w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/微信图片_20260116104025_529_42-683x1024.jpg?v=1768531341 683w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/微信图片_20260116104025_529_42-768x1152.jpg?v=1768531341 768w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/微信图片_20260116104025_529_42-1024x1536.jpg?v=1768531341 1024w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/微信图片_20260116104025_529_42-665x998.jpg?v=1768531341 665w, https://zhongxiaojie.cn/wp-content/uploads/2026/01/微信图片_20260116104025_529_42-173x260.jpg?v=1768531341 173w" sizes="auto, (max-width: 1280px) 100vw, 1280px" /></a></p>
<p>解决数据库替换 url 导致的错误。</p>
<p><span id="more-73"></span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">警告（禁止）	unserialize(): Error at offset 4351 of 4374 bytes	
wp-includes/functions.php:655
unserialize()
wp-includes/functions.php:655
maybe_unserialize()
wp-includes/functions.php:655
array_map()
wp-includes/meta.php:692
get_metadata_raw()
wp-includes/meta.php:602
get_metadata()
wp-includes/post.php:2712
get_post_meta()
wp-admin/includes/class-wp-privacy-policy-content.php:69
WP_Privacy_Policy_Content::text_change_check()
wp-includes/class-wp-hook.php:341
do_action('admin_init')
wp-admin/admin.php:180</pre>
<p>修复代码：</p>
<pre class="EnlighterJSRAW" data-enlighter-language="php">&lt;?php
/**
 * WordPress 序列化数据修复脚本
 * 修复因直接 SQL REPLACE 导致的序列化数据损坏问题
 * 
 * 使用方法：
 * 1. 修改下面的数据库连接信息
 * 2. 在命令行运行: php fix-unserialize-error.php
 * 3. 或者通过浏览器访问（需要配置 web 服务器）
 */

// ========== 配置区域 ==========
define('DB_HOST', '127.0.0.1');
define('DB_NAME', 'zxj');
define('DB_USER', 'root');
define('DB_PASS', 'zxj');
define('DB_PREFIX', 'wp_'); // 表前缀

// 旧 URL 和新 URL（用于修复）
define('OLD_URL', 'http://test.h4ck.org.cn:18888');
define('NEW_URL', 'https://zhongxiaojie.cn');

// ========== 主程序 ==========

// 连接数据库
try {
    $pdo = new PDO(
        "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4",
        DB_USER,
        DB_PASS,
        [
            PDO::ATTR_ERRMODE =&gt; PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE =&gt; PDO::FETCH_ASSOC
        ]
    );
} catch (PDOException $e) {
    die("数据库连接失败: " . $e-&gt;getMessage() . "\n");
}

echo "==========================================\n";
echo "WordPress 序列化数据修复工具\n";
echo "==========================================\n\n";

/**
 * 修复序列化字符串中的 URL
 * 正确更新序列化数据中的长度信息
 */
function fix_serialized_url($serialized, $old_url, $new_url) {
    // 如果不是序列化数据，直接替换
    if (!is_serialized($serialized)) {
        return str_replace($old_url, $new_url, $serialized);
    }
    
    // 尝试反序列化
    $data = @unserialize($serialized);
    
    // 如果反序列化失败，尝试修复
    if ($data === false &amp;&amp; $serialized !== serialize(false)) {
        // 尝试修复常见的序列化问题
        $fixed = fix_broken_serialize($serialized, $old_url, $new_url);
        $data = @unserialize($fixed);
        
        if ($data === false &amp;&amp; $fixed !== serialize(false)) {
            return false; // 无法修复
        }
        
        $serialized = $fixed;
        $data = @unserialize($serialized);
    }
    
    // 递归替换数组或对象中的 URL
    if (is_array($data)) {
        $data = array_map(function($value) use ($old_url, $new_url) {
            if (is_string($value)) {
                return str_replace($old_url, $new_url, $value);
            } elseif (is_array($value)) {
                return array_map(function($v) use ($old_url, $new_url) {
                    return is_string($v) ? str_replace($old_url, $new_url, $v) : $v;
                }, $value);
            }
            return $value;
        }, $data);
    } elseif (is_object($data)) {
        foreach ($data as $key =&gt; $value) {
            if (is_string($value)) {
                $data-&gt;$key = str_replace($old_url, $new_url, $value);
            }
        }
    } elseif (is_string($data)) {
        $data = str_replace($old_url, $new_url, $data);
    }
    
    // 重新序列化
    return serialize($data);
}

/**
 * 检查字符串是否是序列化数据
 */
function is_serialized($data) {
    if (!is_string($data)) {
        return false;
    }
    $data = trim($data);
    if ('N;' == $data) {
        return true;
    }
    if (strlen($data) &lt; 4) {
        return false;
    }
    if (':' !== $data[1]) {
        return false;
    }
    $semi = strpos($data, ';');
    if (false === $semi) {
        return false;
    }
    $token = $data[0];
    switch ($token) {
        case 's':
            if ('"' !== $data[$semi - 1]) {
                return false;
            }
        case 'a':
        case 'O':
            return (bool) preg_match("/^{$token}:[0-9]+:/s", $data);
        case 'b':
        case 'i':
        case 'd':
            return (bool) preg_match("/^{$token}:[0-9.E-]+;/", $data);
    }
    return false;
}

/**
 * 尝试修复损坏的序列化数据
 */
function fix_broken_serialize($serialized, $old_url = null, $new_url = null) {
    // 如果包含旧 URL，先替换
    if ($old_url &amp;&amp; $new_url &amp;&amp; strpos($serialized, $old_url) !== false) {
        $serialized = str_replace($old_url, $new_url, $serialized);
    }
    
    // 尝试修复长度信息
    // 匹配字符串长度模式: s:数字:"内容"
    // 注意：需要处理转义字符和嵌套引号
    $pattern = '/s:(\d+):"((?:[^"\\\\]|\\\\.)*)"/';
    $serialized = preg_replace_callback($pattern, function($matches) {
        // 计算实际字符串长度（考虑转义字符）
        $str = $matches[2];
        $length = strlen($str);
        return 's:' . $length . ':"' . $str . '"';
    }, $serialized);
    
    return $serialized;
}

/**
 * 检测并修复损坏的序列化数据
 */
function detect_and_fix_broken_serialize($serialized) {
    // 如果不是序列化数据，直接返回
    if (!is_serialized($serialized)) {
        return ['fixed' =&gt; false, 'data' =&gt; $serialized, 'error' =&gt; null];
    }
    
    // 清除之前的错误
    @error_clear_last();
    
    // 尝试反序列化
    $data = @unserialize($serialized);
    
    // 检查是否有错误
    $error = error_get_last();
    
    // 如果反序列化成功且没有错误，返回原数据
    if ($data !== false &amp;&amp; ($error === null || strpos($error['message'], 'unserialize') === false)) {
        // 特殊处理：false 的序列化结果是 'b:0;'
        if ($serialized === 'b:0;' || $serialized === serialize(false)) {
            return ['fixed' =&gt; false, 'data' =&gt; $serialized, 'error' =&gt; null];
        }
        return ['fixed' =&gt; false, 'data' =&gt; $serialized, 'error' =&gt; null];
    }
    
    // 反序列化失败，记录错误
    $original_error = $error ? $error['message'] : 'Unserialize failed';
    
    // 尝试修复长度信息
    $fixed = fix_broken_serialize($serialized);
    @error_clear_last();
    $test_data = @unserialize($fixed);
    $test_error = error_get_last();
    
    if ($test_data !== false &amp;&amp; ($test_error === null || strpos($test_error['message'], 'unserialize') === false)) {
        return ['fixed' =&gt; true, 'data' =&gt; $fixed, 'error' =&gt; $original_error];
    }
    
    // 如果还是失败，尝试更激进的修复
    // 修复所有字符串长度（更精确的匹配）
    $fixed2 = preg_replace_callback('/s:(\d+):"((?:[^"\\\\]|\\\\.)*)"/', function($m) {
        $str = $m[2];
        $len = strlen($str);
        return 's:' . $len . ':"' . $str . '"';
    }, $serialized);
    
    @error_clear_last();
    $test_data2 = @unserialize($fixed2);
    $test_error2 = error_get_last();
    
    if ($test_data2 !== false &amp;&amp; ($test_error2 === null || strpos($test_error2['message'], 'unserialize') === false)) {
        return ['fixed' =&gt; true, 'data' =&gt; $fixed2, 'error' =&gt; $original_error];
    }
    
    return ['fixed' =&gt; false, 'data' =&gt; $serialized, 'error' =&gt; $original_error];
}

$fixed_count = 0;
$error_count = 0;
$broken_count = 0;

// 修复 wp_postmeta 表
echo "[1/4] 检查 wp_postmeta 表中的序列化数据...\n";

// 首先查找所有序列化数据
$stmt = $pdo-&gt;prepare("SELECT meta_id, post_id, meta_key, meta_value FROM " . DB_PREFIX . "postmeta WHERE meta_value LIKE 'a:%' OR meta_value LIKE 'O:%' OR meta_value LIKE 's:%' OR meta_value LIKE 'i:%' OR meta_value LIKE 'b:%' OR meta_value LIKE 'd:%'");
$stmt-&gt;execute();
$all_serialized = $stmt-&gt;fetchAll();

echo "找到 " . count($all_serialized) . " 条可能的序列化数据记录\n";
echo "开始检测损坏的数据...\n\n";

$update_stmt = $pdo-&gt;prepare("UPDATE " . DB_PREFIX . "postmeta SET meta_value = ? WHERE meta_id = ?");
$broken_records = [];

foreach ($all_serialized as $row) {
    $result = detect_and_fix_broken_serialize($row['meta_value']);
    
    if ($result['fixed']) {
        $broken_count++;
        $broken_records[] = [
            'meta_id' =&gt; $row['meta_id'],
            'post_id' =&gt; $row['post_id'],
            'meta_key' =&gt; $row['meta_key'],
            'error' =&gt; $result['error']
        ];
        
        try {
            $update_stmt-&gt;execute([$result['data'], $row['meta_id']]);
            $fixed_count++;
            if ($fixed_count % 10 == 0) {
                echo "  已修复 {$fixed_count} 条损坏的记录...\n";
            }
        } catch (PDOException $e) {
            echo "  ✗ 更新失败 meta_id={$row['meta_id']}: " . $e-&gt;getMessage() . "\n";
            $error_count++;
        }
    }
}

if ($broken_count &gt; 0) {
    echo "\n发现并修复了 {$broken_count} 条损坏的序列化数据\n";
    if (count($broken_records) &lt;= 20) {
        echo "\n损坏的记录详情：\n";
        foreach ($broken_records as $rec) {
            echo "  - meta_id={$rec['meta_id']}, post_id={$rec['post_id']}, meta_key={$rec['meta_key']}\n";
        }
    }
} else {
    echo "未发现损坏的序列化数据\n";
}

// 也检查包含旧 URL 的记录（即使已经修复了长度问题，URL 可能还需要更新）
echo "\n检查是否还有包含旧 URL 的记录...\n";
$stmt = $pdo-&gt;prepare("SELECT meta_id, post_id, meta_key, meta_value FROM " . DB_PREFIX . "postmeta WHERE meta_value LIKE ?");
$stmt-&gt;execute(['%' . OLD_URL . '%']);
$url_rows = $stmt-&gt;fetchAll();

if (count($url_rows) &gt; 0) {
    echo "找到 " . count($url_rows) . " 条包含旧 URL 的记录，正在修复...\n";
    foreach ($url_rows as $row) {
        $original = $row['meta_value'];
        $fixed = fix_serialized_url($original, OLD_URL, NEW_URL);
        
        if ($fixed !== false &amp;&amp; $fixed !== $original) {
            try {
                $update_stmt-&gt;execute([$fixed, $row['meta_id']]);
                $fixed_count++;
            } catch (PDOException $e) {
                $error_count++;
            }
        }
    }
}

echo "\n[2/4] 检查 wp_options 表中的序列化数据...\n";

// 检查所有序列化数据
$stmt = $pdo-&gt;prepare("SELECT option_id, option_name, option_value FROM " . DB_PREFIX . "options WHERE option_value LIKE 'a:%' OR option_value LIKE 'O:%' OR option_value LIKE 's:%' OR option_value LIKE 'i:%' OR option_value LIKE 'b:%' OR option_value LIKE 'd:%'");
$stmt-&gt;execute();
$all_options = $stmt-&gt;fetchAll();

echo "找到 " . count($all_options) . " 条可能的序列化选项\n";
echo "开始检测损坏的数据...\n\n";

$update_stmt = $pdo-&gt;prepare("UPDATE " . DB_PREFIX . "options SET option_value = ? WHERE option_id = ?");
$broken_options = [];

foreach ($all_options as $row) {
    // 跳过 siteurl 和 home
    if (in_array($row['option_name'], ['siteurl', 'home'])) {
        continue;
    }
    
    $result = detect_and_fix_broken_serialize($row['option_value']);
    
    if ($result['fixed']) {
        $broken_count++;
        $broken_options[] = [
            'option_id' =&gt; $row['option_id'],
            'option_name' =&gt; $row['option_name'],
            'error' =&gt; $result['error']
        ];
        
        try {
            $update_stmt-&gt;execute([$result['data'], $row['option_id']]);
            $fixed_count++;
        } catch (PDOException $e) {
            $error_count++;
        }
    }
}

if (count($broken_options) &gt; 0) {
    echo "发现并修复了 " . count($broken_options) . " 条损坏的选项\n";
    if (count($broken_options) &lt;= 20) {
        echo "\n损坏的选项详情：\n";
        foreach ($broken_options as $opt) {
            echo "  - option_id={$opt['option_id']}, option_name={$opt['option_name']}\n";
        }
    }
}

// 检查包含旧 URL 的选项
echo "\n检查是否还有包含旧 URL 的选项...\n";
$stmt = $pdo-&gt;prepare("SELECT option_id, option_name, option_value FROM " . DB_PREFIX . "options WHERE option_value LIKE ?");
$stmt-&gt;execute(['%' . OLD_URL . '%']);
$url_options = $stmt-&gt;fetchAll();

if (count($url_options) &gt; 0) {
    echo "找到 " . count($url_options) . " 条包含旧 URL 的选项，正在修复...\n";
    foreach ($url_options as $row) {
        if (in_array($row['option_name'], ['siteurl', 'home'])) {
            continue;
        }
        
        $original = $row['option_value'];
        $fixed = fix_serialized_url($original, OLD_URL, NEW_URL);
        
        if ($fixed !== false &amp;&amp; $fixed !== $original) {
            try {
                $update_stmt-&gt;execute([$fixed, $row['option_id']]);
                $fixed_count++;
            } catch (PDOException $e) {
                $error_count++;
            }
        }
    }
}

echo "\n[3/4] 检查其他元数据表...\n";
$tables = [
    DB_PREFIX . 'usermeta' =&gt; ['umeta_id', 'user_id', 'meta_key', 'meta_value'],
    DB_PREFIX . 'commentmeta' =&gt; ['meta_id', 'comment_id', 'meta_key', 'meta_value'],
    DB_PREFIX . 'termmeta' =&gt; ['meta_id', 'term_id', 'meta_key', 'meta_value']
];

foreach ($tables as $table =&gt; $columns) {
    if (!table_exists($pdo, $table)) {
        continue;
    }
    
    echo "检查 {$table}...\n";
    
    // 检查所有序列化数据
    $stmt = $pdo-&gt;prepare("SELECT {$columns[0]}, {$columns[1]}, {$columns[2]}, {$columns[3]} FROM {$table} WHERE {$columns[3]} LIKE 'a:%' OR {$columns[3]} LIKE 'O:%' OR {$columns[3]} LIKE 's:%' OR {$columns[3]} LIKE 'i:%' OR {$columns[3]} LIKE 'b:%' OR {$columns[3]} LIKE 'd:%'");
    $stmt-&gt;execute();
    $all_rows = $stmt-&gt;fetchAll();
    
    if (count($all_rows) &gt; 0) {
        echo "  找到 " . count($all_rows) . " 条序列化数据，检测中...\n";
        $update_stmt = $pdo-&gt;prepare("UPDATE {$table} SET {$columns[3]} = ? WHERE {$columns[0]} = ?");
        
        foreach ($all_rows as $row) {
            $result = detect_and_fix_broken_serialize($row[$columns[3]]);
            
            if ($result['fixed']) {
                $broken_count++;
                try {
                    $update_stmt-&gt;execute([$result['data'], $row[$columns[0]]]);
                    $fixed_count++;
                } catch (PDOException $e) {
                    $error_count++;
                }
            }
        }
    }
    
    // 也检查包含旧 URL 的记录
    $stmt = $pdo-&gt;prepare("SELECT {$columns[0]}, {$columns[1]}, {$columns[2]}, {$columns[3]} FROM {$table} WHERE {$columns[3]} LIKE ?");
    $stmt-&gt;execute(['%' . OLD_URL . '%']);
    $url_rows = $stmt-&gt;fetchAll();
    
    if (count($url_rows) &gt; 0) {
        echo "  找到 " . count($url_rows) . " 条包含旧 URL 的记录，正在修复...\n";
        $update_stmt = $pdo-&gt;prepare("UPDATE {$table} SET {$columns[3]} = ? WHERE {$columns[0]} = ?");
        
        foreach ($url_rows as $row) {
            $original = $row[$columns[3]];
            $fixed = fix_serialized_url($original, OLD_URL, NEW_URL);
            
            if ($fixed !== false &amp;&amp; $fixed !== $original) {
                try {
                    $update_stmt-&gt;execute([$fixed, $row[$columns[0]]]);
                    $fixed_count++;
                } catch (PDOException $e) {
                    $error_count++;
                }
            }
        }
    }
}

echo "\n[4/4] 查找可能导致错误的特定记录...\n";
echo "根据错误信息 'Error at offset 4351 of 4374 bytes'，查找长度接近的记录...\n";

// 查找长度在 4300-4400 字节之间的序列化数据
$stmt = $pdo-&gt;prepare("SELECT meta_id, post_id, meta_key, meta_value, LENGTH(meta_value) as value_length FROM " . DB_PREFIX . "postmeta WHERE (meta_value LIKE 'a:%' OR meta_value LIKE 'O:%' OR meta_value LIKE 's:%') AND LENGTH(meta_value) BETWEEN 4300 AND 4400 ORDER BY value_length");
$stmt-&gt;execute();
$suspicious = $stmt-&gt;fetchAll();

if (count($suspicious) &gt; 0) {
    echo "找到 " . count($suspicious) . " 条可疑记录（长度在 4300-4400 字节之间）\n";
    echo "这些记录可能是导致错误的原因：\n\n";
    
    $update_stmt = $pdo-&gt;prepare("UPDATE " . DB_PREFIX . "postmeta SET meta_value = ? WHERE meta_id = ?");
    
    foreach ($suspicious as $sus) {
        echo "  - meta_id={$sus['meta_id']}, post_id={$sus['post_id']}, meta_key={$sus['meta_key']}, length={$sus['value_length']}\n";
        
        // 直接尝试反序列化完整数据
        @error_clear_last();
        $test_data = @unserialize($sus['meta_value']);
        $test_error = error_get_last();
        
        // 检查是否有 unserialize 错误
        if ($test_error &amp;&amp; strpos($test_error['message'], 'unserialize') !== false) {
            echo "    ✗ 检测到损坏: {$test_error['message']}\n";
            echo "    正在修复...\n";
            
            // 尝试修复
            $fix_result = detect_and_fix_broken_serialize($sus['meta_value']);
            
            if ($fix_result['fixed']) {
                try {
                    $update_stmt-&gt;execute([$fix_result['data'], $sus['meta_id']]);
                    echo "    ✓ 已修复并更新数据库\n";
                    $fixed_count++;
                    $broken_count++;
                } catch (PDOException $e) {
                    echo "    ✗ 更新失败: " . $e-&gt;getMessage() . "\n";
                    $error_count++;
                }
            } else {
                // 如果自动修复失败，尝试手动修复字符串长度
                echo "    尝试手动修复字符串长度...\n";
                $manual_fixed = preg_replace_callback('/s:(\d+):"((?:[^"\\\\]|\\\\.)*)"/', function($m) {
                    $str = $m[2];
                    $len = strlen($str);
                    if ($len != $m[1]) {
                        return 's:' . $len . ':"' . $str . '"';
                    }
                    return $m[0];
                }, $sus['meta_value']);
                
                @error_clear_last();
                $manual_test = @unserialize($manual_fixed);
                $manual_error = error_get_last();
                
                if ($manual_test !== false &amp;&amp; ($manual_error === null || strpos($manual_error['message'], 'unserialize') === false)) {
                    try {
                        $update_stmt-&gt;execute([$manual_fixed, $sus['meta_id']]);
                        echo "    ✓ 手动修复成功并更新数据库\n";
                        $fixed_count++;
                        $broken_count++;
                    } catch (PDOException $e) {
                        echo "    ✗ 更新失败: " . $e-&gt;getMessage() . "\n";
                        $error_count++;
                    }
                } else {
                    echo "    ✗ 无法修复此记录\n";
                    echo "    建议：如果这个 meta_key 不重要，可以考虑删除\n";
                    echo "    DELETE FROM " . DB_PREFIX . "postmeta WHERE meta_id = {$sus['meta_id']};\n";
                    $error_count++;
                }
            }
        } else {
            echo "    ✓ 数据正常（未检测到错误）\n";
        }
        echo "\n";
    }
}

function table_exists($pdo, $table) {
    try {
        $stmt = $pdo-&gt;query("SHOW TABLES LIKE '{$table}'");
        return $stmt-&gt;rowCount() &gt; 0;
    } catch (PDOException $e) {
        return false;
    }
}

echo "\n==========================================\n";
echo "修复完成！\n";
echo "==========================================\n";
echo "成功修复: {$fixed_count} 条记录\n";
echo "发现损坏: {$broken_count} 条记录\n";
echo "修复失败: {$error_count} 条记录\n";
echo "\n";

if ($fixed_count &gt; 0) {
    echo "✓ 已修复 {$fixed_count} 条损坏的序列化数据\n";
    echo "\n建议：\n";
    echo "1. 清除 WordPress 缓存（对象缓存、页面缓存）\n";
    echo "2. 刷新后台页面，检查错误是否消失\n";
    echo "3. 如果仍有问题，请提供新的错误信息\n";
} else {
    echo "<img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 未发现损坏的序列化数据\n";
    echo "\n可能的原因：\n";
    echo "1. 错误来自其他表或数据\n";
    echo "2. 错误是动态生成的（不是数据库中的数据）\n";
    echo "3. 需要查看完整的错误堆栈信息\n";
    echo "\n建议：\n";
    echo "1. 启用 WordPress 调试模式（wp-config.php 中设置 WP_DEBUG = true）\n";
    echo "2. 查看错误日志，找到具体的 meta_id 或 option_id\n";
    echo "3. 检查 wp-includes/functions.php:655 附近的代码\n";
}

echo "\n";
</pre>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://zhongxiaojie.cn/2026/01/73/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
