此前,我曾经排查过一次 WordPress 核心在线升级失败的问题。当时升级请求经过 EdgeOne,长时间运行的后台请求最终出现超时,数据库中还一度留下了 core_updater.lock。
前文记录:
最近,我已经将英文站迁移到:
en.shuijingwanwq.com
并将上传目录中的图片和附件迁移到:
media.shuijingwanwq.com
这两个域名目前均使用 Cloudflare,中文主站继续通过 EdgeOne 提供服务。
原本以为完成这两次拆分以后,EdgeOne 的流量压力会明显降低。但从 2026 年 7 月 12 日的统计数据来看,www.shuijingwanwq.com 一天仍然产生了约:
L7 访问流量:2.9 GB
EdgeOne 响应流量:2.8 GB
客户端请求流量:102.66 MB
L7 访问请求数:13.8 万次

后台迁移并不能直接消除这 2.9 GB 前台流量,因为其中绝大部分仍然来自中文站页面、静态文件、搜索引擎和其他访客请求。
不过,这至少说明:已经没有必要再让 WordPress 后台操作继续通过 EdgeOne。
结合此前的核心升级超时问题,我最终决定将 WordPress 后台迁移到一个独立子域名:
admin.shuijingwanwq.com
本文完整记录这次迁移过程,包括 Nginx 虚拟主机、W3 Total Cache 兼容、WordPress URL 重写、多语言域名统一跳转、前台 AJAX 接口保护,以及最终用于生产环境的配置文件。
一、最终架构
迁移完成后的访问结构如下:
www.shuijingwanwq.com
↓
EdgeOne
↓
WordPress 中文前台
en.shuijingwanwq.com
↓
Cloudflare
↓
WordPress 英文前台
media.shuijingwanwq.com
↓
Cloudflare
↓
wp-content/uploads
admin.shuijingwanwq.com
↓
Cloudflare DNS Only
不经过 EdgeOne 或 Cloudflare 代理
↓
独立 Nginx 虚拟主机
↓
PHP-FPM / WordPress 后台
需要特别说明的是:
admin 使用独立 Nginx 虚拟主机
但不复制第二套 WordPress 程序
admin、www 和 en 最终仍然共用同一个 WordPress 目录:
/data/wwwroot/www.shuijingwanwq.com
这是因为后台运行仍然需要访问同一套:
wp-admin
wp-includes
wp-content
wp-login.php
wp-config.php
独立虚拟主机负责的是访问入口、证书、日志和安全策略,而不是复制应用程序。
二、为什么不直接修改 WordPress 的 siteurl
WordPress 数据库中的地址仍然保持不变:
home:
https://www.shuijingwanwq.com
siteurl:
https://www.shuijingwanwq.com
没有将 siteurl 修改成:
https://admin.shuijingwanwq.com
原因是直接修改 siteurl 可能同时影响:
- 中文前台资源地址;
- Polylang 多域名识别;
- REST API;
- 插件和主题资源;
- W3 Total Cache 缓存键;
- WP-Cron;
- Yoast SEO;
- 登录和退出跳转;
- 英文站资源地址。
本次方案采用的是:
Nginx
负责前台域名下旧后台入口的统一跳转
WordPress MU 插件
负责 WordPress 自己生成的后台 URL 和资源地址
数据库 home/siteurl
保持原值不变
三、创建 admin DNS 记录
在 Cloudflare 中添加:
类型:A
名称:admin
IPv4 地址:源站公网 IP
代理状态:仅 DNS
TTL:自动
必须保持灰色云朵,也就是:
DNS Only
否则后台请求仍然会经过 Cloudflare 代理,无法达到直接访问源站的目的。
服务器端解析验证:
getent ahostsv4 admin.shuijingwanwq.com | awk 'NR==1{print $1}'
返回源站公网 IP 后,说明解析已经生效。
四、使用 OneinStack 创建独立虚拟主机
我使用 OneinStack 自带的 vhost.sh 创建虚拟主机:
/root/oneinstack/vhost.sh
主要选项如下:
SSL 类型:
Use Let's Encrypt to Create SSL Certificate and Key
域名:
admin.shuijingwanwq.com
网站目录:
/data/wwwroot/www.shuijingwanwq.com
添加更多域名:
n
HTTP 跳转 HTTPS:
y
证书类型:
ec-256
防盗链:
n
Rewrite:
y
Rewrite 类型:
wordpress
访问日志:
y
这里直接将网站目录设置为现有 WordPress 目录:
/data/wwwroot/www.shuijingwanwq.com
OneinStack 最终创建了:
Nginx 配置:
/usr/local/nginx/conf/vhost/admin.shuijingwanwq.com.conf
证书:
/usr/local/nginx/conf/ssl/admin.shuijingwanwq.com.crt
私钥:
/usr/local/nginx/conf/ssl/admin.shuijingwanwq.com.key
访问日志:
/data/wwwlogs/admin.shuijingwanwq.com_nginx.log

