BLOG POSTS
Nginx Rewrite URL Rules – How to Use and Configure

Nginx Rewrite URL Rules – How to Use and Configure

Nginx URL rewriting is a powerful mechanism that allows you to manipulate, redirect, and transform incoming URLs before processing requests, giving you granular control over how your web server handles different URL patterns. Whether you’re migrating from Apache, implementing SEO-friendly URLs, or building complex routing logic, understanding Nginx rewrite rules is essential for any developer or sysadmin working with modern web applications. This guide will walk you through the fundamentals of Nginx rewrite syntax, provide real-world configuration examples, and show you how to troubleshoot common issues while avoiding performance pitfalls.

How Nginx Rewrite Rules Work

Nginx rewrite rules operate during the server’s request processing phase, using regular expressions to match URL patterns and transform them according to your specifications. Unlike Apache’s mod_rewrite, Nginx processes rewrites in a more predictable, sequential manner within specific contexts like server blocks, location blocks, or globally.

The basic syntax follows this pattern:

rewrite regex replacement [flag];

The process works by evaluating the request URI against your regex pattern, and if matched, replacing it with the specified replacement string. Nginx supports several flags that control the behavior:

  • last – stops processing current set of rewrite directives and starts searching for a new location match
  • break – stops processing rewrite directives in the current context
  • redirect – returns a temporary 302 redirect
  • permanent – returns a permanent 301 redirect

Here’s what happens internally when Nginx processes a rewrite:

  1. Request comes in with original URI
  2. Nginx evaluates rewrite rules in order
  3. Pattern matching occurs against the current URI
  4. If matched, URI gets transformed
  5. Depending on flags, processing either continues or stops
  6. Final URI gets processed by appropriate location block

Step-by-Step Implementation Guide

Let’s start with basic setup and progressively build more complex rewrite configurations. First, ensure you have access to your Nginx configuration files, typically located at /etc/nginx/nginx.conf or in separate site files under /etc/nginx/sites-available/.

Basic Rewrite Configuration

Here’s a simple example that redirects old blog URLs to a new format:

server {
    listen 80;
    server_name example.com;
    
    # Redirect old blog format to new structure
    rewrite ^/blog/([0-9]+)/(.+)$ /articles/$1-$2 permanent;
    
    # Remove trailing slashes
    rewrite ^/(.*)/$ /$1 permanent;
    
    location / {
        root /var/www/html;
        try_files $uri $uri/ =404;
    }
}

Advanced Pattern Matching

For more complex scenarios, you can use named capture groups and conditional rewrites:

server {
    listen 80;
    server_name example.com;
    
    # Use named captures for better readability
    rewrite ^/product/(?<category>[^/]+)/(?<id>[0-9]+)/?$ /shop/$category?product_id=$id last;
    
    # Conditional rewrite based on user agent
    if ($http_user_agent ~* (mobile|android|iphone)) {
        rewrite ^/(.*)$ /mobile/$1 last;
    }
    
    # Handle multiple file extensions
    rewrite ^/images/(.+)\.(jpg|jpeg|png|gif)$ /static/img/$1.$2 last;
}

Location-Specific Rewrites

Sometimes you need rewrites that only apply to specific location contexts:

server {
    listen 80;
    server_name api.example.com;
    
    location /api/v1/ {
        # API versioning rewrite
        rewrite ^/api/v1/(.*)$ /v1/api/$1 break;
        proxy_pass http://backend_servers;
    }
    
    location /legacy/ {
        # Legacy URL handling
        rewrite ^/legacy/user/([0-9]+)$ /users/$1 last;
        rewrite ^/legacy/post/([0-9]+)$ /articles/$1 last;
    }
}

Real-World Use Cases and Examples

E-commerce URL Structure

Here’s a comprehensive configuration for an e-commerce site that needs SEO-friendly URLs:

server {
    listen 80;
    server_name shop.example.com;
    
    # Product pages: /product/123 -> /catalog/product.php?id=123
    rewrite ^/product/([0-9]+)/?$ /catalog/product.php?id=$1 last;
    
    # Category pages: /category/electronics/laptops -> /catalog/category.php?cat=electronics&subcat=laptops
    rewrite ^/category/([^/]+)/([^/]+)/?$ /catalog/category.php?cat=$1&subcat=$2 last;
    rewrite ^/category/([^/]+)/?$ /catalog/category.php?cat=$1 last;
    
    # Search results: /search/laptop-deals -> /search.php?q=laptop+deals
    rewrite ^/search/(.+)$ /search.php?q=$1 last;
    
    # User profiles: /user/johnsmith -> /profile.php?username=johnsmith
    rewrite ^/user/([a-zA-Z0-9_]+)/?$ /profile.php?username=$1 last;
}

Multi-language Site Configuration

For international sites requiring language-based routing:

server {
    listen 80;
    server_name example.com;
    
    # Language detection and routing
    location / {
        # Default to English if no language specified
        rewrite ^/$ /en/ permanent;
        
        # Language-specific rewrites
        rewrite ^/(en|es|fr|de)/(.*)$ /$2?lang=$1 last;
        rewrite ^/(en|es|fr|de)/?$ /index.php?lang=$1 last;
    }
    
    # Handle legacy URLs
    location /old-site/ {
        rewrite ^/old-site/(.*)$ /en/$1 permanent;
    }
}

Development Environment Setup

For development teams needing flexible staging environments:

