URL redirection in NGINX allows you to send visitors from one address to another. This capability is essential for site migrations, enforcing HTTPS, consolidating duplicate content, and maintaining SEO after restructuring your website. Whether you need a simple permanent redirect or complex pattern-based rewrites, NGINX provides efficient directives that handle redirects at the server level without requiring application code changes.
This guide demonstrates how to configure URL redirects in NGINX using the return and rewrite directives. You will learn to implement permanent and temporary redirects, match URLs with regular expressions, and test your configuration to ensure redirects work correctly. By the end, you will have working redirect rules that preserve SEO value and provide a seamless experience for visitors.
Prerequisites
Before configuring redirects, ensure you have the following:
- NGINX installed and running on your server
- Root or sudo access to edit NGINX configuration files
- Basic familiarity with NGINX server block structure
- A text editor for modifying configuration files
NGINX configuration files are typically located in /etc/nginx/, with site-specific configurations in /etc/nginx/sites-available/ or /etc/nginx/conf.d/ depending on your distribution. For more details on NGINX configuration structure, see the official NGINX documentation.
Understanding Redirect Types
NGINX uses HTTP status codes to signal different redirect types to browsers and search engines. As a result, choosing the correct status code affects how search engines handle the redirect and whether browsers cache the response.
301 Permanent Redirect
- Signals that a page has permanently moved to a new location
- Search engines transfer ranking signals to the new URL
- Browsers cache this redirect, reducing future requests
- Use for domain migrations, URL restructuring, or removing old pages
302 Temporary Redirect
- Signals that the redirect is temporary and the original URL may return
- Search engines keep the original URL indexed
- Browsers do not cache this redirect permanently
- Use for maintenance pages, A/B testing, or temporary promotions
For most permanent URL changes, use 301 redirects to preserve search engine rankings. Use 302 redirects only when you genuinely intend to restore the original URL later.
Using the Return Directive for Simple Redirects
The return directive is the most efficient method for URL redirects in NGINX. Because it stops processing immediately and sends the response, it uses fewer resources than the rewrite directive. Therefore, use return when you need straightforward redirects without complex pattern matching.
Permanent Redirect with Return
To permanently redirect all traffic from one domain to another, first create a dedicated server block for the old domain. The following example redirects oldsite.com to www.newsite.com while preserving the original request URI:
server {
listen 80;
server_name oldsite.com www.oldsite.com;
return 301 https://www.newsite.com$request_uri;
}
In this configuration, the $request_uri variable appends the original path and query string to the new domain. For example, a visitor requesting http://oldsite.com/products/item?id=123 is redirected to https://www.newsite.com/products/item?id=123. This approach preserves bookmarks and external links pointing to your old domain.
Temporary Redirect with Return
For temporary redirects during maintenance or testing, use status code 302. The following example redirects all traffic from a maintenance domain to another site temporarily:
server {
listen 80;
server_name maintenance.example.com;
return 302 https://www.backup-site.com$request_uri;
}
Search engines will continue to index the original URL when they encounter a 302 redirect, which is appropriate when you plan to restore the original destination.
Redirecting Specific Paths
To redirect only certain paths rather than an entire domain, instead use a location block within your server configuration:
server {
listen 80;
server_name www.example.com;
# Redirect old blog path to new location
location = /old-blog {
return 301 https://www.example.com/blog;
}
# Redirect all URLs under /legacy/ to /new/
location /legacy/ {
return 301 https://www.example.com/new$request_uri;
}
# Handle other requests normally
location / {
root /var/www/html;
index index.html;
}
}
The = modifier in location = /old-blog matches only that exact path. Without the modifier, location /legacy/ matches any URL beginning with /legacy/.
Using the Rewrite Directive for Pattern-Based Redirects
The rewrite directive uses PCRE regular expressions to match and transform URLs. While more flexible than return, it requires slightly more processing. Consequently, use rewrite only when you need to capture parts of the URL and include them in the redirect destination.
Redirect by File Extension
To redirect requests for specific file types to a new directory, use a regular expression that matches the file extension:
server {
listen 80;
server_name www.example.com;
# Redirect all .jpg requests from /images/ to /new-images/
location ~* \.jpg$ {
rewrite ^/images/(.*)\.jpg$ /new-images/$1.jpg permanent;
}
}
In this example, the regular expression ^/images/(.*)\.jpg$ captures everything between /images/ and .jpg. Here, the parentheses create a capture group, and $1 in the replacement inserts the captured content. As a result, a request for /images/photos/sunset.jpg redirects to /new-images/photos/sunset.jpg.
The permanent flag sends a 301 redirect. To send a 302 temporary redirect, use the redirect flag instead.
Redirect with URI Restructuring
When restructuring your site’s URL scheme, similarly use capture groups to remap old paths to new ones:
server {
listen 80;
server_name www.example.com;
# Redirect /category/name to /products/name
rewrite ^/category/(.*)$ /products/$1 permanent;
# Redirect /old-structure/id/info to /new-structure/id/details
rewrite ^/old-structure/([0-9]+)/info$ /new-structure/$1/details permanent;
}
The second rule uses [0-9]+ to match one or more digits, ensuring only numeric IDs trigger the redirect. This prevents unintended matches on URLs with non-numeric paths.
Understanding Rewrite Flags
The rewrite directive supports four flags that control how NGINX processes the result:
permanent– Returns a 301 permanent redirect to the clientredirect– Returns a 302 temporary redirect to the clientlast– Stops processing current rewrite rules and restarts location matching with the new URIbreak– Stops processing rewrite rules and continues with other directives in the current location
For external redirects (sending users to a different URL), use permanent or redirect. Internal rewrites that change the URI without the client knowing require last or break instead. Our guide on creating rewrite rules in NGINX covers these patterns in detail.
Common Redirect Patterns
The following configurations address frequent redirect requirements. Each example includes the complete server block context to show where directives belong.
Redirect HTTP to HTTPS
Forcing HTTPS ensures encrypted connections for all visitors. To implement this, create a server block that listens on port 80 and redirects to the HTTPS equivalent:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
The $host variable preserves the original hostname, so requests to both example.com and www.example.com redirect to their HTTPS equivalents rather than all going to a single domain.
Redirect Non-WWW to WWW
Consolidating traffic to a canonical domain prevents duplicate content issues. To redirect the non-www version to www:
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;
}
This configuration handles both HTTP and HTTPS requests to the non-www domain, redirecting them to the www version over HTTPS. For the reverse (www to non-www), change the server_name to www.example.com and the redirect target to https://example.com$request_uri. For detailed examples, see our guide on redirecting non-www to www in NGINX.
Redirect with Query String Preservation
By default, $request_uri includes both the path and query string. However, if you need to modify the path while preserving query parameters, use $args instead:
server {
listen 80;
server_name www.example.com;
location /old-page {
return 301 /new-page?$args;
}
}
A request to /old-page?ref=newsletter redirects to /new-page?ref=newsletter.
Remove Trailing Slashes
To standardize URLs by removing trailing slashes, use a rewrite rule that captures the path without the slash:
server {
listen 80;
server_name www.example.com;
rewrite ^/(.*)/$ /$1 permanent;
}
This redirects /about/ to /about. Note that some applications expect trailing slashes for directories, so test thoroughly before implementing.
Geographic Redirects with the GeoIP Module
NGINX can redirect visitors based on their geographic location using the ngx_http_geoip_module. This module is not included in the default NGINX build and requires compilation with the --with-http_geoip_module flag, plus MaxMind GeoIP database files.
Geographic redirects require the GeoIP module compiled into NGINX and MaxMind database files installed on your server. Check if your NGINX includes this module by running
nginx -V 2>&1 | grep geoip. If absent, you may need to recompile NGINX or install a package that includes the module (such asnginx-fullon Debian/Ubuntu).
Once you have the GeoIP module configured, you can then use the $geoip_country_code variable in conditional statements:
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;
}
# Default handling for other countries
root /var/www/html;
}
}
This configuration redirects US visitors to a region-specific subdomain while serving the default site to all other visitors. Use 302 temporary redirects for geographic routing so search engines index your primary domain regardless of where their crawlers are located.
For more information on conditional logic in NGINX, see our guide on understanding NGINX if directives.
Testing and Applying Redirect Configuration
After adding redirect rules to your configuration, you must then test the syntax and reload NGINX for changes to take effect.
Test Configuration Syntax
Always verify your configuration before reloading NGINX. Syntax errors can prevent NGINX from starting:
sudo nginx -t
Expected output for a valid configuration:
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 errors, check the line numbers indicated in the error message and verify directive syntax, semicolons, and bracket matching.
Reload NGINX
Once the configuration test passes, next reload NGINX to apply changes without dropping active connections:
sudo systemctl reload nginx
Alternatively, use the older service command:
sudo nginx -s reload
Verify Redirects
Finally, test your redirects using curl with the -I flag to show headers only:
curl -I http://oldsite.com/page
Expected output for a 301 redirect:
HTTP/1.1 301 Moved Permanently Server: nginx Date: Mon, 06 Jan 2026 03:15:00 GMT Content-Type: text/html Content-Length: 162 Connection: keep-alive Location: https://www.newsite.com/page
The Location header shows where the redirect points. Confirm this matches your expected destination. To follow redirects automatically and see the final destination, use:
curl -IL http://oldsite.com/page
The -L flag follows redirects, showing each hop until reaching the final destination.
Troubleshooting Common Redirect Issues
Redirect configurations can fail silently or produce unexpected behavior. The following issues appear frequently and have straightforward solutions.
Redirect Loop Errors
If your browser displays “too many redirects” or “ERR_TOO_MANY_REDIRECTS”, your configuration creates a circular redirect where the destination also triggers a redirect back to the source.
To diagnose, check the redirect chain:
curl -IL http://example.com 2>&1 | grep -E "^(HTTP|Location)"
Common causes and fixes:
- Redirecting to the same server_name: Ensure your redirect target does not match the
server_nameof the same server block - HTTP to HTTPS loop: Verify your HTTPS server block does not also contain an HTTP redirect
- Rewrite rules without break: When using rewrites inside a location block, use the
breakflag to prevent re-processing
Redirect Not Taking Effect
If redirects work in curl but not in browsers, the browser may have cached an old redirect. In this case, clear the browser cache or test in a private/incognito window.
If redirects do not work at all:
- Verify you ran
sudo nginx -tandsudo systemctl reload nginx - Check that the
server_namematches the domain you are testing - Confirm no other server block is intercepting requests first (check for catch-all blocks)
- Review the NGINX error log:
sudo tail -f /var/log/nginx/error.log
Regular Expression Not Matching
When rewrite rules do not match expected URLs, enable the rewrite log to debug:
error_log /var/log/nginx/error.log notice;
rewrite_log on;
After reloading NGINX, check the error log for rewrite processing details. Remember to disable rewrite_log in production as it generates substantial log output.
Common regex mistakes:
- Forgetting to escape dots (
\.vs.) – a bare dot matches any character - Missing anchors – use
^for start and$for end of string - Case sensitivity – use
~*in location blocks for case-insensitive matching
Query String Lost After Redirect
If query parameters disappear after a redirect, check which variable you use. With rewrite, appending a ? at the end of the replacement stops NGINX from adding the original query string:
# Query string IS preserved (default behavior)
rewrite ^/old$ /new permanent;
# Query string IS discarded (trailing ?)
rewrite ^/old$ /new? permanent;
# Explicit query string preservation
rewrite ^/old$ /new?$args permanent;
Advanced Redirect Scenarios
Beyond basic redirects, NGINX supports complex routing scenarios that combine multiple conditions or integrate with upstream servers.
Redirect Based on Request Method
To redirect only specific HTTP methods while allowing others to pass through:
server {
listen 80;
server_name api.example.com;
# Redirect GET requests to documentation, allow POST
if ($request_method = GET) {
return 301 https://docs.example.com$request_uri;
}
location / {
proxy_pass http://backend;
}
}
Map-Based Redirects for Multiple URLs
When migrating many individual URLs, use a map block instead of multiple if statements for better performance:
http {
map $uri $redirect_uri {
/old-page-1 /new-page-1;
/old-page-2 /new-page-2;
/old-page-3 /new-page-3;
/legacy/about /company/about;
default "";
}
server {
listen 80;
server_name www.example.com;
if ($redirect_uri) {
return 301 $redirect_uri;
}
# Normal site handling
root /var/www/html;
}
}
The map block creates a lookup table evaluated once per request. This is more efficient than multiple if conditionals when handling dozens or hundreds of individual redirects.
Proxying to Upstream Servers
For load-balanced architectures, you may need to route requests to a pool of backend servers. Unlike the redirect examples above, this approach uses the upstream module with proxy_pass to forward requests internally rather than sending a redirect response to the client:
http {
upstream backend {
server backend1.example.com;
server backend2.example.com;
server backend3.example.com;
}
server {
listen 80;
server_name www.example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
This configuration proxies requests to backend servers without a visible redirect to the client. By default, NGINX distributes requests using round-robin. For session persistence, add the ip_hash directive to the upstream block to route requests from the same client IP to the same backend server.
For more details on reverse proxy configurations, see our guide on creating a reverse proxy in NGINX.
Conclusion
You now have the knowledge to implement URL redirects in NGINX using both the return and rewrite directives. The return directive handles straightforward redirects efficiently, while rewrite enables pattern-based URL transformation with regular expressions. Remember to test configurations with nginx -t before reloading, and verify redirects with curl -I to ensure the correct status codes and destination URLs. For complex migrations involving many URLs, use map blocks to maintain readable, performant configurations that scale with your site’s needs.