五、第一次测试:WordPress 自动跳回 www
虚拟主机建立完成后,我直接测试:
for u in \
"http://admin.shuijingwanwq.com/" \
"https://admin.shuijingwanwq.com/" \
"https://admin.shuijingwanwq.com/wp-login.php" \
"https://admin.shuijingwanwq.com/wp-admin/"
do
echo
echo "========== $u =========="
curl -sS -o /dev/null -D - \
--max-time 20 \
--resolve admin.shuijingwanwq.com:80:127.0.0.1 \
--resolve admin.shuijingwanwq.com:443:127.0.0.1 \
"$u" \
| grep -Ei \
'HTTP/|location:|set-cookie:|x-redirect-by:|content-type:|server:'
done
结果是:
http://admin.../
→ 301 到 https://admin.../
https://admin.../
→ 301 到 https://www.shuijingwanwq.com/
https://admin.../wp-login.php
→ 301 到 https://www.shuijingwanwq.com/wp-login.php
https://admin.../wp-admin/
→ 301 到 https://www.shuijingwanwq.com/wp-admin/
响应中显示:
x-redirect-by: WordPress
但这并不是 WordPress 核心单独造成的。
此前英文站从 /en/ 迁移到独立域名时,也遇到过相同问题。最终确认是 W3 Total Cache Page Cache 的:
redirect_on_foreign_domain
将不在允许列表中的 Host 重新跳回主站域名。
六、让 W3 Total Cache 放行 admin 域名
此前已经存在一个 MU 插件:
wp-content/mu-plugins/w3tc-polylang-domain-fix.php
它会从 Polylang 配置中读取语言域名,并移除 W3TC 的跨域名跳转。
由于 admin.shuijingwanwq.com 不是 Polylang 语言域名,因此需要将它明确加入允许列表。
最终完整文件如下。
wp-content/mu-plugins/w3tc-polylang-domain-fix.php
<?php
/*
Plugin Name: W3TC Polylang Domain Fix
Description: Prevents W3 Total Cache Page Cache from redirecting valid Polylang language domains back to the main home_url.
*/
add_action('init', function () {
$host = $_SERVER['HTTP_HOST'] ?? '';
if ($host === '') {
return;
}
$polylang = get_option('polylang');
$domains = $polylang['domains'] ?? array();
$allowed_hosts = array(
'admin.shuijingwanwq.com',
);
foreach ((array) $domains as $domain) {
if (!empty($domain)) {
$parsed = wp_parse_url($domain);
if (!empty($parsed['host'])) {
$allowed_hosts[] = strtolower($parsed['host']);
}
}
}
if (!in_array(strtolower($host), $allowed_hosts, true)) {
return;
}
global $wp_filter;
if (empty($wp_filter['init']) || empty($wp_filter['init']->callbacks)) {
return;
}
foreach ($wp_filter['init']->callbacks as $priority => $callbacks) {
foreach ($callbacks as $callback) {
$function = $callback['function'] ?? null;
if (
is_array($function)
&& isset($function[0], $function[1])
&& is_object($function[0])
&& $function[1] === 'redirect_on_foreign_domain'
&& get_class($function[0]) === 'W3TC\PgCache_Plugin'
) {
remove_action('init', $function, $priority);
}
}
}
}, 0);
修改后再次测试:
https://admin.shuijingwanwq.com/
→ 200
https://admin.shuijingwanwq.com/wp-login.php
→ 200
W3TC 的跨域名跳转已经解除。
七、WordPress 生成的登录地址仍然指向 www
W3TC 问题解决后,登录页面虽然已经能通过 admin 打开,但登录表单仍然是:
<form
name="loginform"
id="loginform"
action="https://www.shuijingwanwq.com/wp-login.php"
method="post"
>
忘记密码链接也指向:
https://www.shuijingwanwq.com/wp-login.php?action=lostpassword
未登录访问:
https://admin.shuijingwanwq.com/wp-admin/
则会跳转到:
https://www.shuijingwanwq.com/wp-login.php
这部分需要通过 WordPress URL 过滤器解决。
八、创建后台域名兼容 MU 插件
我创建了:
wp-content/mu-plugins/swq-admin-domain.php
这个插件负责:
- 将后台登录地址改为
admin; - 将后台管理链接改为
admin; - 将后台 CSS、JavaScript 和字体改为
admin; - 将后台 REST API 改为
admin; - 将后台插件和主题资源改为
admin; - 从 Polylang 配置中动态读取未来新增的语言域名;
- 保留前台自己的
admin-ajax.php; - 保留前台自己的
admin-post.php; - 保留密码保护文章的
postpass提交地址; - 保持上传文件继续使用
media.shuijingwanwq.com。
以下是最终整理后的完整版本。它与当前生产环境中已经通过测试的逻辑一致,仅对注释和排版进行了整理。
wp-content/mu-plugins/swq-admin-domain.php
<?php
/*
Plugin Name: SWQ Admin Domain
Description: Routes WordPress login, administration resources and backend APIs through admin.shuijingwanwq.com without changing the database home or siteurl options.
Version: 1.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! defined( 'SWQ_ADMIN_BASE_URL' ) ) {
define( 'SWQ_ADMIN_BASE_URL', 'https://admin.shuijingwanwq.com' );
}
if ( ! defined( 'SWQ_ADMIN_HOST' ) ) {
define( 'SWQ_ADMIN_HOST', 'admin.shuijingwanwq.com' );
}
/**
* Normalize an HTTP host for comparison.
*/
function swq_admin_domain_normalize_host( $host ) {
$host = strtolower( trim( (string) $host ) );
$host = preg_replace( '/:\d+$/', '', $host );
return rtrim( $host, '.' );
}
/**
* Return all WordPress frontend and administration hosts.
*
* Future Polylang language domains are read automatically from the
* Polylang option after they have been configured in WordPress.
*/
function swq_admin_domain_get_managed_hosts() {
static $hosts = null;
if ( $hosts !== null ) {
return $hosts;
}
$hosts = array(
'shuijingwanwq.com' => true,
'www.shuijingwanwq.com' => true,
SWQ_ADMIN_HOST => true,
);
foreach ( array( 'home', 'siteurl' ) as $option_name ) {
$option_url = get_option( $option_name );
if ( is_string( $option_url ) && $option_url !== '' ) {
$option_host = wp_parse_url(
$option_url,
PHP_URL_HOST
);
$option_host = swq_admin_domain_normalize_host(
$option_host
);
if ( $option_host !== '' ) {
$hosts[ $option_host ] = true;
}
}
}
$polylang = get_option( 'polylang' );
$domains = is_array( $polylang )
? ( $polylang['domains'] ?? array() )
: array();
foreach ( (array) $domains as $domain ) {
if ( ! is_string( $domain ) || $domain === '' ) {
continue;
}
$domain_host = wp_parse_url(
$domain,
PHP_URL_HOST
);
$domain_host = swq_admin_domain_normalize_host(
$domain_host
);
if ( $domain_host !== '' ) {
$hosts[ $domain_host ] = true;
}
}
/*
* The uploads hostname is intentionally independent and must not be
* rewritten to the administration hostname.
*/
unset( $hosts['media.shuijingwanwq.com'] );
return $hosts;
}
/**
* Determine whether a host belongs to the WordPress/Polylang installation.
*/
function swq_admin_domain_is_managed_host( $host ) {
$host = swq_admin_domain_normalize_host( $host );
$hosts = swq_admin_domain_get_managed_hosts();
return $host !== '' && isset( $hosts[ $host ] );
}
/**
* Replace the origin of a managed WordPress URL while preserving its
* path, query string and fragment.
*/
function swq_admin_domain_replace_origin( $url ) {
if ( ! is_string( $url ) || $url === '' ) {
return $url;
}
$parts = wp_parse_url( $url );
if (
! is_array( $parts )
|| empty( $parts['host'] )
|| ! swq_admin_domain_is_managed_host( $parts['host'] )
) {
return $url;
}
$port = isset( $parts['port'] )
? ':' . (int) $parts['port']
: '';
$pattern = '#^https?://'
. preg_quote( $parts['host'], '#' )
. preg_quote( $port, '#' )
. '(?=/|$)#i';
return preg_replace(
$pattern,
SWQ_ADMIN_BASE_URL,
$url,
1
);
}
/**
* Return the path portion of a relative WordPress URL.
*/
function swq_admin_domain_get_path_only( $path ) {
$path = ltrim( (string) $path, '/' );
$position = strpos( $path, '?' );
if ( $position !== false ) {
$path = substr( $path, 0, $position );
}
return $path;
}
/**
* Return the action query argument from a relative WordPress URL.
*/
function swq_admin_domain_get_url_action( $path ) {
$path = (string) $path;
$position = strpos( $path, '?' );
if ( $position === false ) {
return '';
}
$query = array();
parse_str(
substr( $path, $position + 1 ),
$query
);
return strtolower(
(string) ( $query['action'] ?? '' )
);
}
/**
* Determine whether a backend-looking path is also a public frontend
* endpoint and therefore must remain on the frontend hostname.
*/
function swq_admin_domain_is_public_frontend_endpoint( $path ) {
$path_only = swq_admin_domain_get_path_only( $path );
if (
in_array(
$path_only,
array(
'wp-admin/admin-ajax.php',
'wp-admin/admin-post.php',
),
true
)
) {
return true;
}
return (
$path_only === 'wp-login.php'
&& swq_admin_domain_get_url_action( $path ) === 'postpass'
);
}
/**
* Determine whether a site URL belongs to login or administration.
*/
function swq_admin_domain_is_backend_url(
$path,
$scheme = null
) {
if ( swq_admin_domain_is_public_frontend_endpoint( $path ) ) {
return false;
}
$path_only = swq_admin_domain_get_path_only( $path );
if (
in_array(
$scheme,
array(
'login',
'login_post',
'admin',
),
true
)
) {
return true;
}
return (
$path_only === 'wp-login.php'
|| $path_only === 'wp-admin'
|| strpos( $path_only, 'wp-admin/' ) === 0
);
}
/**
* Determine whether the current request uses the dedicated admin host.
*/
function swq_admin_domain_is_current_request() {
$host = swq_admin_domain_normalize_host(
$_SERVER['HTTP_HOST'] ?? ''
);
return $host === SWQ_ADMIN_HOST;
}
/**
* Filter site_url().
*
* On the admin host, WordPress application URLs must use the admin host.
* On frontend hosts, only true login and management URLs are rewritten.
*/
function swq_admin_domain_filter_site_url(
$url,
$path,
$scheme,
$blog_id
) {
if (
swq_admin_domain_is_current_request()
|| swq_admin_domain_is_backend_url(
$path,
$scheme
)
) {
return swq_admin_domain_replace_origin( $url );
}
return $url;
}
add_filter(
'site_url',
'swq_admin_domain_filter_site_url',
999,
4
);
/**
* Filter network_site_url(), including lost-password URLs.
*/
function swq_admin_domain_filter_network_site_url(
$url,
$path,
$scheme
) {
if (
swq_admin_domain_is_current_request()
|| swq_admin_domain_is_backend_url(
$path,
$scheme
)
) {
return swq_admin_domain_replace_origin( $url );
}
return $url;
}
add_filter(
'network_site_url',
'swq_admin_domain_filter_network_site_url',
999,
3
);
/**
* Replace a generated URL origin with the current managed frontend host.
*/
function swq_admin_domain_replace_origin_with_current_host( $url ) {
if ( ! is_string( $url ) || $url === '' ) {
return $url;
}
$host = swq_admin_domain_normalize_host(
$_SERVER['HTTP_HOST'] ?? ''
);
if (
$host === ''
|| $host === SWQ_ADMIN_HOST
|| ! swq_admin_domain_is_managed_host( $host )
) {
return $url;
}
$parts = wp_parse_url( $url );
if (
! is_array( $parts )
|| empty( $parts['host'] )
) {
return $url;
}
$port = isset( $parts['port'] )
? ':' . (int) $parts['port']
: '';
$pattern = '#^https?://'
. preg_quote( $parts['host'], '#' )
. preg_quote( $port, '#' )
. '(?=/|$)#i';
$origin = ( is_ssl() ? 'https' : 'http' )
. '://'
. $host;
return preg_replace(
$pattern,
$origin,
$url,
1
);
}
/**
* Filter ordinary administration URLs.
*
* Public AJAX and form endpoints remain on the current frontend host.
* Inside the dedicated admin host they use the admin hostname.
*/
function swq_admin_domain_filter_admin_url(
$url,
$path = '',
$blog_id = null,
$scheme = null
) {
$path_only = swq_admin_domain_get_path_only( $path );
if (
! swq_admin_domain_is_current_request()
&& in_array(
$path_only,
array(
'admin-ajax.php',
'admin-post.php',
),
true
)
) {
return swq_admin_domain_replace_origin_with_current_host(
$url
);
}
return swq_admin_domain_replace_origin( $url );
}
add_filter(
'admin_url',
'swq_admin_domain_filter_admin_url',
999,
4
);
/**
* Filter network administration URLs.
*/
function swq_admin_domain_filter_network_admin_url(
$url,
$path = '',
$scheme = null
) {
return swq_admin_domain_replace_origin( $url );
}
add_filter(
'network_admin_url',
'swq_admin_domain_filter_network_admin_url',
999,
3
);
/**
* Authentication URLs.
*/
add_filter(
'login_url',
'swq_admin_domain_replace_origin',
999
);
add_filter(
'lostpassword_url',
'swq_admin_domain_replace_origin',
999
);
add_filter(
'logout_url',
'swq_admin_domain_replace_origin',
999
);
add_filter(
'register_url',
'swq_admin_domain_replace_origin',
999
);
/**
* Rewrite final CSS and JavaScript URLs only while rendering admin.
*/
function swq_admin_domain_filter_asset_src(
$src,
$handle = ''
) {
if ( ! swq_admin_domain_is_current_request() ) {
return $src;
}
return swq_admin_domain_replace_origin( $src );
}
add_filter(
'style_loader_src',
'swq_admin_domain_filter_asset_src',
999,
2
);
add_filter(
'script_loader_src',
'swq_admin_domain_filter_asset_src',
999,
2
);
/**
* Keep REST API requests generated inside wp-admin off the frontend CDN.
*/
function swq_admin_domain_filter_rest_url(
$url,
$path,
$blog_id,
$scheme
) {
if ( ! swq_admin_domain_is_current_request() ) {
return $url;
}
return swq_admin_domain_replace_origin( $url );
}
add_filter(
'rest_url',
'swq_admin_domain_filter_rest_url',
999,
4
);
/**
* Rewrite WordPress-generated plugin, content, include and theme
* resources while serving the dedicated administration hostname.
*/
function swq_admin_domain_filter_generated_resource_url( $url ) {
if ( ! swq_admin_domain_is_current_request() ) {
return $url;
}
return swq_admin_domain_replace_origin( $url );
}
add_filter(
'plugins_url',
'swq_admin_domain_filter_generated_resource_url',
999
);
add_filter(
'content_url',
'swq_admin_domain_filter_generated_resource_url',
999
);
add_filter(
'includes_url',
'swq_admin_domain_filter_generated_resource_url',
999
);
add_filter(
'theme_root_uri',
'swq_admin_domain_filter_generated_resource_url',
999
);
add_filter(
'template_directory_uri',
'swq_admin_domain_filter_generated_resource_url',
999
);
add_filter(
'stylesheet_directory_uri',
'swq_admin_domain_filter_generated_resource_url',
999
);
add_filter(
'theme_file_uri',
'swq_admin_domain_filter_generated_resource_url',
999
);
add_filter(
'parent_theme_file_uri',
'swq_admin_domain_filter_generated_resource_url',
999
);
保存后执行语法检查:
php -l wp-content/mu-plugins/swq-admin-domain.php
返回:
No syntax errors detected in wp-content/mu-plugins/swq-admin-domain.php
服务器端最终写入和语法检查记录也已经保留。
九、后台资源最初仍然经过 www
登录进入 admin 后,页面地址虽然已经保持为:
https://admin.shuijingwanwq.com/wp-admin/
但浏览器开发者工具显示,许多后台资源仍然从:
www.shuijingwanwq.com
加载。
其中包括:
load-styles.php;- 插件 CSS;
- 插件 JavaScript;
- 字体文件;
wp-content/plugins资源;- 部分主题资源。