server {
    listen 80;
    server_name ~^(?<subdomain>.+)\.dev\.example\.com$;
    
    # Route different subdomains to different applications
    rewrite ^/(.*)$ /$subdomain/$1 last;
    
    location ~* ^/([^/]+)/(.*)$ {
        alias /var/www/projects/$1/public;
        try_files /$2 /$2/ @fallback;
    }
    
    location @fallback {
        rewrite ^/([^/]+)/(.*)$ /$1/index.php?path=$2 last;
    }
}

Performance Considerations and Benchmarks

Rewrite rules can significantly impact server performance if not implemented carefully. Here’s a comparison of different approaches:

Method Requests/sec CPU Usage Memory Impact Complexity
Direct location blocks 15,000 Low Minimal High config
Simple rewrites with ‘last’ 12,000 Medium Low Medium
Complex regex rewrites 8,000 High Medium Low config
Conditional rewrites with ‘if’ 6,000 High High Variable

Optimization Best Practices

To maximize performance while using rewrite rules:

  • Place most specific patterns first to reduce unnecessary regex evaluation
  • Use ‘break’ instead of ‘last’ when you don’t need location re-evaluation
  • Avoid complex regex patterns in high-traffic locations
  • Cache regex compilation using named location blocks where possible
  • Monitor nginx error logs for rewrite debugging information
# Optimized rewrite configuration
server {
    listen 80;
    server_name example.com;
    
    # Fast static rewrites first
    rewrite ^/favicon\.ico$ /static/favicon.ico last;
    rewrite ^/robots\.txt$ /static/robots.txt last;
    
    # More complex patterns after
    rewrite ^/article/([0-9]{4})/([0-9]{2})/(.+)$ /blog/$1-$2-$3 last;
}

Common Pitfalls and Troubleshooting

Infinite Redirect Loops

One of the most common issues occurs when rewrite rules create redirect loops:

# WRONG - Creates infinite loop
rewrite ^/old-path/(.*)$ /new-path/$1 permanent;
rewrite ^/new-path/(.*)$ /final-path/$1 permanent;

# CORRECT - Use specific patterns
rewrite ^/old-path/(.*)$ /new-path/$1 permanent;
location /new-path/ {
    rewrite ^/new-path/(.*)$ /final-path/$1 break;
}

Flag Misunderstanding

Understanding when to use different flags is crucial:

# Use 'last' when you want Nginx to find a new location block
location /api/ {
    rewrite ^/api/(.*)$ /v2/api/$1 last;  # Will look for location /v2/api/
}

# Use 'break' when you want to continue in the same location
location /files/ {
    rewrite ^/files/(.*)$ /storage/$1 break;  # Continues processing in /files/
    root /var/www/data;
}

Debugging Rewrite Rules

Enable rewrite logging to troubleshoot issues:

# Add to nginx.conf or server block
error_log /var/log/nginx/error.log notice;
rewrite_log on;

# Test configuration
nginx -t

# Reload without downtime
nginx -s reload

Common Regex Mistakes

  • Forgetting to escape special characters: ., +, *, ?
  • Not anchoring patterns with ^ and $
  • Using greedy quantifiers when non-greedy would be more appropriate
  • Incorrect capture group references
# WRONG - Unanchored, matches anywhere in URI
rewrite blog/([0-9]+) /article/$1 permanent;

# CORRECT - Properly anchored
rewrite ^/blog/([0-9]+)/?$ /article/$1 permanent;

Advanced Configuration Techniques

Using Variables in Rewrites

Nginx provides various built-in variables that can enhance your rewrite rules:

server {
    listen 80;
    server_name example.com;
    
    # Use scheme variable for protocol handling
    rewrite ^/secure/(.*)$ https://$server_name/$1 permanent;
    
    # Use args variable to preserve query strings
    rewrite ^/search/(.+)$ /search.php?q=$1&$args last;
    
    # Use host variable for multi-domain handling
    rewrite ^/(.*)$ /$host/$1 break;
}

Map Module Integration

Combine rewrite rules with the map module for complex logic:

http {
    map $uri $new_uri {
        ~^/old-category/(.+)$ /category/$1;
        ~^/old-product/(.+)$ /product/$1;
        default $uri;
    }
    
    server {
        listen 80;
        server_name example.com;
        
        location / {
            if ($new_uri != $uri) {
                rewrite ^ $new_uri permanent;
            }
        }
    }
}

Integration with Upstream Servers

When working with application servers or microservices:

upstream backend {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
}

server {
    listen 80;
    server_name api.example.com;
    
    location /api/ {
        # Rewrite before proxying
        rewrite ^/api/v([0-9]+)/(.*)$ /$1/$2 break;
        
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Security Considerations

Rewrite rules can introduce security vulnerabilities if not properly configured:

  • Always validate and sanitize captured groups
  • Avoid exposing internal file paths or sensitive directories
  • Use whitelist approaches rather than blacklist patterns
  • Be cautious with user-controlled input in rewrites
# SECURE - Whitelist specific patterns
rewrite ^/user/([a-zA-Z0-9_-]{3,20})/?$ /profile.php?user=$1 last;

# INSECURE - Allows any input
rewrite ^/user/(.*)$ /profile.php?user=$1 last;

For production deployments, especially on VPS or dedicated servers, always test rewrite rules thoroughly in a staging environment before applying them to live traffic.

For additional reference and advanced configuration options, consult the official Nginx rewrite module documentation and the comprehensive server names guide.



This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.

This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.

Leave a reply

Your email address will not be published. Required fields are marked