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

A Practical Record of Adding a Domain to an Existing OneinStack Nginx vhost and Reissuing the SSL Certificate

【图 10:新证书 SAN 信息截图,显示已经包含 en.shuijingwanwq.com】

作者:

Recently, I started preparing to move the English version of my WordPress site from the path-based URL:

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

to a subdomain-based URL:

Plaintext
https://en.shuijingwanwq.com/

The direct reason for this migration was that, while reviewing recent EdgeOne traffic data, I noticed that traffic to the main site had grown far beyond my previous expectations.

I am currently using the EdgeOne Personal plan, which includes 50 GB of traffic. Once the usage exceeds that quota, it moves into pay-as-you-go billing. Since the monthly fee of the Basic plan is relatively high, I do not plan to upgrade the package directly for now. Instead, I prefer to control costs by splitting domains and distributing CDN traffic more carefully.

Figure 1: EdgeOne traffic statistics for the last 6 hours, showing the current traffic growth trend
Figure 1: EdgeOne traffic statistics for the last 6 hours, showing the current traffic growth trend

So I started considering whether to split the English site from the main domain path /en/ to:

Plaintext
https://en.shuijingwanwq.com/

This would allow me to plan the CDN, cache, and traffic strategy for the English site separately, instead of letting the Chinese site, English site, and static resources all continue consuming the EdgeOne traffic quota of the main domain.

However, before actually changing the language URL settings in Polylang, I first ran into a more basic question:

For an existing OneinStack Nginx virtual host, how can I add a new domain name and make the current SSL certificate cover that new domain as well?

Some solutions found online suggest deleting the existing virtual host and then adding the domains again. But for a WordPress main site that has been running for a long time and already contains quite a few manually customized Nginx rules, I did not want to take the “delete and recreate” route.

This article records how I added en.shuijingwanwq.com to the existing OneinStack Nginx virtual host and reissued an SSL certificate that covers multiple domain names.

Current Site Environment

The current main site is a WordPress site deployed with OneinStack. Its Nginx virtual host configuration file is located at:

Plaintext
/usr/local/nginx/conf/vhost/www.shuijingwanwq.com.conf

The main site root directory is:

Plaintext
/data/wwwroot/www.shuijingwanwq.com

The existing domains are:

Plaintext
www.shuijingwanwq.com
shuijingwanwq.com

Now I want to add:

Plaintext
en.shuijingwanwq.com

But this subdomain is not an independent site, nor is it a static asset domain. It still needs to enter the same WordPress application, and Polylang will identify the English language based on the subdomain.

In other words, the target structure is:

Plaintext
www.shuijingwanwq.com

同一个 WordPress 根目录
/data/wwwroot/www.shuijingwanwq.com

en.shuijingwanwq.com

同一个 WordPress 根目录
/data/wwwroot/www.shuijingwanwq.com

This is different from the earlier media.shuijingwanwq.com setup. media is a static asset domain, so it is suitable for a separate virtual host. But en is a language subdomain, so it is more appropriate for it to enter the same WordPress site.

Figure 2: Polylang URL settings page, showing the option to set the language from the subdomain in pretty permalinks
Figure 2: Polylang URL settings page, showing the option to set the language from the subdomain in pretty permalinks

Why I Did Not Delete and Recreate the vhost

The OneinStack vhost.sh script does support adding more domain names when creating a virtual host. The script includes an interactive prompt similar to this:

Plaintext
Do you want to add more domain name? [y/n]:
Figure 3: OneinStack vhost.sh menu or script prompt showing the option related to adding more domain names
Figure 3: OneinStack vhost.sh menu or script prompt showing the option related to adding more domain names

However, judging from the local script output, this logic mainly belongs to the “create virtual host” workflow. If the corresponding ${domain}.conf already exists, the script reports that the virtual host already exists and asks you to delete it before creating it again.

For a new site, that is usually fine.

But for my current main site, the situation is different.

If I deleted the vhost first and then recreated it with OneinStack, the website directory would not necessarily be deleted. But the process could still affect normal access to the live site. My main site already has relatively stable traffic, so I cannot accept a short period of downtime, certificate errors, redirect errors, or overwritten Nginx configuration caused by recreating the virtual host.

In addition, the Nginx configuration file for my main site is no longer a completely default template. It already includes custom includes, WordPress rewrite rules, AMP redirects, tag redirects, static file access restrictions, and other rules. If I deleted and recreated the virtual host, I would have to manually add those rules back afterward, which would increase both the workload and the risk.

So this time I did not delete the virtual host. Instead, I chose to:

Plaintext
保留现有 vhost
只修改 server_name
调整主域名跳转规则
重新签发包含 en 子域名的 SSL 证书

