最近我准备把 WordPress 英文站从路径形式:
https://www.shuijingwanwq.com/en/
迁移到子域名形式:
https://en.shuijingwanwq.com/
这次迁移的直接原因,是我最近观察 EdgeOne 流量数据时发现,主站流量增长已经明显超出之前的预期。
我当前购买的是 EdgeOne 个人版,套餐内包含 50 GB 流量。如果后续超过这个额度,就会进入按量计费。考虑到基础版月费较高,目前我不打算直接升级套餐,因此更倾向于通过域名拆分和 CDN 分流来控制成本。

因此,我开始考虑把英文站从主域名路径 /en/ 拆分到:
https://en.shuijingwanwq.com/
这样后续可以单独规划英文站的 CDN、缓存和流量策略,而不是让中文站、英文站、静态资源都继续集中消耗主域名的 EdgeOne 流量。
不过,在真正修改 Polylang 的语言 URL 设置之前,我先遇到了一个更基础的问题:
OneinStack 已有的 Nginx 虚拟主机,如何追加一个新域名,并让现有 SSL 证书同时覆盖这个新域名?
网上能搜到的一些做法,通常是先删除虚拟主机,再重新添加域名。但对于一个已经运行很久、并且手工改过不少 Nginx 规则的 WordPress 主站来说,我并不想走“删除再重建”的路线。
这篇文章记录的,就是这次在现有 OneinStack Nginx 虚拟主机中追加 en.shuijingwanwq.com,并重新签发包含多个域名的 SSL 证书的过程。
当前站点环境
当前主站是一个 OneinStack 部署的 WordPress 站点,Nginx 虚拟主机配置文件位于:
/usr/local/nginx/conf/vhost/www.shuijingwanwq.com.conf
主站根目录是:
/data/wwwroot/www.shuijingwanwq.com
已有域名包括:
www.shuijingwanwq.com
shuijingwanwq.com
现在希望追加:
en.shuijingwanwq.com
但这个子域名并不是一个独立站点,也不是一个静态资源域名。它仍然要进入同一个 WordPress 程序,由 Polylang 根据子域名识别英文语言。
也就是说,目标结构是:
www.shuijingwanwq.com
↓
同一个 WordPress 根目录
/data/wwwroot/www.shuijingwanwq.com
en.shuijingwanwq.com
↓
同一个 WordPress 根目录
/data/wwwroot/www.shuijingwanwq.com
这一点和之前的 media.shuijingwanwq.com 不一样。media 是静态资源域名,适合单独虚拟主机;而 en 是语言子域名,更适合进入同一个 WordPress 站点。

为什么没有使用删除再重建 vhost 的方式
OneinStack 的 vhost.sh 脚本确实支持在创建虚拟主机时添加更多域名。脚本里有类似这样的交互提示:
Do you want to add more domain name? [y/n]:

但从本机脚本输出看,这个逻辑主要发生在“创建虚拟主机”流程中。如果对应的 ${domain}.conf 已经存在,脚本会提示该虚拟主机已经存在,需要删除后再重新创建。
对于一个新站,这样做问题不大。
但对我现在这个主站来说,情况不一样。
如果先删除 vhost,再用 OneinStack 重建,理论上网站目录不会被删除,但这个过程仍然会影响线上站点的正常访问。现在我的主站已经有比较稳定的访问量,不能接受因为重建虚拟主机导致网站短时间不可访问、证书异常、跳转异常,或者 Nginx 配置被覆盖。
另外,我的主站配置文件已经不是完全默认模板,里面包含一些自定义 include、WordPress rewrite、AMP 跳转、tag 跳转、静态文件访问限制等规则。如果删除后再重建,后续还需要重新补回这些规则,操作成本和风险都会更高。
所以这次我没有删除虚拟主机,而是选择:
保留现有 vhost
只修改 server_name
调整主域名跳转规则
重新签发包含 en 子域名的 SSL 证书
第一步:备份现有 Nginx 虚拟主机配置
正式修改前,先备份当前配置文件:
cp /usr/local/nginx/conf/vhost/www.shuijingwanwq.com.conf /usr/local/nginx/conf/vhost/www.shuijingwanwq.com.conf.bak-$(date +%F-%H%M)
这是必要步骤。因为现有站点已经上线,任何 Nginx 配置改错,都需要能快速回滚。
第二步:在现有 server_name 中追加 en 子域名
原来的配置类似:
server_name www.shuijingwanwq.com shuijingwanwq.com;
修改为:
server_name www.shuijingwanwq.com shuijingwanwq.com en.shuijingwanwq.com;
这样,en.shuijingwanwq.com 就会进入同一个 Nginx server block,也就是同一个 WordPress 根目录。

