Effective Techniques for Debugging Nginx Rewrite Rules During Apache-to-Nginx Migration


3 views

Migrating rewrite rules from Apache's mod_rewrite to Nginx's rewrite directives can be particularly tricky when you can't see what's happening under the hood. Unlike Apache's RewriteLog and RewriteLogLevel, Nginx doesn't have built-in rewrite debugging - but there are powerful alternatives.

Start with these fundamental approaches:

# 1. Enable Nginx debug logging in your nginx.conf:
error_log /var/log/nginx/error.log debug;

# 2. For specific server blocks:
server {
    error_log /var/log/nginx/rewrite_debug.log debug;
    ...
}

Here are my go-to methods when debugging complex rewrites:

# Technique 1: Debug by return
rewrite ^/old-path /new-path?debug=1 break;
# Then check $request_uri in access logs

# Technique 2: Conditional logging
if ($request_uri ~* "(test-pattern)") {
    access_log /var/log/nginx/rewrite_test.log custom_format;
}

# Technique 3: Use echo module (ngx_http_echo_module)
location /test-rewrite {
    echo "Original URI: $uri";
    rewrite ^/test-rewrite/(.*)$ /processed/$1;
    echo "Rewritten URI: $uri";
}

For large-scale migrations, I create a test harness like this:

# test_rewrites.conf
server {
    listen 9000;
    server_name rewrite-test;

    location / {
        # Original Apache rule simulation
        if ($uri ~ ^/products/([0-9]+)) {
            set $product_id $1;
            return 200 "Apache-style match: product_id=$product_id\n";
        }

        # Nginx alternative
        location ~ ^/products/(?[0-9]+) {
            return 200 "Nginx capture: product_id=$product_id\n";
        }
    }
}

Watch out for these Apache-to-Nginx differences:

# 1. [L] flag equivalent - use 'last' in Nginx
rewrite ^/old$ /new last;

# 2. Query string handling
# Apache automatically preserves unless you use ?
# Nginx requires explicit $query_string or ?:
rewrite ^/path$ /newpath?$query_string?;

# 3. If condition limitations
# Nginx if is evil! Use location blocks instead:
location ~* "\.(jpg|png)$" {
    # Instead of if ($uri ~ "\.(jpg|png)$")
}

When examining logs, filter for these key patterns:

grep -E "rewritten redirect|test_uri|final route" /var/log/nginx/access.log

# Custom log format example:
log_format rewrite_debug '$remote_addr - $request [$uri -> $final_uri]';
access_log /var/log/nginx/rewrite.log rewrite_debug;

Remember that Nginx processes rewrite rules differently than Apache - it's not just syntax translation but also understanding the processing order and context differences.


When transitioning from Apache to Nginx, rewrite rules often become a major pain point. While Apache's mod_rewrite offers detailed logging with RewriteLog and RewriteLogLevel, Nginx requires different debugging approaches.

Here are the most effective methods to debug rewrite rules in Nginx:

# 1. Enable debug logging in nginx.conf
error_log /var/log/nginx/error.log debug;

# 2. For specific server block debugging
server {
    error_log /var/log/nginx/rewrite_debug.log debug;
    ...
}

Method 1: The Return Directive Trick

location / {
    rewrite ^/oldpath(.*)$ /newpath$1;
    return 200 "Debug: URI after rewrite is $uri";
    # Remove the return after testing
}

Method 2: Conditional Logging

location / {
    if ($request_uri ~ ^/oldpath) {
        rewrite ^/oldpath(.*)$ /newpath$1;
        access_log /var/log/nginx/rewrite.log custom_format;
    }
    ...
}
log_format rewrite_debug '$remote_addr - $remote_user [$time_local] '
                         '"$request" $status $body_bytes_sent '
                         '"$http_referer" "$http_user_agent" '
                         'Rewrite: $uri -> $request_uri';

server {
    access_log /var/log/nginx/rewrite_access.log rewrite_debug;
    ...
}

For complex rewrite scenarios, consider using OpenResty's Lua module:

location / {
    rewrite_by_lua_block {
        ngx.log(ngx.DEBUG, "Before rewrite: ", ngx.var.request_uri)
        -- Your rewrite logic here
        ngx.log(ngx.DEBUG, "After rewrite: ", ngx.var.request_uri)
    }
}
  • Difference between break and last flags
  • Infinite rewrite loops (use rewrite_log on; to detect)
  • Context limitations (some directives can't be used in certain blocks)

Apache:

RewriteEngine On
RewriteCond %{HTTP_HOST} ^example.com [NC]
RewriteRule ^(.*)$ http://www.example.com/$1 [L,R=301]

Nginx equivalent:

server {
    listen 80;
    server_name example.com;
    return 301 $scheme://www.example.com$request_uri;
}