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

Migrating a WordPress English Site from /en/ to an English Subdomain: Preserving Thousands of Legacy Nginx Rules and Avoiding Double 301 Redirects

【图1:浏览器开发者工具中,旧 /en/ 首页返回 301,随后成功加载 en 子域名英文首页】

Previously, the Chinese and English content on my WordPress site was separated by URL path:

Plaintext
中文站:
https://www.shuijingwanwq.com/

英文站:
https://www.shuijingwanwq.com/en/

As the English site became more independent, I moved the English content to a dedicated subdomain:

Plaintext
https://en.shuijingwanwq.com/

After the English subdomain went live, the homepage, posts, categories, tags, and series URLs previously located under /en/ all needed to be permanently migrated to the new domain with 301 redirects.

On a new site with no legacy redirect rules, a single Nginx rewrite might be enough.

My site, however, had already accumulated thousands of rules for tag slug conversion, legacy tag consolidation, and series path changes. Adding a generic /en/* redirect could bypass the existing slug mappings and also create a two-step 301 redirect chain.

This article documents the migration strategy, the final Nginx structure, the validation process, and how I now maintain 301 rules separately for the www primary domain and the en English subdomain.

For additional context, see the earlier article:


1. URL Structure Before and After the Migration

Before the migration, the English homepage and a regular English post used these URLs:

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

https://www.shuijingwanwq.com/en/2026/07/05/18871/

After the migration, the corresponding URLs became:

Plaintext
https://en.shuijingwanwq.com/

https://en.shuijingwanwq.com/2026/07/05/18871/

The basic migration target was therefore:

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

https://en.shuijingwanwq.com/

And:

Plaintext
https://www.shuijingwanwq.com/en/2026/07/05/18871/

https://en.shuijingwanwq.com/2026/07/05/18871/

The migration needed to remove the leading /en/ path segment while preserving the rest of the post path and any query parameters.

Figure 1: In the browser developer tools, the old /en/ homepage returns a 301 before the English homepage loads successfully from the en subdomain
Figure 1: In the browser developer tools, the old /en/ homepage returns a 301 before the English homepage loads successfully from the en subdomain.

2. Why a Single Generic rewrite Was Not Enough

If a site has no other legacy redirects, a rule like the following may be sufficient:

Nginx
rewrite ^/en(?:/(.*))?$ https://en.shuijingwanwq.com/$1 permanent;

My site, however, already had a large set of legacy URL mappings used to:

  1. Consolidate Chinese tag slugs;
  2. Convert Chinese tag slugs under the original /en/ path into English slugs;
  3. Merge old series paths into new series paths;
  4. Normalize duplicate tags and other historical paths.

These rules were already maintained with an Nginx map in a structure similar to this:

Nginx
map $uri $new_tag_uri {
    default "";

    ~^/tag/权限/?$ "/tag/permission/";
    ~^/en/tag/权限/?$ "/en/tag/permission/";

    ~^/tag/30-天试用/?$ "/tag/30-day-trial/";
    ~^/en/tag/30-天试用/?$ "/en/tag/30-day-trial-en/";

    ~^/series/15-year-backend-dev-survival-notes-in-ai-era/?$
        "/series/ai-era-developer-monetization-system/";

    ~^/en/series/15-year-backend-dev-survival-notes-in-ai-era-en/?$
        "/en/series/ai-era-developer-monetization-system-en/";
}

Before migrating the English site, the mapped result could be returned directly from the server block:

Nginx
if ($new_tag_uri != "") {
    return 301 $new_tag_uri;
}

The problem is that if the generic /en/ redirect runs before the legacy mapping, the following URL:

Plaintext
https://www.shuijingwanwq.com/en/tag/30-天试用/

May be redirected directly to:

Plaintext
https://en.shuijingwanwq.com/tag/30-天试用/

The existing rule responsible for converting the Chinese tag slug into an English slug would then be bypassed.

If the legacy mapping runs first and the domain migration is handled separately afterward, the request may instead follow this path:

Plaintext
https://www.shuijingwanwq.com/en/tag/30-天试用/

https://www.shuijingwanwq.com/en/tag/30-day-trial-en/

https://en.shuijingwanwq.com/tag/30-day-trial-en/

The final destination is correct, but the browser has to pass through two separate 301 responses.

The preferable result is:

Plaintext
https://www.shuijingwanwq.com/en/tag/30-天试用/

https://en.shuijingwanwq.com/tag/30-day-trial-en/

In other words, a single 301 should perform both operations:

Plaintext
旧标签 slug 转换
+
英文域名迁移

Because the final configuration is already live, this two-step redirect can no longer be reproduced online. I have therefore kept the path-flow explanation without adding a screenshot for that intermediate scenario.


3. The Final Layered Redirect Strategy

Rather than editing thousands of existing mappings one by one, I added a second layer after the legacy mapping to convert the normalized path into its final domain URL.

The overall flow is:

Plaintext
请求进入 Nginx

检查是否命中旧标签或旧专题规则

命中后生成标准目标路径

判断标准路径是否以 /en/ 开头

以 /en/ 开头:
移除 /en/,跳转到 en 英文子域名

不以 /en/ 开头:
继续跳转到 www 中文主域名

如果没有命中旧路径映射

检查原始请求是否为普通 /en/ 页面

统一迁移到 en 英文子域名

This approach satisfies all of the following requirements:

  • Existing tag and series mappings continue to work;
  • Legacy English tag URLs reach their final English destinations in one redirect;
  • Legacy Chinese tag URLs remain on the www primary domain;
  • Regular English posts, pages, and categories are migrated consistently;
  • Existing rules do not need to be rewritten individually.

4. Layer One: Keep the Existing Legacy Path Mappings

The original $new_tag_uri variable continues to normalize historical URLs into canonical paths.

For example:

Nginx
~^/tag/30-天试用/?$ "/tag/30-day-trial/";

This means:

Plaintext
/tag/30-天试用/

/tag/30-day-trial/

Legacy English paths continue to retain the /en/ marker:

Nginx
~^/en/tag/30-天试用/?$ "/en/tag/30-day-trial-en/";

This means:

Plaintext
/en/tag/30-天试用/

/en/tag/30-day-trial-en/

At this stage, the mapping only produces a normalized destination path. It is not yet returned to the browser.

Keeping the /en/ prefix is important because the next layer can use it to determine that the final destination belongs on the English subdomain.


5. Layer Two: Convert the Normalized Path into the Final Domain URL

I added a second map to the existing mapping file:

Nginx
# 仅处理 www/裸域旧规则的映射结果
# en.shuijingwanwq.com 后续使用独立的英文标签跳转规则
map "$host:$new_tag_uri" $www_final_redirect_uri {
    default "";

    # www 旧 /en/ 路径:去掉 /en/,跳转到英文子域名
    ~*^(?:www\.shuijingwanwq\.com|shuijingwanwq\.com):/en/(.*)$
        "https://en.shuijingwanwq.com/$1";

    # www 中文旧路径:继续留在 www 主域名
    ~*^(?:www\.shuijingwanwq\.com|shuijingwanwq\.com):/(.*)$
        "https://www.shuijingwanwq.com/$1";
}

This map evaluates both:

Plaintext
$host
$new_tag_uri

It can therefore distinguish between:

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

And:

Plaintext
www.shuijingwanwq.com:/tag/...

When the first layer produces:

Plaintext
/en/tag/30-day-trial-en/

The second layer converts it into:

Plaintext
https://en.shuijingwanwq.com/tag/30-day-trial-en/

When the first layer produces:

Plaintext
/tag/30-day-trial/

It is converted into:

Plaintext
https://www.shuijingwanwq.com/tag/30-day-trial/

This allows legacy English and Chinese tag URLs to be routed correctly while still using the same historical mapping set.


6. A Fallback Rule for Regular /en/ Pages

The legacy tag and series mappings only cover historical paths that have been explicitly registered.

Regular English posts, pages, and categories usually do not match $new_tag_uri, so a fallback mapping is still required for ordinary /en/ paths:

Nginx
# www/裸域普通 /en/ 路径迁移到英文子域名
# 仅作为旧标签、旧专题映射之后的兜底规则
map "$host:$uri" $www_en_migration_uri {
    default "";

    ~*^(?:www\.shuijingwanwq\.com|shuijingwanwq\.com):/en/?$
        "https://en.shuijingwanwq.com/";

    ~*^(?:www\.shuijingwanwq\.com|shuijingwanwq\.com):/en/(.*)$
        "https://en.shuijingwanwq.com/$1";
}

It handles:

Plaintext
https://www.shuijingwanwq.com/en/
Plaintext
https://www.shuijingwanwq.com/en/2026/07/05/18871/
Plaintext
https://www.shuijingwanwq.com/en/category/wordpress/

As well as any other /en/ URL that does not match a legacy mapping.


7. Execution Order Inside the server Block Matters

Inside the Nginx server block, the precise legacy mapping result must be checked before the generic /en/ migration fallback:

Nginx
# 旧标签、旧专题先映射,再一次跳转到最终中文或英文域名
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;
}

The order must not be reversed.

If the generic /en/ fallback runs first, legacy English tag URLs are moved to the English subdomain before the original tag slug conversion has a chance to run.

The current order can be summarized as:

Plaintext
先处理精确的旧标签和旧专题映射
再处理通用的 /en/ 域名迁移

This is the most important part of the implementation. The production virtual host follows this order and only checks the English subdomain’s independent rules afterward.


8. Preserving Query Parameters from the Original Request

During testing, I found that the path was redirected to the correct final English URL, but the initial version did not preserve the test query parameters.

For example:

Plaintext
?utm_source=redirect-test&utm_medium=nginx

To preserve UTM parameters, advertising attribution data, and other query strings, I added the following to the final return directive:

Nginx
$is_args$args

The complete form is:

Nginx
return 301 $www_final_redirect_uri$is_args$args;

And:

Nginx
return 301 $www_en_migration_uri$is_args$args;

Where:

  • $args contains the original request’s query parameters;
  • $is_args outputs ? when query parameters are present;
  • When there are no query parameters, $is_args is empty.

Test command:

Bash
curl -4 -sS -I --max-time 20 "https://www.shuijingwanwq.com/en/tag/30-%E5%A4%A9%E8%AF%95%E7%94%A8/?utm_source=redirect-test&utm_medium=nginx" | grep -Ei "HTTP/|location:|server:|eo-cache-status"

Actual response:

Plaintext
HTTP/2 301
server: nginx
location: https://en.shuijingwanwq.com/tag/30-day-trial-en/?utm_source=redirect-test&utm_medium=nginx
eo-cache-status: MISS

As shown in the response, the legacy tag URL completes both slug conversion and domain migration while keeping the query parameters intact.

Figure 2: The utm_source and utm_medium query parameters remain intact after the legacy English tag URL completes its 301 redirect
Figure 2: The utm_source and utm_medium query parameters remain intact after the legacy English tag URL completes its 301 redirect.

9. A Separate Rule File for the English Subdomain

After completing the migration, I created a separate file:

Plaintext
/usr/local/nginx/conf/include/shuijingwan_en_redirect.conf

This file is not used to process historical URLs under /en/.

For example:

Plaintext
https://www.shuijingwanwq.com/en/tag/旧地址/

The source belongs to the legacy www site, so it should still be handled by:

Plaintext
shuijingwan_nginx_redirect.conf

This file remains responsible for that redirect.

By contrast, a URL such as:

Plaintext
https://en.shuijingwanwq.com/tag/旧地址/

Because this source URL is already on the new English subdomain, it should be handled by:

Plaintext
shuijingwan_en_redirect.conf

This file remains responsible for that redirect.

en.shuijingwanwq.com only went live as part of this migration, so it does not yet have thousands of historical English-subdomain URLs to support.

There is therefore no reason to duplicate thousands of existing /en/tag/... mappings and manually remove /en/ from every one.

For now, the English rule file contains only an empty framework:

Nginx
# en.shuijingwanwq.com 独立旧路径跳转规则
# 以后英文域名自身出现需要合并或变更的网址时,在这里添加
map $uri $en_new_uri {
    default "";
}

If a tag on the English subdomain needs to be merged later, a rule can be added such as:

Nginx
~^/tag/old-slug/?$ "/tag/new-slug/";

The following map can then build the complete destination URL:

Nginx
# 仅在 en.shuijingwanwq.com 上执行英文旧路径跳转
map "$host:$en_new_uri" $en_final_redirect_uri {
    default "";

    ~*^en\.shuijingwanwq\.com:(/.*)$
        "https://en.shuijingwanwq.com$1";
}

The corresponding logic in the server block is:

Nginx
if ($en_final_redirect_uri != "") {
    return 301 $en_final_redirect_uri$is_args$args;
}

The final English rule file therefore contains only an empty mapping framework and a domain restriction; it does not duplicate the thousands of historical rules from the former /en/ path.


10. Responsibilities of the Two Rule Files

After the migration, the responsibilities of the two configuration files are clearly separated.

shuijingwan_nginx_redirect.conf

It handles historical source URLs on:

Plaintext
www.shuijingwanwq.com

As well as:

Plaintext
shuijingwanwq.com

These are the domains covered by the legacy rule set.

This includes:

Nginx
# 中文主站旧地址
~^/tag/旧中文标签/?$ "/tag/新中文标签/";

# 历史上位于 www /en/ 下的英文旧地址
~^/en/tag/旧英文标签/?$ "/en/tag/新英文标签/";

Although the second type of rule remains in the historical www rule file, the second map layer automatically converts its destination to:

Plaintext
en.shuijingwanwq.com

shuijingwan_en_redirect.conf

It handles only future legacy URLs whose source is already on:

Plaintext
en.shuijingwanwq.com

This English subdomain.

There are currently no active path mappings in this file. New rules will be added only after the English subdomain develops its own tag mergers, series renames, or path changes.

A simple way to remember the separation is:

Plaintext
www 或旧 www/en/ 来源
→ shuijingwan_nginx_redirect.conf

en 子域名来源
→ shuijingwan_en_redirect.conf
Figure 3: The two production rule files in the Nginx include directory, together with backup files created during the migration
Figure 3: The two production rule files in the Nginx include directory, together with backup files created during the migration.

11. Validating Each Redirect Scenario

After updating the configuration, I tested the legacy English homepage, a legacy English tag, a regular English post, a legacy Chinese tag, and a legacy English series URL separately.

To avoid accidental spaces after backslashes when copying multiline Bash commands, all commands below are written on a single line.

1. Test the Legacy English Homepage

Bash
curl -4 -sS -I -L --max-time 30 "https://www.shuijingwanwq.com/en/" | grep -Ei "HTTP/|location:|server:|eo-cache-status|cf-cache-status"

Expected redirect chain:

Plaintext
HTTP/2 301
location: https://en.shuijingwanwq.com/

HTTP/2 200

The browser developer tools also show the old /en/ request returning a 301 before the English subdomain homepage loads successfully.


2. Test a Legacy English Tag URL

For paths containing Chinese characters, it is safer to use percent-encoded URLs:

Bash
curl -4 -sS -I -L --max-time 30 "https://www.shuijingwanwq.com/en/tag/30-%E5%A4%A9%E8%AF%95%E7%94%A8/" | grep -Ei "HTTP/|location:|server:|eo-cache-status|cf-cache-status"

Actual result:

Plaintext
HTTP/2 301
server: nginx
location: https://en.shuijingwanwq.com/tag/30-day-trial-en/
eo-cache-status: MISS

HTTP/2 200
server: cloudflare
cf-cache-status: MISS

The entire process uses only one 301 redirect.

Figure 4: A legacy English tag URL reaches the final English tag page on the English subdomain after a single 301 redirect
Figure 4: A legacy English tag URL reaches the final English tag page on the English subdomain after a single 301 redirect.

3. Test a Regular English Post

Bash
curl -4 -sS -I -L --max-time 30 "https://www.shuijingwanwq.com/en/2026/07/05/18871/" | grep -Ei "HTTP/|location:|server:|eo-cache-status|cf-cache-status"

Actual result:

Plaintext
HTTP/2 301
server: nginx
location: https://en.shuijingwanwq.com/2026/07/05/18871/
eo-cache-status: MISS

HTTP/2 200
server: nginx

This confirms that the regular English post correctly matched the /en/ migration fallback.

Figure 5: The complete redirect chain for a regular English post under /en/ is 301 → 200
Figure 5: The complete redirect chain for a regular English post under /en/ is 301 → 200.

4. Test a Legacy Chinese Tag URL

Bash
curl -4 -sS -I -L --max-time 30 "https://www.shuijingwanwq.com/tag/30-%E5%A4%A9%E8%AF%95%E7%94%A8/" | grep -Ei "HTTP/|location:|server:|eo-cache-status|cf-cache-status"

Actual result:

Plaintext
HTTP/2 301
server: nginx
location: https://www.shuijingwanwq.com/tag/30-day-trial/
eo-cache-status: MISS

HTTP/2 200
server: nginx
eo-cache-status: MISS

The legacy Chinese tag remains on the Chinese www primary domain and is not incorrectly moved to the English subdomain.

Figure 6: A legacy Chinese tag URL still redirects to its final Chinese tag URL on the www primary domain
Figure 6: A legacy Chinese tag URL still redirects to its final Chinese tag URL on the www primary domain.

5. Test a Legacy English Series URL

Bash
curl -4 -sS -I -L --max-time 30 "https://www.shuijingwanwq.com/en/series/15-year-backend-dev-survival-notes-in-ai-era-en/" | grep -Ei "HTTP/|location:|server:|eo-cache-status|cf-cache-status"

Actual result:

Plaintext
HTTP/2 301
server: nginx
location: https://en.shuijingwanwq.com/series/ai-era-developer-monetization-system-en/
eo-cache-status: MISS

HTTP/2 200
server: cloudflare
cf-cache-status: MISS

This confirms that the legacy series rule can normalize the slug first and then reach the final English-subdomain URL in a single redirect.

Figure 7: A legacy English series URL reaches its final English-subdomain series URL in a single 301 redirect
Figure 7: A legacy English series URL reaches its final English-subdomain series URL in a single 301 redirect.

12. Why a Copied Multiline curl Command May Fail

During testing, I copied a command similar to this:

Bash
curl -4 -sS -I -L \ "https://www.shuijingwanwq.com/en/tag/..."

curl then returned an error:

Plaintext
curl: (1) Protocol " https" not supported or disabled in libcurl

The server did support HTTPS. The actual problem was a space after the backslash:

Plaintext
\ 

In the shell, a backslash escapes the character immediately following it.

When that character is a space, curl receives a URL that effectively begins with a space:

 https://www.shuijingwanwq.com/...

curl therefore interprets the protocol as:

 https

Instead of the valid form:

Plaintext
https

To avoid hidden spaces introduced when copying from WordPress, chat windows, or code blocks, every test command in this article is written as a single line.

When a multiline Bash command is unavoidable, the backslash must be the final character on the line, with no spaces or other characters after it.


13. curl May Return 400 for an Unencoded Chinese URL

When testing a tag URL containing Chinese characters, I initially ran:

Bash
curl -I "https://en.shuijingwanwq.com/tag/30-天试用/"

In this test environment, the response was:

Plaintext
HTTP/2 400

This does not necessarily mean that the Nginx mapping failed, because the Chinese path in the command had not yet been percent-encoded.

After encoding the Chinese path:

Bash
curl -4 -sS -I --max-time 20 "https://en.shuijingwanwq.com/tag/30-%E5%A4%A9%E8%AF%95%E7%94%A8/"

The request was sent successfully.

Browsers usually encode Chinese paths automatically, so the same problem is less likely to appear during normal browser access.

When testing URLs containing Chinese characters from the command line, it is best to use the percent-encoded form consistently.


14. Why the 301 Rules Live in Nginx Instead of Entirely at the CDN Layer

The Chinese primary domain and the English subdomain currently use different CDN configurations:

Plaintext
www.shuijingwanwq.com
→ EdgeOne

en.shuijingwanwq.com
→ Cloudflare

Both EdgeOne and Cloudflare can issue redirects, but I still chose Nginx as the authoritative source for this migration’s 301 rules for several reasons:

  1. The legacy tag and series rules already lived in Nginx;
  2. Path normalization and domain migration need to be evaluated together;
  3. Splitting the rules across two CDN dashboards would make redirect precedence harder to troubleshoot;
  4. The origin rules will continue to work if the CDN provider changes later;
  5. Thousands of legacy mappings should not be maintained repeatedly in multiple control panels.

A CDN may cache some 301 responses, but the underlying redirect logic remains centrally managed at the origin.

This is easier to maintain over the long term and makes the complete redirect flow visible in one configuration.


15. Final Result

After the migration, all of the following scenarios were verified successfully.

Regular English Pages

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

en.shuijingwanwq.com/...

English Tag URLs with Legacy Slugs

Plaintext
www.shuijingwanwq.com/en/tag/旧-slug/

en.shuijingwanwq.com/tag/最终-slug/

Only one 301 redirect is used.

Legacy Chinese Tag URLs

Plaintext
www.shuijingwanwq.com/tag/旧-slug/

www.shuijingwanwq.com/tag/最终-slug/

They remain on the Chinese primary domain.

Future Rules Created on the English Subdomain

Plaintext
en.shuijingwanwq.com/旧路径/

en.shuijingwanwq.com/新路径/

They are added to the dedicated English rule file and are not mixed with the legacy /en/ migration rules.


16. Conclusion

Migrating a WordPress English site from /en/ to a dedicated subdomain may appear to require nothing more than removing one path prefix.

Once a site has accumulated extensive tag consolidation, slug translation, and series redirect rules, however, a single generic rewrite can easily cause:

  • Legacy rules to be bypassed;
  • Slugs to remain unconverted;
  • Two consecutive 301 redirects;
  • Lost query parameters;
  • Interference between Chinese and English rules;
  • Uncertainty about which configuration file should be updated later.

The final implementation uses layered mappings:

Plaintext
历史路径映射

最终域名转换

普通 /en/ 兜底迁移

It also separates:

Plaintext
旧 www 和旧 www/en/ 规则

From:

Plaintext
英文子域名上线后产生的新规则

For ongoing maintenance.

This preserves the existing thousands of rules while allowing legacy English URLs to reach their final English-subdomain destinations in a single redirect. It also leaves a clear extension point for future tag and series path changes.


Appendix 1: Sanitized Nginx Virtual Host Configuration

The following example preserves the complete virtual host structure relevant to this migration.

Certificate paths, log paths, the WordPress document root, and the PHP-FPM socket have been replaced with generic placeholders. The configuration must not be copied into production without adapting those values.

Nginx
# map 必须位于 http 上下文。
# 当前环境中的 vhost 文件由 nginx.conf 在 http 块内引入。

# 引入 www 主域名及历史 /en/ 路径规则
include /usr/local/nginx/conf/include/shuijingwan_nginx_redirect.conf;

# 引入 en.shuijingwanwq.com 独立旧路径规则
include /usr/local/nginx/conf/include/shuijingwan_en_redirect.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 /path/to/ssl/example.com.crt;
    ssl_certificate_key /path/to/ssl/example.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 /path/to/logs/example.com.access.log combined;

    index index.html index.htm index.php;
    root /path/to/wordpress;

    if ($ssl_protocol = "") {
        return 301 https://$host$request_uri;
    }

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

    # 非 www 域名访问 WordPress 后台时,统一跳回 www 主域名。
    set $redirect_non_www_admin "";

    if ($host != www.shuijingwanwq.com) {
        set $redirect_non_www_admin "${redirect_non_www_admin}H";
    }

    if ($uri ~* "^/(wp-admin(?:/|$)|wp-login\.php$)") {
        set $redirect_non_www_admin "${redirect_non_www_admin}U";
    }

    if ($redirect_non_www_admin = "HU") {
        return 301 https://www.shuijingwanwq.com$request_uri;
    }

    include /usr/local/nginx/conf/rewrite/wordpress.conf;

    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 unix:/path/to/php-fpm.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;
    }
}

Appendix 2: Historical Redirect Rules for www and the Former /en/ Path

In production, $new_tag_uri contains thousands of tag and series mappings.

Expanding every rule in the article would make it unnecessarily large and obscure the two-layer conversion logic that actually matters.

The example below therefore preserves the real file structure and representative rules, while omitting large groups of equivalent mappings with comments.

Nginx
map $uri $new_tag_uri {
    default "";

    # 中文标签合并
    ~^/tag/权限/?$ "/tag/permission/";
    ~^/tag/菜单/?$ "/tag/menu/";
    ~^/tag/安装/?$ "/tag/installation/";

    # 原 /en/ 路径下的英文标签标准化
    ~^/en/tag/权限/?$ "/en/tag/permission/";
    ~^/en/tag/菜单/?$ "/en/tag/menu/";
    ~^/en/tag/安装/?$ "/en/tag/installation/";

    # 同一个中文标签在中英文站使用不同的最终 slug
    ~^/tag/30-天试用/?$ "/tag/30-day-trial/";
    ~^/en/tag/30-天试用/?$ "/en/tag/30-day-trial-en/";

    ~^/tag/在线商店/?$ "/tag/online-store/";
    ~^/en/tag/在线商店/?$ "/en/tag/online-store-en/";

    ~^/tag/多个-php-版本/?$ "/tag/multiple-php-versions/";
    ~^/en/tag/多个-php-版本/?$ "/en/tag/multiple-php-versions-en/";

    ~^/tag/chrome扩展/?$ "/tag/chrome-extension/";
    ~^/en/tag/chrome扩展/?$ "/en/tag/chrome-extension-en/";

    # ……中间省略数千条同类标签映射……

    # 中文专题合并
    ~^/series/15-year-backend-dev-survival-notes-in-ai-era/?$
        "/series/ai-era-developer-monetization-system/";

    # 原 /en/ 下的英文专题合并
    ~^/en/series/15-year-backend-dev-survival-notes-in-ai-era-en/?$
        "/en/series/ai-era-developer-monetization-system-en/";
}


# 仅处理 www/裸域旧规则的映射结果
# en.shuijingwanwq.com 后续使用独立的英文标签跳转规则
map "$host:$new_tag_uri" $www_final_redirect_uri {
    default "";

    # www 旧 /en/ 路径:
    # 去掉 /en/,一次跳转到英文子域名
    ~*^(?:www\.shuijingwanwq\.com|shuijingwanwq\.com):/en/(.*)$
        "https://en.shuijingwanwq.com/$1";

    # www 中文旧路径:
    # 继续留在中文主域名
    ~*^(?:www\.shuijingwanwq\.com|shuijingwanwq\.com):/(.*)$
        "https://www.shuijingwanwq.com/$1";
}


# www/裸域普通 /en/ 路径迁移到英文子域名
# 仅作为旧标签、旧专题映射之后的兜底规则
map "$host:$uri" $www_en_migration_uri {
    default "";

    ~*^(?:www\.shuijingwanwq\.com|shuijingwanwq\.com):/en/?$
        "https://en.shuijingwanwq.com/";

    ~*^(?:www\.shuijingwanwq\.com|shuijingwanwq\.com):/en/(.*)$
        "https://en.shuijingwanwq.com/$1";
}

In the future, if a source URL belongs to:

Plaintext
www.shuijingwanwq.com

Or to the former:

Plaintext
www.shuijingwanwq.com/en/

The mapping should still be added to this file.


Appendix 3: Independent Redirect Rules for the English Subdomain

en.shuijingwanwq.com only went live during this migration, so there is currently no large set of historical English-subdomain URLs to migrate.

The final file contains only the rule framework:

Nginx
# en.shuijingwanwq.com 独立旧路径跳转规则
# 以后英文域名自身出现需要合并或变更的网址时,在这里添加
map $uri $en_new_uri {
    default "";

    # 示例:
    # ~^/tag/old-slug/?$ "/tag/new-slug/";
}


# 仅在 en.shuijingwanwq.com 上执行英文旧路径跳转
map "$host:$en_new_uri" $en_final_redirect_uri {
    default "";

    ~*^en\.shuijingwanwq\.com:(/.*)$
        "https://en.shuijingwanwq.com$1";
}

A mapping should be added to this file only when the source URL itself already belongs to:

Plaintext
https://en.shuijingwanwq.com/

This English subdomain.

系列导航

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

我是拥有 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 来减少垃圾评论。了解你的评论数据如何被处理