第三步:调整裸域名跳转规则
原来配置中有一条规则:
if ($host != www.shuijingwanwq.com) { return 301 $scheme://www.shuijingwanwq.com$request_uri; }
这条规则在只有 www 和裸域名时没问题。它的作用是把非 www.shuijingwanwq.com 的请求都跳转到 www。
但现在要增加 en.shuijingwanwq.com,这条规则就不合适了。
如果不修改,访问:
https://en.shuijingwanwq.com/
会被 Nginx 直接 301 回:
https://www.shuijingwanwq.com/
因此,我把它改成只处理裸域名:
if ($host = shuijingwanwq.com) { return 301 $scheme://www.shuijingwanwq.com$request_uri; }
这样:
shuijingwanwq.com → www.shuijingwanwq.com
但:
en.shuijingwanwq.com
不会再被 Nginx 强制跳回 www。
第四步:检查并重载 Nginx
修改完成后,先检查配置语法:
nginx -t
输出正常:
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

然后重载 Nginx:
nginx -s reload
到这一步,Nginx 已经能够识别 en.shuijingwanwq.com 这个 Host。
不过这时还不能算完成,因为 SSL 证书还需要重新签发,让它同时包含 www.shuijingwanwq.com、shuijingwanwq.com 和 en.shuijingwanwq.com。
第五步:查看 acme.sh 中现有证书的域名记录
在重新签发证书前,我先查看了 OneinStack 当前环境中 acme.sh 对主站证书的记录:
grep -E "Le_Domain|Le_Alt|Le_Webroot|Le_RealCertPath|Le_RealCACertPath|Le_RealKeyPath|Le_ReloadCmd" /root/.acme.sh/www.shuijingwanwq.com/www.shuijingwanwq.com.conf
当时输出如下:
Le_Domain='www.shuijingwanwq.com'
Le_Alt='shuijingwanwq.com'
Le_Webroot='/data/wwwroot/www.shuijingwanwq.com'
Le_RealCertPath=''
Le_RealCACertPath=''
Le_RealKeyPath='/usr/local/nginx/conf/ssl/www.shuijingwanwq.com.key'
Le_ReloadCmd='__ACME_BASE64__START_L2Jpbi9zeXN0ZW1jdGwgcmVzdGFydCBuZ2lueA==__ACME_BASE64__END_'
从这里可以看到,当前主站证书记录中的主域名是:
www.shuijingwanwq.com
备用域名是:
shuijingwanwq.com
还没有包含:
en.shuijingwanwq.com
同时也能确认,当前证书对应的 Webroot 是:
/data/wwwroot/www.shuijingwanwq.com
这也正好符合我的目标:en.shuijingwanwq.com 不是新建独立站点,而是继续指向同一个 WordPress 根目录。

因此,下一步需要重新签发一个同时包含以下三个域名的证书:
www.shuijingwanwq.com
shuijingwanwq.com
en.shuijingwanwq.com
第六步:先添加 DNS 解析
签发证书前,需要先让 en.shuijingwanwq.com 解析到源站服务器。
这里我先添加了一条 DNS 记录:
主机记录:en
记录类型:A
记录值:源站服务器公网 IP
代理状态:暂时不走 CDN / 代理
这一步主要是为了让 ACME HTTP 验证能访问到源站。
服务器上可以用下面命令检查解析:
getent ahostsv4 en.shuijingwanwq.com | head -n 1
当时返回结果类似:
121.40.248.29 STREAM en.shuijingwanwq.com
如果返回的是源站公网 IP,说明服务器侧解析已经正常。

第七步:使用 acme.sh 重新签发多域名证书
OneinStack 当前环境中已经安装了 acme.sh,并且原有证书记录也在:
/root/.acme.sh/www.shuijingwanwq.com/
为了保持证书体系一致,我没有引入新的证书工具,而是继续使用已有的 acme.sh。
重新签发命令如下:
~/.acme.sh/acme.sh --force --issue -w /data/wwwroot/www.shuijingwanwq.com -d www.shuijingwanwq.com -d shuijingwanwq.com -d en.shuijingwanwq.com
签发过程中可以看到多域名信息:
Multi domain='DNS:www.shuijingwanwq.com,DNS:shuijingwanwq.com,DNS:en.shuijingwanwq.com'
三个域名都验证成功后,证书签发完成。
新证书生成在:
/root/.acme.sh/www.shuijingwanwq.com_ecc/
其中包括:
/root/.acme.sh/www.shuijingwanwq.com_ecc/fullchain.cer
/root/.acme.sh/www.shuijingwanwq.com_ecc/www.shuijingwanwq.com.key

第八步:安装新证书到 Nginx 使用的证书路径
签发成功后,还需要把新证书安装到 Nginx 当前使用的证书路径。
安装前,先备份旧证书:
cp /usr/local/nginx/conf/ssl/www.shuijingwanwq.com.crt /usr/local/nginx/conf/ssl/www.shuijingwanwq.com.crt.bak-$(date +%F-%H%M)
cp /usr/local/nginx/conf/ssl/www.shuijingwanwq.com.key /usr/local/nginx/conf/ssl/www.shuijingwanwq.com.key.bak-$(date +%F-%H%M)
然后安装新证书:
~/.acme.sh/acme.sh --install-cert --ecc -d www.shuijingwanwq.com \
--fullchain-file /usr/local/nginx/conf/ssl/www.shuijingwanwq.com.crt \
--key-file /usr/local/nginx/conf/ssl/www.shuijingwanwq.com.key \
--reloadcmd "nginx -s reload"
安装成功后,输出如下:
Installing key to: /usr/local/nginx/conf/ssl/www.shuijingwanwq.com.key
Installing full chain to: /usr/local/nginx/conf/ssl/www.shuijingwanwq.com.crt
Run reload cmd: nginx -s reload
Reload success

第九步:验证新证书是否已经包含 en 子域名
安装新证书后,再验证 en.shuijingwanwq.com 实际拿到的证书是否已经包含 en 子域名:
openssl s_client -connect en.shuijingwanwq.com:443 -servername en.shuijingwanwq.com </dev/null 2>/dev/null | openssl x509 -noout -text | grep -A2 "Subject Alternative Name"
返回结果中已经包含:
X509v3 Subject Alternative Name:
DNS:www.shuijingwanwq.com, DNS:en.shuijingwanwq.com, DNS:shuijingwanwq.com
这说明 en.shuijingwanwq.com 的 HTTPS 证书已经正常。

第十步:当前状态与下一步
到这里,服务器层面的准备已经完成:
Nginx 已识别 en.shuijingwanwq.com
SSL 证书已包含 en.shuijingwanwq.com
en 子域名已指向同一个 WordPress 根目录
不过,此时直接访问:
curl -I https://en.shuijingwanwq.com/
仍然会得到 WordPress 发出的 301 跳转:
HTTP/2 301
location: https://www.shuijingwanwq.com/
x-redirect-by: WordPress
这说明 Nginx 和 SSL 已经不是问题了,剩下的问题在 WordPress / Polylang 层。

下一步需要进入 WordPress 后台:
语言 → 设置 → URL 修改
把 Polylang 的语言 URL 模式从:
/en/my-post/
切换为:
https://en.shuijingwanwq.com/my-post/
也就是让 Polylang 根据子域名识别语言。
这一步我还没有继续操作,因为它会影响现有英文站 URL,需要在确认服务器、证书和缓存策略都准备好之后再切换。
小结
这次操作最大的收获是:
对于已经存在的 OneinStack Nginx 虚拟主机,不一定要删除再重建,才能追加域名和证书。
如果现有 vhost 配置已经有很多自定义规则,删除再重建不只是麻烦,还可能影响线上访问。对于一个已经有稳定流量的 WordPress 主站来说,短时间不可访问、证书异常、跳转异常,都会带来不必要的风险。
更稳妥的做法是:
备份现有 vhost
修改 server_name
调整跳转规则
检查 Nginx 配置
添加 DNS
查看 acme.sh 现有证书记录
重新签发包含新域名的证书
安装证书到原有 Nginx 证书路径
验证 HTTPS
最后再修改 WordPress / Polylang 设置
对于普通独立站点,OneinStack 的 vhost.sh 创建流程已经足够。但对于一个已经长期运行、并且配置经过多次调整的 WordPress 站点,保留现有 vhost,只做最小变更,通常更安全。
需要长期技术维护或远程问题排查?
我是拥有 15+ 年经验的 PHP / Go 后端工程师,长期关注已有系统维护、Bug 修复、性能优化、服务器排查、WordPress 网站维护和小功能迭代。
如果你的项目遇到以下情况,可以先从一次小问题排查开始合作:
- ✅ PHP / Laravel / Yii2 老项目无人维护
- ✅ Go / Gin 后端接口需要排查或优化
- ✅ WordPress 网站访问慢、报错或插件冲突
- ✅ Nginx / MySQL / Redis / Linux 服务器异常
- ✅ CDN / Cloudflare / DNS / HTTPS 配置问题
- ✅ 需要长期远程技术支持或兼职维护
更多介绍请查看:关于我 & 合作
微信:13980074657
邮箱:shuijingwanwq@gmail.com
Telegram:@shuijingwan
GitHub:https://github.com/shuijingwan


发表回复