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.com

CentOS/RHEL/Rocky Systems:

/etc/nginx/conf.d/yoursite.com.conf

After making changes, always test and reload:

sudo nginx -t
sudo systemctl reload nginx

Simple 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 301 sends 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_uri captures 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, $3 reference 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_uri preserves 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:

  • $host contains the requested domain name
  • Maintains www or non-www based on how users access the site
  • $request_uri preserves 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-page

Browser 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:

  1. Verify configuration syntax: sudo nginx -t
  2. Reload NGINX: sudo systemctl reload nginx
  3. Check server block order (most specific first)
  4. Clear browser cache
  5. Verify DNS points to the correct server

Performance Considerations

Use 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

  1. Use 301 for permanent changes - Transfers link equity
  2. Avoid redirect chains - Impacts crawl efficiency
  3. Update internal links - Don't rely solely on redirects
  4. Monitor 404 errors - Create redirects for broken links
  5. Keep redirects indefinitely - Some links take time to update
  6. Submit new sitemap - Help search engines discover changes
  7. Use relative redirects - Better for protocol changes