Create redirects in Nginx
NGINX Redirects Guide
Introduction
Redirects are essential for managing website changes, maintaining SEO rankings, and ensuring visitors reach the correct content when URLs change. NGINX provides efficient directives for implementing redirects that handle everything from single-page redirections to complex domain migrations.
This guide covers practical redirect configurations using NGINX, demonstrating both temporary and permanent redirect techniques that you can apply to your server.
Although complex rewrite configurations are not officially supported by the Global Linux Rackspace team, we are providing this information as a reference for customers who may choose to implement it at their discretion.
Understanding Redirect Types
Before implementing redirects, choose the appropriate type for your situation:
301 - Permanent Redirect
Use this when content has permanently moved. Search engines transfer SEO value to the new location. Best for permanent domain changes and retired pages with new homes.
302 - Temporary Redirect
Signals temporary relocation without transferring SEO value. Use for maintenance pages, A/B testing, or seasonal content that will return to its original location.
For understanding the basics of Regular Expression, please visit the Regular Expression Building Blocks Apache and Nginx article.
Basic Configuration Location
Add redirect rules to your NGINX configuration file based on your setup:
Ubuntu/Debian Systems:
/etc/nginx/sites-available/yoursite.comCentOS/RHEL/Rocky Systems:
/etc/nginx/conf.d/yoursite.com.confAfter making changes, always test and reload:
sudo nginx -t
sudo systemctl reload nginxSimple Page Redirects
Redirect One Page to Another
Redirect a single page using the return directive:
server {
listen 80;
server_name example.com www.example.com;
location = /old-page.html {
return 301 /new-page.html;
}
location / {
root /var/www/example.com;
index index.html;
}
}What This Does:
- The
location =block creates an exact match for/old-page.html return 301sends a permanent redirect to the new page- Visitors typing the old URL automatically reach the new page
Redirect Multiple Specific Pages
server {
listen 80;
server_name example.com;
location = /products.html {
return 301 /shop;
}
location = /aboutus.html {
return 301 /about;
}
location = /contactform.html {
return 301 /contact;
}
}Directory and Path Redirects
Redirect an Entire Directory
Move all content from one directory to another:
server {
listen 80;
server_name example.com;
location /old-blog {
return 301 /blog$request_uri;
}
location /legacy-products {
return 301 /products$request_uri;
}
}How It Works:
$request_uricaptures the full path after the matched location- If someone visits
/old-blog/post-title, they're sent to/blog/post-title - Preserves the complete URL structure
Redirect with Pattern Matching
Use rewrite for pattern-based redirects:
server {
listen 80;
server_name example.com;
# Redirect old PHP pages to HTML
location ~ ^/pages/(.+)\.php$ {
return 301 /articles/$1.html;
}
# Redirect year-month structure to new format
location ~ ^/archive/(\d{4})/(\d{2})/(.+)$ {
return 301 /posts/$1-$2-$3;
}
}Pattern Breakdown:
~enables regular expression matching\d{4}matches four digits (year)\d{2}matches two digits (month)(.+)captures one or more characters$1,$2,$3reference captured groups
Domain-Level Redirects
Redirect Old Domain to New Domain
Redirect an entire domain while preserving paths:
server {
listen 80;
listen 443 ssl;
server_name olddomain.com www.olddomain.com;
return 301 https://newdomain.com$request_uri;
}
server {
listen 80;
listen 443 ssl http2;
server_name newdomain.com www.newdomain.com;
root /var/www/newdomain.com/public_html;
index index.html index.php;
location / {
try_files $uri $uri/ =404;
}
}Important Notes:
- Configure separate server blocks for old and new domains
- Include both HTTP (80) and HTTPS (443) listeners
$request_uripreserves complete paths and query strings
Redirect with Subdomain Mapping
# Redirect old subdomain to new subdomain
server {
listen 80;
server_name blog.olddomain.com;
return 301 https://blog.newdomain.com$request_uri;
}
# Redirect all subdomains to main domain
server {
listen 80;
server_name *.tempsite.com;
return 301 https://mainsite.com$request_uri;
}WWW and Non-WWW Redirects
Force WWW Prefix
server {
listen 80;
listen 443 ssl;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 80;
listen 443 ssl http2;
server_name www.example.com;
# Your site configuration
root /var/www/example.com;
}Remove WWW Prefix
server {
listen 80;
listen 443 ssl;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 80;
listen 443 ssl http2;
server_name example.com;
# Your site configuration
root /var/www/example.com;
}Force Secure Connection (HTTP to HTTPS)
Force Secure Connection for Entire Site
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /path/to/certificate.crt;
ssl_certificate_key /path/to/private.key;
root /var/www/example.com;
index index.html;
}Variable Explanation:
$hostcontains the requested domain name- Maintains www or non-www based on how users access the site
$request_uripreserves the complete path
Secure Connection Redirect with WWW Enforcement
server {
listen 80;
server_name example.com www.example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 443 ssl http2;
server_name www.example.com;
ssl_certificate /path/to/certificate.crt;
ssl_certificate_key /path/to/private.key;
# Site configuration
root /var/www/example.com;
}Query String and Parameter Handling
Redirect with Query Strings Preserved
server {
listen 80;
server_name example.com;
# Automatically preserves query strings
location = /search.php {
return 301 /search;
}
}Result:
/search.php?q=nginx&type=guide redirects to /search?q=nginx&type=guide
Redirect Based on Query Parameter
server {
listen 80;
server_name example.com;
location = /products {
if ($arg_category = "electronics") {
return 301 /shop/electronics;
}
if ($arg_category = "books") {
return 301 /shop/books;
}
}
}Remove Query Strings
server {
listen 80;
server_name example.com;
location = /old-page {
return 301 /new-page?;
}
}Note: The trailing ? removes query strings from the redirect.
Conditional Redirects
Redirect Based on User Agent
server {
listen 80;
server_name example.com;
location / {
if ($http_user_agent ~* "mobile|android|iphone") {
return 301 https://m.example.com$request_uri;
}
root /var/www/example.com;
}
}Redirect Based on Referrer
server {
listen 80;
server_name example.com;
location /special-offer {
if ($http_referer !~* "example.com") {
return 301 /;
}
root /var/www/example.com;
}
}Maintenance Mode Redirects
Temporary Maintenance Redirect
server {
listen 80;
server_name example.com;
location / {
return 302 /maintenance.html;
}
location = /maintenance.html {
root /var/www/example.com;
}
}Maintenance Mode with Exception
server {
listen 80;
server_name example.com;
set $maintenance 1;
# Allow specific IP to bypass maintenance
if ($remote_addr = "203.0.113.45") {
set $maintenance 0;
}
# Allow maintenance page itself
if ($request_uri ~* "^/maintenance.html$") {
set $maintenance 0;
}
if ($maintenance = 1) {
return 302 /maintenance.html;
}
root /var/www/example.com;
index index.html;
}Testing Redirects
Using cURL to Test
Test redirects from command line:
# Check redirect status and destination
curl -I http://example.com/old-page
# Follow redirects
curl -L http://example.com/old-page
# See all redirect steps
curl -IL http://example.com/old-pageBrowser Testing Considerations
- Clear browser cache before testing
- Use private/incognito mode for clean tests
- Check the browser dev tools Network tab to see the redirect chain
Common Issues and Solutions
Redirect Loops
Problem: Page redirects to itself endlessly.
Solution: Check that the redirect destination differs from the source:
# WRONG - Creates loop
location /page {
return 301 /page;
}
# CORRECT
location /old-page {
return 301 /new-page;
}Too Many Redirects
Problem: Multiple server blocks creating redirect chains.
Solution: Consolidate redirects to single blocks:
# Instead of multiple redirects, use one
server {
listen 80;
server_name example.com www.example.com;
return 301 https://www.example.com$request_uri;
}Redirects Not Working
Checklist:
- Verify configuration syntax:
sudo nginx -t - Reload NGINX:
sudo systemctl reload nginx - Check server block order (most specific first)
- Clear browser cache
- Verify DNS points to the correct server
Performance Considerations
Use return Over rewrite
return Over rewrite# FASTER - Direct return
location = /old {
return 301 /new;
}
# SLOWER - Rewrite processing
location = /old {
rewrite ^/old$ /new permanent;
}Why: return directive is more efficient as it bypasses regex evaluation.
Minimize Redirect Chains
# BAD - Two redirects
# example.com → www.example.com → https://www.example.com
# GOOD - One redirect
server {
listen 80;
server_name example.com www.example.com;
return 301 https://www.example.com$request_uri;
}Best Practices
Complete Example Configuration
Here's a comprehensive configuration incorporating multiple redirect strategies:
# Redirect all HTTP to HTTPS, enforce www
server {
listen 80;
server_name example.com www.example.com;
return 301 https://www.example.com$request_uri;
}
# Redirect non-www HTTPS to www HTTPS
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
return 301 https://www.example.com$request_uri;
}
# Main site configuration
server {
listen 443 ssl http2;
server_name www.example.com;
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
root /var/www/example.com/public_html;
index index.html index.php;
# Specific page redirects
location = /old-about.html {
return 301 /about;
}
location = /old-contact.php {
return 301 /contact;
}
# Directory redirect
location /blog-archive {
return 301 /blog$request_uri;
}
# Pattern-based redirect
location ~ ^/products/category-(.+)$ {
return 301 /shop/$1;
}
# Main location block
location / {
try_files $uri $uri/ =404;
}
}Best Practices
- Use 301 for permanent changes - Transfers link equity
- Avoid redirect chains - Impacts crawl efficiency
- Update internal links - Don't rely solely on redirects
- Monitor 404 errors - Create redirects for broken links
- Keep redirects indefinitely - Some links take time to update
- Submit new sitemap - Help search engines discover changes
- Use relative redirects - Better for protocol changes
Updated 7 days ago
