When migrating domains with multiple subdomains, regex-based server_name matching in Nginx should theoretically capture and reuse the subdomain portion. Here's what's happening with the OP's configuration:
server { listen 80; server_name ~^(\\w+)\\.olddomain\\.com$; rewrite ^ $scheme://$1.doma.in$request_uri? permanent; }
After testing multiple Nginx versions (1.18-1.25), I discovered three critical points:
- The regex capture group
(\\w+)
only matches ASCII word characters (a-z, A-Z, 0-9, _)- Nginx evaluates server_name regex before normalizing the host header
- Browser headers might include trailing dots or mixed case
Here's the battle-tested configuration we use for domain migrations:
server { listen 80; server_name "~^(?
[a-zA-Z0-9-]+)\\.olddomain\\.com$"; # Handle case sensitivity and trailing dots if ($host ~* "^(.+)\\.olddomain\\.com\.?$") { set $clean_sub $1; } return 301 $scheme://$clean_sub.doma.in$request_uri; } # Fallback for non-matching subdomains server { listen 80; server_name .olddomain.com; return 301 $scheme://doma.in$request_uri; } For complex subdomain patterns:
# Match both legacy and new domains map $host $new_domain { "~^(?.+)\.olddomain\.com$" "$sub.doma.in"; default "doma.in"; } server { listen 80; server_name ~.olddomain.com$; return 301 $scheme://$new_domain$request_uri; }
Add this temporary block to inspect values:
server { listen 80; server_name ~.olddomain.com$; add_header Content-Type text/plain; return 200 "Host: $host\nSub: $1\nRequest: $request_uri"; }
When migrating from
olddomain.com
todoma.in
, I needed to preserve all wildcard subdomains while changing only the root domain. The initial approach using regex capture groups seemed logical:server { listen 80; server_name ~^(\\w+)\\.olddomain\\.com$; rewrite ^ $scheme://$1.doma.in$request_uri? permanent; }
The configuration fails because Nginx's
$1
capture group doesn't behave as expected in rewrite rules when combined withserver_name
regex. The captured subdomain value gets lost during processing.Here's the proper implementation using
map
for reliable subdomain capture:map $host $newdomain { default ""; "~^(?
\\w+)\\.olddomain\\.com$" "${subdomain}.doma.in"; } server { listen 80; server_name ~^(\\w+)\\.olddomain\\.com$; if ($newdomain) { return 301 $scheme://$newdomain$request_uri; } } For simpler cases, explicit server blocks work better than regex:
server { listen 80; server_name *.olddomain.com; if ($host ~* ^(?
.+)\.olddomain\.com$) { return 301 $scheme://${subdomain}.doma.in$request_uri; } } Always verify redirects with:
curl -I http://test.olddomain.com/some/path
Should return:
HTTP/1.1 301 Moved Permanently Location: http://test.doma.in/some/path
For high-traffic sites, avoid regex where possible. Pre-compile mappings:
map $http_host $newdomain { include /etc/nginx/subdomain_mappings.conf; }
Where
subdomain_mappings.conf
contains pre-defined mappings like:"api.olddomain.com" "api.doma.in"; "app.olddomain.com" "app.doma.in";
Nginx Redirect Subdomain Wildcard from Old Domain to New Domain While Preserving Subdomains
9 views