Step 1: Back Up the Existing Nginx Virtual Host Configuration

Before making any formal changes, I first backed up the current configuration file:

Bash
cp /usr/local/nginx/conf/vhost/www.shuijingwanwq.com.conf /usr/local/nginx/conf/vhost/www.shuijingwanwq.com.conf.bak-$(date +%F-%H%M)

This is a necessary step. Since the site is already live, any mistake in the Nginx configuration must be easy to roll back quickly.

Step 2: Add the en Subdomain to the Existing server_name

The original configuration looked like this:

Nginx
server_name www.shuijingwanwq.com shuijingwanwq.com;

I changed it to:

Nginx
server_name www.shuijingwanwq.com shuijingwanwq.com en.shuijingwanwq.com;

With this change, en.shuijingwanwq.com enters the same Nginx server block, which means it reaches the same WordPress root directory.

Figure 4: Screenshot of the updated Nginx server_name configuration
Figure 4: Screenshot of the updated Nginx server_name configuration

Step 3: Adjust the Root Domain Redirect Rule

The original configuration included this rule:

Nginx
if ($host != www.shuijingwanwq.com) {  return 301 $scheme://www.shuijingwanwq.com$request_uri;  }

This rule was fine when the site only had www and the root domain. Its job was to redirect every request that was not www.shuijingwanwq.com back to www.

But now that en.shuijingwanwq.com needs to be added, this rule is no longer suitable.

If it were left unchanged, visiting:

Plaintext
https://en.shuijingwanwq.com/

would be redirected by Nginx with a 301 back to:

Plaintext
https://www.shuijingwanwq.com/

So I changed the rule to handle only the root domain:

Nginx
if ($host = shuijingwanwq.com) {  return 301 $scheme://www.shuijingwanwq.com$request_uri;  }

This way:

Plaintext
shuijingwanwq.com → www.shuijingwanwq.com

But:

Plaintext
en.shuijingwanwq.com

will no longer be forcibly redirected back to www by Nginx.

Step 4: Check and Reload Nginx

After the changes were made, I checked the configuration syntax first:

Bash
nginx -t

The output was normal:

Plaintext
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
Figure 5: Screenshot showing nginx -t passed successfully
Figure 5: Screenshot showing nginx -t passed successfully

Then I reloaded Nginx:

Bash
nginx -s reload

At this point, Nginx was already able to recognize the en.shuijingwanwq.com Host.

However, the task was not complete yet, because the SSL certificate still needed to be reissued so that it would include www.shuijingwanwq.com, shuijingwanwq.com, and en.shuijingwanwq.com at the same time.

Step 5: Check the Existing Certificate Domain Records in acme.sh

Before reissuing the certificate, I first checked the current acme.sh record for the main site certificate in the OneinStack environment:

Bash
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

The output at that time was:

Plaintext
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_'

From this output, I could see that the main domain in the current certificate record was:

Plaintext
www.shuijingwanwq.com

The alternative domain was:

Plaintext
shuijingwanwq.com

It did not yet include:

Plaintext
en.shuijingwanwq.com

I could also confirm that the Webroot corresponding to the current certificate was:

Plaintext
/data/wwwroot/www.shuijingwanwq.com

This matched my target exactly: en.shuijingwanwq.com was not going to be a newly created independent site. It would continue pointing to the same WordPress root directory.

Figure 6: Terminal screenshot showing the main site certificate configuration record in acme.sh
Figure 6: Terminal screenshot showing the main site certificate configuration record in acme.sh

Therefore, the next step was to reissue a certificate that includes the following three domain names:

Plaintext
www.shuijingwanwq.com
shuijingwanwq.com
en.shuijingwanwq.com

Step 6: Add the DNS Record First

Before issuing the certificate, en.shuijingwanwq.com needed to resolve to the origin server.

So I first added a DNS record:

Plaintext
主机记录:en
记录类型:A
记录值:源站服务器公网 IP
代理状态:暂时不走 CDN / 代理

The main purpose of this step was to make sure ACME HTTP validation could reach the origin server.

On the server, I used the following command to check DNS resolution:

Bash
getent ahostsv4 en.shuijingwanwq.com | head -n 1

The result returned at that time looked like this:

Plaintext
121.40.248.29   STREAM en.shuijingwanwq.com

If the returned value is the public IP address of the origin server, DNS resolution on the server side is working properly.

Figure 7: Screenshot of the DNS resolution check result
Figure 7: Screenshot of the DNS resolution check result

Step 7: Reissue a Multi-Domain Certificate with acme.sh

acme.sh was already installed in the current OneinStack environment, and the original certificate record was also stored under:

Plaintext
/root/.acme.sh/www.shuijingwanwq.com/

To keep the certificate setup consistent, I did not introduce a new certificate tool. I continued using the existing acme.sh setup.

The reissue command was:

Bash
~/.acme.sh/acme.sh --force --issue -w /data/wwwroot/www.shuijingwanwq.com -d www.shuijingwanwq.com -d shuijingwanwq.com -d en.shuijingwanwq.com

During issuance, the multi-domain information was visible:

Plaintext
Multi domain='DNS:www.shuijingwanwq.com,DNS:shuijingwanwq.com,DNS:en.shuijingwanwq.com'

After all three domain names passed validation, the certificate was issued successfully.

The new certificate was generated under:

Plaintext
/root/.acme.sh/www.shuijingwanwq.com_ecc/

It includes:

Plaintext
/root/.acme.sh/www.shuijingwanwq.com_ecc/fullchain.cer
/root/.acme.sh/www.shuijingwanwq.com_ecc/www.shuijingwanwq.com.key
Figure 8: Screenshot showing acme.sh successfully issued a multi-domain certificate
Figure 8: Screenshot showing acme.sh successfully issued a multi-domain certificate

Step 8: Install the New Certificate to the Certificate Path Used by Nginx

After the certificate was issued successfully, I still needed to install it to the certificate path currently used by Nginx.

Before installation, I backed up the old certificate first:

Bash
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)

Then I installed the new certificate:

Bash
~/.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"

After the installation succeeded, the output was:

Plaintext
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
Figure 9: Screenshot showing the certificate was installed and reload success was returned
Figure 9: Screenshot showing the certificate was installed and reload success was returned

Step 9: Verify Whether the New Certificate Includes the en Subdomain

After installing the new certificate, I verified whether the certificate actually served for en.shuijingwanwq.com already included the en subdomain:

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

The returned result already included:

Plaintext
X509v3 Subject Alternative Name:
    DNS:www.shuijingwanwq.com, DNS:en.shuijingwanwq.com, DNS:shuijingwanwq.com

This confirmed that the HTTPS certificate for en.shuijingwanwq.com was working correctly.

Figure 10: Screenshot of the new certificate SAN information, showing that en.shuijingwanwq.com is included
Figure 10: Screenshot of the new certificate SAN information, showing that en.shuijingwanwq.com is included

Step 10: Current Status and Next Step

At this point, the server-side preparation was complete:

Plaintext
Nginx 已识别 en.shuijingwanwq.com
SSL 证书已包含 en.shuijingwanwq.com
en 子域名已指向同一个 WordPress 根目录

However, directly visiting:

Bash
curl -I https://en.shuijingwanwq.com/

still returned a WordPress 301 redirect:

Plaintext
HTTP/2 301
location: https://www.shuijingwanwq.com/
x-redirect-by: WordPress

This means Nginx and SSL are no longer the problem. The remaining issue is at the WordPress / Polylang layer.

Figure 11: curl -I https://en.shuijingwanwq.com/ returns a WordPress 301 redirect
Figure 11: curl -I https://en.shuijingwanwq.com/ returns a WordPress 301 redirect

The next step is to enter the WordPress admin dashboard:

Plaintext
语言 → 设置 → URL 修改

and change Polylang’s language URL mode from:

Plaintext
/en/my-post/

to:

Plaintext
https://en.shuijingwanwq.com/my-post/

In other words, Polylang will identify the language based on the subdomain.

I did not continue with this step yet, because it will affect the existing English site URLs. It should only be switched after the server, certificate, and cache strategy have all been confirmed.

Summary

The biggest takeaway from this operation was:

For an existing OneinStack Nginx virtual host, deleting and recreating it is not always necessary just to add another domain name and update the certificate.

If the existing vhost configuration already contains many custom rules, deleting and recreating it is not only inconvenient, but may also affect live access. For a WordPress main site with stable traffic, even a short period of downtime, certificate errors, or redirect errors would introduce unnecessary risk.

A safer approach is:

Plaintext
备份现有 vhost
修改 server_name
调整跳转规则
检查 Nginx 配置
添加 DNS
查看 acme.sh 现有证书记录
重新签发包含新域名的证书
安装证书到原有 Nginx 证书路径
验证 HTTPS
最后再修改 WordPress / Polylang 设置

For a normal independent site, the OneinStack vhost.sh creation workflow is usually enough. But for a WordPress site that has been running for a long time and whose configuration has been adjusted multiple times, keeping the existing vhost and making only minimal changes is often the safer choice.

系列导航

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

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

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

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

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

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

评论

发表回复

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

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