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
andlast
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;
}