Redirect one domain to another with Nginx

Preserve paths and query strings in a domain redirect, rewrite changed paths, and retain valid TLS on the old hostname.

Best practices
Illustration of server operations and request logs

For a whole-site domain move, return a redirect to the new hostname and append $request_uri to preserve the original path and query string:

server {
    listen 80;
    server_name old-example.com;
    return 301 https://new-example.com$request_uri;
}

A request for /a?x=1 points to https://new-example.com/a?x=1. Using an explicit https target avoids carrying plain HTTP forward to the new site.

Choose the redirect status deliberately

301 and 308 indicate a permanent move; 302 and 307 are temporary. Unlike 301/302, 307/308 preserve the request method. For API requests, test client behavior and consider whether old clients should continue calling the old hostname at all.

A permanent redirect is a migration signal for search engines, not a guarantee of rankings or an immediate transfer of every indexing signal. Preserve meaningful page-to-page destinations instead of redirecting all old URLs to the homepage.

Rewrite paths only when the structure changed

server {
    listen 80;
    server_name old-example.com;

    rewrite ^/blog/(.*)$ https://new-example.com/articles/$1 permanent;
    return 301 https://new-example.com$request_uri;
}

The first matching rewrite redirects /blog/post to /articles/post; other paths use the fallback. permanent produces 301 and redirect produces 302. Original arguments are retained unless the replacement ends in ?:

rewrite ^/blog/(.*)$ https://new-example.com/articles/$1? permanent;

That alternative drops the original query string. Test encoded paths and parameters that matter to the application.

A server-level return runs during the rewrite phase before location selection. If specific locations need exceptions, put the catch-all return inside location / instead.

Keep HTTPS working on the old domain

server {
    listen 443 ssl;
    server_name old-example.com;
    ssl_certificate /etc/nginx/ssl/old-example.com-fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/old-example.com.key;

    return 301 https://new-example.com$request_uri;
}

TLS negotiation happens before the browser can receive the redirect. The old hostname therefore still needs a valid certificate and reachable DNS. An expired certificate cannot be repaired by a redirect response that the client will not trust.

Validate real old links

sudo nginx -t && sudo systemctl reload nginx
curl -I 'http://old-example.com/a?x=1'
curl -IL 'https://old-example.com/blog/post?source=docs'

Inspect every status and Location, plus the final response. server_name may list several old names, but their certificates must cover HTTPS access. Keep the new site's server configuration distinct to avoid a redirect loop.

After the migration, compare old and new 404s and fix broken mappings rather than assuming a successful homepage redirect covers the site.

FAQ

Should a domain migration always use 301?

Use a permanent redirect for a permanent move. 301 is widely used; 308 is also permanent and preserves the method. Search behavior is more nuanced than “302 never transfers signals”; choose the code based on the actual move and request semantics.

Should I use return or rewrite?

Use return for an unchanged URI with a different origin. Use rewrite when a regular expression needs to transform the path. Prefer readability over an unmeasured performance claim.

Why did the browser's address bar stay unchanged?

A reverse proxy using proxy_pass fetches a response on the client's behalf. It does not itself instruct the browser to navigate. An HTTP redirect does.

References: return and rewrite, TLS server configuration.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting