Redirects are the cleanup layer for old URLs, domain moves, HTTPS enforcement, and canonical host changes. In NGINX, most URL redirects should use return because it sends the redirect response immediately; use rewrite only when a regular expression needs to reshape part of the URI.
The examples cover permanent and temporary redirects for exact paths, location blocks, regex captures, query-string preservation, and www versus non-www cleanup. Test each change with nginx -t and curl. In reverse proxy setups, proxy_redirect has a narrower job: it changes upstream Location headers rather than redirecting the original client request.
Choose the Right Nginx Redirect Method
An NGINX redirect sends a 3xx status code plus a Location header to the client. For the common whole-domain move, put return 301 https://www.example.com$request_uri; in the old hostname’s server block. If you are looking for URL forwarding, use a redirect when the client should see the new address; use a reverse proxy when NGINX should fetch content from an upstream server without changing the browser URL.
Compare Nginx Redirect Directives
Use this quick reference to choose the directive before editing a server block. The official NGINX docs document the contexts for the return directive, rewrite directive, map directive, proxy_redirect directive, and absolute_redirect directive. The NGINX core variable reference documents $request_uri, $args, and $is_args.
| Directive or variable | Context | Default or note | Use for redirects |
|---|---|---|---|
return | server, location, if | No default | Simple 301, 302, 307, 308, or other immediate responses. |
rewrite | server, location, if | No default | Regex-based URI changes and redirects that need capture groups. |
map | http | No default | Large redirect lookup tables evaluated before the server block handles the request. |
proxy_redirect | http, server, location | proxy_redirect default; | Rewriting upstream Location and Refresh headers in proxied responses. |
absolute_redirect | http, server, location | absolute_redirect on; | Controls whether NGINX-generated redirects from local URI targets use absolute or relative Location headers. |
$request_uri | Variable | Full original URI with arguments | Preserve the original path and query string. |
$is_args$args | Variables | ? plus arguments only when a query string exists | Move to a new path while preserving query parameters cleanly. |
Nginx Redirect Status Codes
Choose the redirect status before writing the rule. A cached permanent redirect can make testing confusing, while a temporary redirect can leave crawlers treating the old URL as the main URL longer than intended.
| Status Code | Meaning | Use When |
|---|---|---|
301 | Permanent redirect | The old URL should permanently move to the new URL. |
302 | Temporary redirect | The old URL should remain valid after a maintenance window, test, or short campaign. |
307 | Temporary redirect that preserves the request method | A temporary POST, PUT, or API route should not be converted into a GET by the client. |
308 | Permanent redirect that preserves the request method | A permanent API or form route move must keep the original request method. |
The rewrite flags permanent and redirect cover 301 and 302 redirects. Use return when you need 307, 308, or a direct redirect that does not require a regular expression.
Nginx Redirect Prerequisites
Before changing redirects, confirm you have NGINX running, sudo or root access, and the right site configuration file. Common Linux layouts use /etc/nginx/nginx.conf for global settings and either /etc/nginx/sites-available/ or /etc/nginx/conf.d/ for site-specific server blocks. The official NGINX beginner guide explains the base configuration structure.
If NGINX sits behind a CDN, hosting panel, or Nginx Proxy Manager, decide which layer owns each redirect before editing raw configuration. Use one redirect owner per hostname or path; duplicated redirects across the edge and origin are a common cause of loops.
Redirect rules can affect every visitor and crawler that reaches a host. Test changes in a staging server or low-traffic window when possible, especially for catch-all server blocks, HTTPS redirects, or domain migrations.
Redirect URLs in Nginx with return
The return directive is the safest default for redirects because it does not continue searching for other rewrite rules. Put it in the narrowest matching server or location block that owns the old URL.
Create a Permanent Nginx Domain Redirect
Use a dedicated server block when an old domain should redirect to a new domain. Replace the example hostnames with your real source and destination domains. The $request_uri variable keeps the original path and query string intact.
server {
listen 80;
server_name old.example.com www.old.example.com;
return 301 https://www.example.com$request_uri;
}
A request for http://old.example.com/products/item?id=123 redirects to https://www.example.com/products/item?id=123. That behavior is why $request_uri is the usual choice for whole-domain migrations.
Create a Temporary Nginx Redirect
Use a 302 redirect when the move is temporary and the original URL should remain the long-term destination.
server {
listen 80;
server_name maintenance.example.com;
return 302 https://status.example.com$request_uri;
}
This pattern is useful during short maintenance windows or tests where you do not want browsers and crawlers to treat the new destination as permanent.
Redirect Specific Nginx Locations
Use location blocks when only specific paths should redirect. Exact locations are best for one-off URL moves; prefix locations are useful when a whole subtree should move to another host while keeping the same path.
server {
listen 80;
server_name www.example.com;
location = /old-blog {
return 301 /blog$is_args$args;
}
location /legacy/ {
return 301 https://archive.example.com$request_uri;
}
location / {
root /var/www/html;
index index.html;
}
}
The = modifier in location = /old-blog matches only that exact URI. The $is_args$args pair keeps query parameters when the destination path changes. The prefix location /legacy/ matches anything below that path, such as /legacy/post-1.
Configure Pattern-Based Nginx Redirects with rewrite
Use rewrite when the redirect needs a regular expression and capture groups. For direct redirects without captures, prefer return because it is clearer and easier to audit.
Redirect Nginx URLs by File Extension
This example redirects old image paths into a new image directory while preserving the captured filename.
server {
listen 80;
server_name www.example.com;
location ~* \.jpg$ {
rewrite ^/images/(.*)\.jpg$ /new-images/$1.jpg permanent;
}
}
The regular expression ^/images/(.*)\.jpg$ captures the path between /images/ and .jpg. The $1 variable then places that captured value in the destination path.
Restructure Nginx URLs with Rewrite Captures
Capture groups also help when an old URL structure needs to map into a new structure.
server {
listen 80;
server_name www.example.com;
rewrite ^/category/(.*)$ /products/$1 permanent;
rewrite ^/old-structure/([0-9]+)/info$ /new-structure/$1/details permanent;
}
The second rule uses [0-9]+ so only numeric IDs match. This avoids redirecting unrelated paths that happen to share part of the old structure.
Choose Nginx Rewrite Flags Carefully
The rewrite directive supports four common flags. Use the redirect flags for client-facing redirects, and keep last or break for internal rewrites that should not send a new URL to the client.
permanentreturns a 301 redirect.redirectreturns a 302 redirect.laststops the current rewrite set and starts a new location search.breakstops the current rewrite set and continues in the current location.
For deeper internal rewrite examples, see our guide to creating rewrite rules in NGINX.
Use Practical Nginx Redirect Patterns
These patterns cover the redirects most sites need during migrations, HTTPS rollouts, canonical host cleanup, and path restructuring.
Redirect HTTP to HTTPS in Nginx
After your HTTPS server block and certificate are ready, redirect plain HTTP traffic to the HTTPS version of the same host and URI.
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
The $host variable keeps the requested hostname, while $request_uri keeps the path and query string. If you also need to choose one canonical hostname, combine HTTPS enforcement with a www or non-www redirect instead of keeping both hosts live.
Redirect Non-WWW to WWW in Nginx
Use a dedicated server block for the hostname you want to leave behind. The HTTPS listener requires a valid certificate for the non-www hostname, so do not add the SSL lines until those certificate files exist.
server {
listen 80;
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
return 301 https://www.example.com$request_uri;
}
For the reverse direction, set server_name www.example.com and redirect to https://example.com$request_uri. For a narrower canonical-host walkthrough, use our guide to redirecting non-www to www in NGINX.
Preserve Query Strings in Nginx Redirects
return 301 https://www.example.com$request_uri preserves the original path and query string because $request_uri contains both parts. When the destination path changes, use $is_args$args so NGINX adds a question mark only when query parameters exist.
| Redirect Pattern | Query String Behavior | Best Use |
|---|---|---|
https://www.example.com$request_uri | Preserves the original path and query string. | Whole-domain or canonical-host migrations. |
/new-page$is_args$args | Adds ? plus the original arguments only when arguments exist. | Path changes that should keep tracking, filters, or search parameters. |
/new-page | Drops the original query string. | Clean destination URLs that should not inherit old parameters. |
rewrite ... /new?source=legacy permanent; | Adds the new argument, then appends the original arguments. | Regex redirects that need both a marker and the old query string. |
rewrite ... /new? permanent; | Drops the original query string because the replacement ends with ?. | Regex redirects where old parameters should not survive. |
Do not append $args to $request_uri. The query string is already inside $request_uri, so adding $is_args$args after it can duplicate parameters.
server {
listen 80;
server_name www.example.com;
location = /old-page {
return 301 /new-page$is_args$args;
}
}
A request to /old-page?ref=newsletter redirects to /new-page?ref=newsletter. A request without query parameters redirects cleanly to /new-page without a dangling question mark.
Control Relative Location Headers with absolute_redirect
NGINX uses absolute_redirect on; by default, so a local URI target such as /new-page normally becomes an absolute Location header built from the request scheme, host, and port. Leave that default in place for most public redirects because absolute destinations are clearer for crawlers, caches, and clients.
Use absolute_redirect off; only when clients should receive a relative Location header from an NGINX-generated redirect.
server {
listen 80;
server_name www.example.com;
absolute_redirect off;
location = /old-page {
return 301 /new-page$is_args$args;
}
}
This setting does not rewrite redirect headers sent by a proxied application. Use proxy_redirect when the upstream response itself contains an internal or wrong public URL.
Remove Trailing Slashes with Nginx
Use a trailing-slash redirect only when your application does not rely on directory-style URLs. Some frameworks treat /about and /about/ differently.
server {
listen 80;
server_name www.example.com;
rewrite ^/(.*)/$ /$1 permanent;
}
This redirects /about/ to /about. Test application routes, generated links, and canonical tags before making this global.
Handle Advanced Nginx Redirect Scenarios
Advanced redirect rules should stay readable enough to audit later. If a redirect list grows beyond a few one-off paths, move the mapping into a dedicated structure instead of stacking many regex rules.
Use Nginx map for Multiple Redirects
The map directive belongs in the http context and creates a variable that a server block can use later. This keeps large one-to-one migrations easier to scan.
http {
map $uri $redirect_target {
default "";
/old-page-1 /new-page-1;
/old-page-2 /new-page-2;
/old-page-3 /new-page-3;
/legacy/about /company/about;
}
server {
listen 80;
server_name www.example.com;
if ($redirect_target) {
return 301 $redirect_target$is_args$args;
}
root /var/www/html;
}
}
This approach avoids a long chain of repeated location or if blocks. Keep the map close to the site configuration or in an included file that is clearly named for the migration.
Redirect by Request Method in Nginx
A limited if block that returns immediately is appropriate when one request method should redirect and others should continue to the normal handler.
server {
listen 80;
server_name api.example.com;
if ($request_method = GET) {
return 302 https://docs.example.com$request_uri;
}
location / {
proxy_pass http://127.0.0.1:8080;
}
}
Use 302 here unless the method-specific redirect is permanent. If the route is part of an API, verify clients tolerate the redirect before sending production traffic through it.
Use GeoIP Nginx Redirects Only When the Module Exists
The legacy ngx_http_geoip_module is not built by default and requires the MaxMind GeoIP library plus a database file. Use this pattern only when your installed NGINX build actually exposes the GeoIP variables.
http {
geoip_country /usr/share/GeoIP/GeoIP.dat;
server {
listen 80;
server_name www.example.com;
if ($geoip_country_code = US) {
return 302 https://us.example.com$request_uri;
}
if ($geoip_country_code = GB) {
return 302 https://uk.example.com$request_uri;
}
root /var/www/html;
}
}
Use temporary redirects for geographic routing unless you are certain each regional URL should become the permanent canonical destination for that visitor class.
Separate Nginx proxy_redirect from Normal Redirects
proxy_redirect does not redirect the original client request. It rewrites Location and Refresh headers returned by an upstream server after NGINX proxies the request.
The default form uses the current location and proxy_pass values to build the replacement. If proxy_pass uses variables, configure an explicit proxy_redirect pair instead of relying on proxy_redirect default;.
location /app/ {
proxy_pass http://127.0.0.1:8080/;
proxy_redirect http://127.0.0.1:8080/ /app/;
}
Use proxy_redirect when an upstream application sends redirects to its internal hostname and NGINX needs to translate those headers for public clients. For full proxy routing, headers, and upstream health checks, see our guide to creating a reverse proxy in NGINX.
Test and Apply Nginx Redirect Configuration
After editing a redirect rule, test the NGINX syntax, reload the service, and verify the final response headers from the URL you changed.
Test Nginx Redirect Syntax
Run the configuration test before reloading. A missing semicolon or misplaced server block can stop NGINX from accepting the new configuration.
sudo nginx -t
A valid configuration prints:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
If the test reports an error, read the file and line number in the message before reloading. Most redirect failures come from a missing semicolon, an unmatched brace, or a directive placed in the wrong context.
Reload Nginx After Redirect Changes
Reload NGINX only after the syntax test succeeds.
sudo systemctl reload nginx
If your system does not use systemd, use the NGINX signal command instead.
sudo nginx -s reload
Verify Nginx Redirect Headers
Use curl to check the status code and Location header. The -D - -o /dev/null options print response headers while discarding the body. For local origin tests before DNS or CDN routing is ready, send the request to loopback with the intended hostname in the Host header. For more request and header options, see our curl command in Linux reference.
curl --noproxy '*' -sS -D - -o /dev/null -H 'Host: old.example.com' http://127.0.0.1/page
Relevant headers include:
HTTP/1.1 301 Moved Permanently Location: https://www.example.com/page
When public DNS and TLS already route to this NGINX origin, test the public URL directly. To bypass DNS or a CDN while preserving the hostname and HTTPS SNI, add --resolve entries for each host in the chain and replace 203.0.113.10 with the origin IP. Limit followed redirects so a loop does not flood the terminal.
curl --resolve old.example.com:80:203.0.113.10 --resolve www.example.com:443:203.0.113.10 -sSL --max-redirs 10 -D - -o /dev/null http://old.example.com/page
Troubleshoot Nginx Redirect Issues
Redirect problems usually come from the wrong server block matching first, browser-cached 301 responses, overly broad regex rules, or query-string handling that does not match the intended destination.
Fix ERR_TOO_MANY_REDIRECTS in Nginx
If a browser shows this error, the redirect target is triggering another redirect back to the original URL or into another loop.
ERR_TOO_MANY_REDIRECTS
Check the redirect chain from the terminal. The grep -Ei filter keeps only the status and destination headers.
curl -sSL --max-redirs 6 -D - -o /dev/null http://example.com 2>/dev/null | grep -Ei '^(HTTP|Location)'
Relevant output often shows the loop:
HTTP/1.1 301 Moved Permanently Location: https://example.com/ HTTP/1.1 301 Moved Permanently Location: http://example.com/
- If HTTP redirects to HTTPS and HTTPS redirects back to HTTP, remove the HTTPS-to-HTTP rule.
- If www redirects to non-www and non-www redirects back to www, choose one canonical host and remove the opposing rule.
- If a regex redirect catches its own destination, narrow the pattern or add a more specific location block before it.
Fix Nginx Redirects Not Taking Effect
If curl shows the old behavior, confirm the expected server block is matching the request and that NGINX was reloaded after the syntax test.
For an HTTP virtual host, test the active local NGINX route before blaming DNS, a CDN, or browser cache.
curl --noproxy '*' -H 'Host: www.example.com' -D - -o /dev/null http://127.0.0.1/old-page
Relevant headers should show the redirect you expect:
HTTP/1.1 301 Moved Permanently Location: https://www.example.com/new-page
If the loopback probe works but the public request does not, the mismatch is likely outside the local NGINX server block. If the loopback probe still returns the wrong response, inspect the matching server_name, default server, and Nginx location block priority before changing the redirect again.
- Run
sudo nginx -tand reload only after the test succeeds. - Check that
server_namematches the hostname you are testing. - Look for a catch-all server block that may be handling the request first.
- Test in a private browser window when a previous 301 may be cached.
- Review recent NGINX errors with
sudo journalctl -u nginx --no-pager -n 50or your distribution’s NGINX error log path.
Fix Nginx Rewrite Regex Not Matching
Enable rewrite logging temporarily in the relevant server or location context when a regex rule does not match the URI you expect.
error_log /var/log/nginx/error.log notice;
rewrite_log on;
After adding or removing rewrite logging, test the configuration and reload NGINX before checking the affected URL again.
sudo nginx -t
sudo nginx -s reload
Disable rewrite_log after debugging because it can create noisy logs on busy sites.
- Escape literal dots as
\.; a bare dot matches any character. - Use
^and$anchors when a rule should match the whole URI. - Use
~*in regex locations only when matching should be case-insensitive.
Fix Lost or Duplicated Nginx Query Strings
With return, use $request_uri for whole-URI preservation or $is_args$args when changing the path. With rewrite, NGINX preserves the original query string by default unless the replacement adds a question mark.
# Query string is preserved by default.
rewrite ^/old$ /new permanent;
# Query string is discarded because the replacement ends with ?.
rewrite ^/old$ /new? permanent;
# New arguments are added, and original arguments are appended after them.
rewrite ^/old$ /new?source=legacy permanent;
If query parameters appear twice, check whether the replacement already includes $args. In many rewrite redirects, adding $args manually is unnecessary because NGINX appends the original arguments when the replacement already contains new arguments.
Fix 502 Bad Gateway While Testing Nginx Redirects
A 502 response is not a redirect status. It usually means the request reached a proxying location and the upstream server failed or was unreachable.
HTTP/1.1 502 Bad Gateway
If you expected a 301 or 302 instead, verify that your redirect location is matching before the proxy location. If the request is supposed to be proxied, troubleshoot the upstream service, socket, DNS name, or proxy_pass target in the reverse proxy configuration. For the full upstream diagnostic flow, use the dedicated guide to fix Nginx 502 Bad Gateway errors.
Conclusion
A clean NGINX redirect setup keeps each directive in its lane: return handles direct URL moves, rewrite handles capture-based reshaping, map keeps large migrations readable, absolute_redirect controls relative Location headers, and proxy_redirect stays scoped to upstream response headers. Test with nginx -t, reload NGINX, then verify the status code and Location header with curl.


Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags currently allowed in published comments:
<code>command</code>command<strong>bold</strong><em>italic</em><blockquote>quote</blockquote>