由于后台页面本身位于 admin 域名,这些跨域字体和脚本还触发了:
CORS Missing Allow Origin
正确的处理方式不是在 www 上开放 CORS,而是让后台资源本身也通过 admin 加载。
MU 插件最终补充了这些过滤器:
site_url
network_site_url
admin_url
network_admin_url
login_url
lostpassword_url
logout_url
register_url
style_loader_src
script_loader_src
rest_url
plugins_url
content_url
includes_url
theme_root_uri
template_directory_uri
stylesheet_directory_uri
theme_file_uri
parent_theme_file_uri
完成后,在开发者工具中筛选:
www.shuijingwanwq.com
和:
en.shuijingwanwq.com
均显示:
没有请求

十、统一所有前台域名的后台入口
仅仅让 admin 本身正常工作还不够。
原来的前台域名仍然可以访问:
https://www.shuijingwanwq.com/wp-login.php
https://www.shuijingwanwq.com/wp-admin/
https://en.shuijingwanwq.com/wp-login.php
https://en.shuijingwanwq.com/wp-admin/
此前 Nginx 中还存在一段旧规则:
# Redirect non-www admin/login requests to the main www domain.
它会将 en 和裸域的后台请求先跳转到 www。
既然后台已经统一迁移到 admin,这段逻辑就应该替换为:
所有前台语言域名的后台管理入口
↓
admin.shuijingwanwq.com
同时必须保留三个前台接口:
/wp-admin/admin-ajax.php
/wp-admin/admin-post.php
/wp-login.php?action=postpass
原因是:
admin-ajax.php可能被前台主题或插件调用;admin-post.php可能承担前台表单提交;postpass用于密码保护文章,Cookie 应保留在当前前台域名。
十一、后台跳转 map 配置
新建:
/usr/local/nginx/conf/include/shuijingwan_admin_redirect_map.conf
完整内容如下。
shuijingwan_admin_redirect_map.conf
# 所有前台语言域名共用的 WordPress 后台入口判断。
#
# 以下前台接口保留在原域名:
# - /wp-admin/admin-ajax.php
# - /wp-admin/admin-post.php
# - /wp-login.php?action=postpass
map "$uri|$arg_action" $swq_frontend_admin_redirect {
default 0;
# 前台 AJAX 与表单接口。
~*^/wp-admin/admin-(?:ajax|post)\.php\| 0;
# 密码保护文章提交接口。
~*^/wp-login\.php\|postpass$ 0;
# 其他登录和后台管理入口。
~*^/wp-login\.php\| 1;
~*^/wp-admin(?:/.*)?\| 1;
}
这里使用 map,可以让路径判断保持清晰,也方便以后扩展新的例外接口。
十二、server 上下文跳转配置
新建:
/usr/local/nginx/conf/include/shuijingwan_admin_redirect_server.conf
完整内容如下。
shuijingwan_admin_redirect_server.conf
# 将前台语言域名下的登录和后台管理入口统一转到独立后台域名。
# 307 可保留旧登录表单 POST 请求的方法与正文。
if ($swq_frontend_admin_redirect = 1) {
return 307 https://admin.shuijingwanwq.com$request_uri;
}
这里使用的是:
307 Temporary Redirect
而不是 301 或普通 302。
主要原因是,假如用户仍然从旧登录页面提交 POST 表单,307 可以保留原来的请求方法和正文,避免用户名、密码或其他表单内容在跳转过程中丢失。
十三、最终 www 虚拟主机配置
当前中文、英文和裸域仍然共用:
/usr/local/nginx/conf/vhost/www.shuijingwanwq.com.conf
以下是最终完整内容。
其中原有的 3,900 多行标签映射仍然保存在独立的:
shuijingwan_nginx_redirect.conf
中,因此没有必要在本文重复展开这些与后台迁移无直接关系的站点专用规则。
/usr/local/nginx/conf/vhost/www.shuijingwanwq.com.conf
# 引入生成的 map 规则(位于 server 外部,属于 http 上下文)
include /usr/local/nginx/conf/include/shuijingwan_nginx_redirect.conf;
# 引入 en.shuijingwanwq.com 独立旧路径规则
include /usr/local/nginx/conf/include/shuijingwan_en_redirect.conf;
# 引入所有前台语言域名共用的后台入口判断
include /usr/local/nginx/conf/include/shuijingwan_admin_redirect_map.conf;
server {
limit_req zone=site_limit burst=50 nodelay;
limit_conn conn_limit 50;
limit_req_status 429;
listen 80;
listen [::]:80;
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl_certificate /usr/local/nginx/conf/ssl/www.shuijingwanwq.com.crt;
ssl_certificate_key /usr/local/nginx/conf/ssl/www.shuijingwanwq.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256;
ssl_conf_command Ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
ssl_conf_command Options PrioritizeChaCha;
ssl_prefer_server_ciphers on;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_buffer_size 2k;
add_header Strict-Transport-Security max-age=15768000;
ssl_stapling on;
ssl_stapling_verify on;
server_name
www.shuijingwanwq.com
shuijingwanwq.com
en.shuijingwanwq.com;
access_log /data/wwwlogs/www.shuijingwanwq.com_nginx.log combined;
index index.html index.htm index.php;
root /data/wwwroot/www.shuijingwanwq.com;
if ($ssl_protocol = "") {
return 301 https://$host$request_uri;
}
# 所有前台语言域名共用的后台入口跳转
include /usr/local/nginx/conf/include/shuijingwan_admin_redirect_server.conf;
if ($host = shuijingwanwq.com) {
return 301 $scheme://www.shuijingwanwq.com$request_uri;
}
include /usr/local/nginx/conf/rewrite/wordpress.conf;
#error_page 404 /404.html;
#error_page 502 /502.html;
location ~ .*\.(wma|wmv|asf|mp3|mmf|zip|rar|jpg|gif|png|swf|flv|mp4)$ {
valid_referers
none
blocked
*.shuijingwanwq.com
www.shuijingwanwq.com
shuijingwanwq.com;
if ($invalid_referer) {
return 403;
}
}
# 批量重定向所有 /amp 后缀链接至原规范页面
location ~* ^(.+)/amp/?$ {
return 301 $1/;
}
# 旧标签、旧专题先映射,再一次跳转到最终中文或英文域名
if ($www_final_redirect_uri != "") {
return 301 $www_final_redirect_uri$is_args$args;
}
# 未命中旧规则的普通 /en/ 页面迁移到英文子域名
if ($www_en_migration_uri != "") {
return 301 $www_en_migration_uri$is_args$args;
}
# en 子域名自己的旧标签、旧专题路径跳转
if ($en_final_redirect_uri != "") {
return 301 $en_final_redirect_uri$is_args$args;
}
location ~ [^/]\.php(/|$) {
#fastcgi_pass remote_php_ip:9000;
fastcgi_pass unix:/dev/shm/php-cgi.sock;
fastcgi_index index.php;
fastcgi_read_timeout 150s;
include fastcgi.conf;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ {
expires 30d;
access_log off;
}
location ~ .*\.(js|css)?$ {
expires 7d;
access_log off;
}
location ~ /(\.user\.ini|\.ht|\.git|\.svn|\.project|LICENSE|README\.md) {
deny all;
}
location /.well-known {
allow all;
}
}
十四、最终 admin 虚拟主机配置
admin 使用独立证书、日志和虚拟主机,但与主站共用 WordPress 目录。
为了避免搜索引擎将 admin 域名下可能访问到的前台页面作为重复内容收录,我在 Nginx 中增加了:
X-Robots-Tag: noindex, nofollow, noarchive
/usr/local/nginx/conf/vhost/admin.shuijingwanwq.com.conf
server {
listen 80;
listen [::]:80;
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl_certificate /usr/local/nginx/conf/ssl/admin.shuijingwanwq.com.crt;
ssl_certificate_key /usr/local/nginx/conf/ssl/admin.shuijingwanwq.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256;
ssl_conf_command Ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
ssl_conf_command Options PrioritizeChaCha;
ssl_prefer_server_ciphers on;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_buffer_size 2k;
add_header Strict-Transport-Security max-age=15768000;
ssl_stapling on;
ssl_stapling_verify on;
server_name admin.shuijingwanwq.com;
add_header X-Robots-Tag "noindex, nofollow, noarchive" always;
access_log /data/wwwlogs/admin.shuijingwanwq.com_nginx.log combined;
index index.html index.htm index.php;
root /data/wwwroot/www.shuijingwanwq.com;
if ($ssl_protocol = "") {
return 301 https://$host$request_uri;
}
include /usr/local/nginx/conf/rewrite/wordpress.conf;
#error_page 404 /404.html;
#error_page 502 /502.html;
location ~ [^/]\.php(/|$) {
#fastcgi_pass remote_php_ip:9000;
fastcgi_pass unix:/dev/shm/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ {
expires 30d;
access_log off;
}
location ~ .*\.(js|css)?$ {
expires 7d;
access_log off;
}
location ~ /(\.user\.ini|\.ht|\.git|\.svn|\.project|LICENSE|README\.md) {
deny all;
}
location /.well-known {
allow all;
}
}
配置检测和重载:
/usr/local/nginx/sbin/nginx -t
/usr/local/nginx/sbin/nginx -s reload
响应头验证:
curl -sS -o /dev/null -D - \
--resolve admin.shuijingwanwq.com:443:127.0.0.1 \
"https://admin.shuijingwanwq.com/" \
| grep -Ei \
'HTTP/|x-robots-tag:|strict-transport-security:'
返回:
HTTP/2 200
strict-transport-security: max-age=15768000
x-robots-tag: noindex, nofollow, noarchive
十五、前台接口与后台入口验证
源站直连测试:
for host in \
"www.shuijingwanwq.com" \
"en.shuijingwanwq.com" \
"shuijingwanwq.com"
do
for path in \
"/wp-login.php" \
"/wp-admin/" \
"/wp-admin/update-core.php" \
"/wp-admin/admin-ajax.php" \
"/wp-admin/admin-post.php" \
"/wp-login.php?action=postpass"
do
echo
echo "========== https://$host$path =========="
curl -sS -o /dev/null -D - \
--max-time 20 \
--resolve "$host:443:127.0.0.1" \
"https://$host$path" \
| grep -Ei \
'HTTP/|location:|x-redirect-by:|set-cookie:'
done
done
最终结果:
www / en / 裸域:
/wp-login.php
→ 307 到 admin
/wp-admin/
→ 307 到 admin
/wp-admin/update-core.php
→ 307 到 admin
/wp-admin/admin-ajax.php
→ 保留在当前前台域名
/wp-admin/admin-post.php
→ 保留在当前前台域名
/wp-login.php?action=postpass
→ 保留在当前前台域名
裸域的公共接口继续先规范化到 www,属于原有裸域跳转行为。
十六、EdgeOne 和 Cloudflare 公网验证
不使用 --resolve,直接测试公网 CDN 链路:
for u in \
"https://www.shuijingwanwq.com/wp-login.php" \
"https://www.shuijingwanwq.com/wp-admin/" \
"https://www.shuijingwanwq.com/wp-admin/admin-ajax.php" \
"https://en.shuijingwanwq.com/wp-login.php" \
"https://en.shuijingwanwq.com/wp-admin/" \
"https://en.shuijingwanwq.com/wp-admin/admin-ajax.php"
do
echo
echo "========== $u =========="
curl -sS -o /dev/null -D - \
--max-time 20 \
"$u?swq_test=$(date +%s)" \
| grep -Ei \
'HTTP/|location:|server:|eo-cache-status:|cf-cache-status:'
done
EdgeOne 返回:
wp-login.php:
HTTP/2 307
location: https://admin.shuijingwanwq.com/wp-login.php
eo-cache-status: MISS
wp-admin/:
HTTP/2 307
location: https://admin.shuijingwanwq.com/wp-admin/
eo-cache-status: MISS
admin-ajax.php:
HTTP/2 400
eo-cache-status: MISS
Cloudflare 返回:
wp-login.php:
HTTP/2 307
location: https://admin.shuijingwanwq.com/wp-login.php
cf-cache-status: DYNAMIC
wp-admin/:
HTTP/2 307
location: https://admin.shuijingwanwq.com/wp-admin/
cf-cache-status: DYNAMIC
admin-ajax.php:
HTTP/2 400
cf-cache-status: DYNAMIC
admin-ajax.php 在没有携带 action 参数时返回 400,属于 WordPress 的正常响应。
十七、运行时 URL 验证
通过 PHP 分别模拟 www、en 和 admin Host:
cd /data/wwwroot/www.shuijingwanwq.com
for host in \
"www.shuijingwanwq.com" \
"en.shuijingwanwq.com" \
"admin.shuijingwanwq.com"
do
echo
echo "========== $host =========="
SWQ_TEST_HOST="$host" php -r '
$host = getenv("SWQ_TEST_HOST");
$_SERVER["HTTP_HOST"] = $host;
$_SERVER["SERVER_NAME"] = $host;
$_SERVER["HTTPS"] = "on";
$_SERVER["SERVER_PORT"] = "443";
$_SERVER["REQUEST_URI"] = "/";
require "wp-load.php";
echo "admin_url : " . admin_url() . "\n";
echo "admin-ajax : " . admin_url("admin-ajax.php") . "\n";
echo "admin-post : " . admin_url("admin-post.php") . "\n";
echo "login_url : " . wp_login_url() . "\n";
echo "rest_url : " . rest_url() . "\n";
'
done
最终输出为:
www.shuijingwanwq.com
admin_url:
https://admin.shuijingwanwq.com/wp-admin/
admin-ajax:
https://www.shuijingwanwq.com/wp-admin/admin-ajax.php
admin-post:
https://www.shuijingwanwq.com/wp-admin/admin-post.php
login_url:
https://admin.shuijingwanwq.com/wp-login.php
rest_url:
https://www.shuijingwanwq.com/wp-json/
en.shuijingwanwq.com
admin_url:
https://admin.shuijingwanwq.com/wp-admin/
admin-ajax:
https://en.shuijingwanwq.com/wp-admin/admin-ajax.php
admin-post:
https://en.shuijingwanwq.com/wp-admin/admin-post.php
login_url:
https://admin.shuijingwanwq.com/wp-login.php
rest_url:
https://en.shuijingwanwq.com/wp-json/
admin.shuijingwanwq.com
admin_url:
https://admin.shuijingwanwq.com/wp-admin/
admin-ajax:
https://admin.shuijingwanwq.com/wp-admin/admin-ajax.php
admin-post:
https://admin.shuijingwanwq.com/wp-admin/admin-post.php
login_url:
https://admin.shuijingwanwq.com/wp-login.php
rest_url:
https://admin.shuijingwanwq.com/wp-json/
这说明前台接口、语言域名 REST API 和后台管理地址已经正确分离。
十八、通过 admin 成功升级 WordPress
迁移完成后,我在:
https://admin.shuijingwanwq.com/wp-admin/
重新执行 WordPress 核心升级。
此次升级顺利完成,没有再次出现 EdgeOne 524,也没有卡在升级页面。
最终版本:
WordPress 7.0.1

服务器端检查:
cd /data/wwwroot/www.shuijingwanwq.com
php -r '
require "wp-includes/version.php";
echo "WordPress 版本:{$wp_version}\n";
echo "维护模式文件:";
echo file_exists(".maintenance")
? "存在\n"
: "不存在\n";
$_SERVER["HTTP_HOST"] = "admin.shuijingwanwq.com";
$_SERVER["HTTPS"] = "on";
require "wp-load.php";
echo "core_updater.lock:";
echo get_option("core_updater.lock") === false
? "不存在\n"
: "仍然存在\n";
'
输出:
WordPress 版本:7.0.1
维护模式文件:不存在
core_updater.lock:不存在
这次升级结果实际证明:此前的失败主要与后台请求经过 CDN 链路有关,而不是 WordPress 文件权限、核心文件或服务器环境本身损坏。
十九、CDN 缓存规则是否需要修改
这次迁移完成后,我没有继续修改 EdgeOne 或 Cloudflare 的缓存规则。
现有规则已经能够保证:
/wp-login.php
/wp-admin/*
/wp-admin/admin-ajax.php
/wp-admin/admin-post.php
/wp-json/*
不被页面缓存。
公网验证中也已经看到:
EdgeOne:
eo-cache-status: MISS
Cloudflare:
cf-cache-status: DYNAMIC
因此,没有必要再在 EdgeOne 和 Cloudflare 中分别建立后台跳转。
后台入口统一跳转继续由 Nginx 负责,优点是:
- EdgeOne 和 Cloudflare 使用同一套源站逻辑;
- 不会出现两套 CDN 规则不一致;
- 可以精确保留
admin-ajax.php; - 可以精确保留
admin-post.php; - 可以精确保留
postpass; - 未来新增语言域名后可以自动继承。
admin.shuijingwanwq.com 本身使用 DNS Only,不经过 CDN,因此也不需要单独配置缓存规则。
二十、未来新增语言域名时如何处理
当前 Nginx 后台跳转规则并没有写死 www 或 en。
只要未来新增的语言域名:
- 配置 DNS;
- 加入服务器证书;
- 加入当前前台虚拟主机的
server_name; - 在 Polylang 中配置为语言域名;
就可以自动继承:
/wp-login.php
/wp-admin/*
↓
admin.shuijingwanwq.com
PHP MU 插件也会从:
get_option( 'polylang' )
动态读取 Polylang 的 domains 配置,因此不需要再手动添加:
fr.shuijingwanwq.com
de.shuijingwanwq.com
es.shuijingwanwq.com
这比在 PHP 中写死:
(?:www|en|admin)
更适合长期维护。
二十一、这次迁移解决了什么
最终已经确认:
admin使用独立 Nginx 虚拟主机;admin使用独立 ECC HTTPS 证书;admin通过 DNS Only 直连源站;- 后台页面不再经过 EdgeOne;
- 后台资源不再经过 EdgeOne;
- 后台资源不再经过英文站 Cloudflare;
- WordPress 登录、忘记密码和退出地址统一使用
admin; www、en和裸域后台入口统一跳到admin;- 前台 AJAX、表单和文章密码提交没有被误伤;
- Polylang 未来新增语言域名可动态兼容;
- W3 Total Cache 不再将
admin跳回www; admin已添加noindex, nofollow, noarchive;- WordPress 7.0.1 核心升级成功;
.maintenance不存在;core_updater.lock不存在;- 中文站、英文站和旧
/en/跳转均保持正常。
二十二、这次迁移没有解决什么
需要保持客观的是,这次后台迁移并不会直接消除 EdgeOne 每天约 2.9 GB 的流量。
后台访问通常只有管理员本人,产生的流量远小于前台访客、搜索引擎和静态资源请求。
因此,这次迁移的主要价值是:
提高后台稳定性
解决长请求超时
降低后台操作对 CDN 的依赖
减少一部分不必要的 EdgeOne 流量
建立清晰的前后台网络边界
后续如果要继续降低 EdgeOne 用量,仍然需要单独分析:
- 中文前台 HTML 流量;
- 非 uploads 静态文件;
- 字体文件;
- 爬虫请求;
- 标签页和分页;
- 缓存命中率;
- 高频访问来源;
- 是否还有资源可以迁移到其他静态域名。
二十三、总结
最开始,我只是希望解决一次 WordPress 核心升级失败的问题。
但真正排查后发现,后台迁移并不是简单添加一个 DNS 记录和 Nginx 虚拟主机就能完成。
完整链路还涉及:
DNS
HTTPS 证书
Nginx 虚拟主机
W3 Total Cache 跨域名跳转
WordPress site_url
WordPress admin_url
登录表单
后台静态资源
REST API
Polylang 多域名
前台 AJAX
前台表单
文章密码 Cookie
搜索引擎隔离
EdgeOne
Cloudflare
最终采用的职责划分是:
Nginx:
统一前台语言域名下的后台入口
MU 插件:
修正 WordPress 生成的后台 URL 和资源
W3TC 补丁:
放行合法的多域名 Host
CDN:
保持后台路径不缓存,不负责核心跳转
数据库:
home 和 siteurl 保持不变
完成这些调整后,WordPress 后台已经真正从前台 CDN 链路中独立出来。
而最有说服力的验证结果,并不是某一条配置检查通过,而是:
WordPress 7.0.1 已经通过
admin.shuijingwanwq.com
成功完成在线升级
此前经过 EdgeOne 时出现的 524 超时,没有再次发生。
Linux 服务器运维、部署与线上故障排查
如果你的网站或后端服务部署在 Linux 服务器上,遇到访问异常、Nginx 配置问题、MySQL / Redis 异常、Docker 服务不可用、磁盘占满、CPU / 内存过高等问题,可以联系我做一次远程排查。
适合以下场景:
✅ 网站打不开或访问不稳定
✅ Nginx / PHP-FPM 配置异常
✅ MySQL / Redis 性能或连接问题
✅ Docker 服务部署与维护
✅ 服务器迁移与环境配置
✅ CPU / 内存 / 磁盘异常排查
服务内容:
✅ Linux 环境检查
✅ 网站部署与迁移
✅ Nginx / PHP-FPM / MySQL / Redis 排查
✅ Docker 配置与维护
✅ 服务器性能分析
✅ 长期远程运维支持
如需咨询,请联系我,并注明:Linux 运维咨询。
联系方式:
Telegram:@shuijingwan
微信:13980074657
邮箱:shuijingwanwq@gmail.com


发表回复