Introduction

You've configured rewrite rules in Nginx to redirect URLs or transform request paths, but the rules aren't being applied. Requests go through unchanged, redirect to wrong URLs, or cause infinite loops. The rewrite configuration appears correct but doesn't work as expected.

Symptoms

```bash # Expected: /old-page redirects to /new-page $ curl -I http://localhost/old-page HTTP/1.1 200 OK # No redirect, page served as-is

# Expected: /api/v1/* redirects to /api/v2/* $ curl http://localhost/api/v1/users {"version": "v1"} # Not redirected to v2

# Infinite redirect loop $ curl -L http://localhost/page curl: (47) Maximum (50) redirects followed

# Wrong redirect destination $ curl -I http://localhost/blog/post HTTP/1.1 301 Moved Permanently Location: http://localhost/ # Wrong destination ```

Common Causes

  1. 1.Regex pattern doesn't match: Pattern syntax incorrect or doesn't match URI
  2. 2.Location block priority: Rewrite in wrong location block or overridden
  3. 3.Missing flags: No redirect flag (permanent, redirect, last, break)
  4. 4.Wrong URI variable: Using wrong variable ($uri vs $request_uri)
  5. 5.Capture group errors: Regex groups not captured correctly
  6. 6.Infinite loop: Rewrite creates circular redirect
  7. 7.Case sensitivity: Pattern doesn't match case variations
  8. 8.Order of rules: Earlier rules matching before intended rule
  9. 9.Server block mismatch: Rewrite in wrong server block

Step-by-Step Fix

  1. 1.Check logs for specific error messages
  2. 2.Verify configuration settings
  3. 3.Test network connectivity
  4. 4.Review recent changes
  5. 5.Apply corrective action
  6. 6.Verify the fix

Step 1: Test Rewrite Pattern

Verify the regex pattern matches:

```bash # Test regex matching # Check if URI matches pattern

# Use nginx -T to see effective configuration nginx -T 2>/dev/null | grep rewrite

# Test specific URI curl -v http://localhost/old-page

# Check what Nginx sees # Add debug logging ```

nginx
# Enable rewrite debug
rewrite_log on;
error_log /var/log/nginx/error.log notice;
bash
# Watch rewrite logs
tail -f /var/log/nginx/error.log | grep rewrite

Step 2: Fix Regex Pattern

Correct the rewrite regex:

```nginx # BASIC SYNTAX # rewrite REGEX REPLACEMENT [FLAG];

# Simple exact match (not regex) rewrite ^/old-page$ /new-page permanent;

# Match with capture groups rewrite ^/article/([0-9]+)$ /blog/post-$1 permanent;

# Match any ending rewrite ^/old.*$ /new permanent;

# Case-insensitive match rewrite ^/Old-Page$ /new-page permanent; # Won't match /old-page rewrite ~^/old-page$ /new-page permanent; # Case-insensitive (~ prefix)

# Common regex patterns # ^ = start of string # $ = end of string # .* = any characters # [0-9]+ = one or more digits # [a-z]+ = one or more lowercase letters # ([^/]+) = capture until slash ```

Common pattern errors: ```nginx # WRONG: Missing anchors rewrite /old-page /new-page; # Matches /anything/old-page/anything

# CORRECT: With anchors rewrite ^/old-page$ /new-page; # Matches exactly /old-page

# WRONG: Wrong capture syntax rewrite ^/article/([0-9])$ /blog/$article; # $article doesn't exist, should be $1

# CORRECT: Use capture group reference rewrite ^/article/([0-9]+)$ /blog/post-$1; ```

Step 3: Choose Correct Flag

Use appropriate rewrite flag:

```nginx # FLAGS: # last - Stop processing, restart location matching (internal redirect) # break - Stop processing, continue in current location # redirect - 302 temporary redirect (visible to client) # permanent - 301 permanent redirect (visible to client)

# Internal rewrite (URL stays same, content changes) rewrite ^/api/v1/(.*)$ /api/v2/$1 last;

# External redirect (URL changes, client sees new URL) rewrite ^/old-page$ /new-page permanent; # 301 rewrite ^/temp-page$ /new-page redirect; # 302

# Break in location (stop processing rules in this location) location /static { rewrite ^/static/(.*)$ /files/$1 break; # Continue with other directives in this location try_files $uri =404; } ```

Flag behavior: ```nginx # last - Re-evaluate locations with new URI rewrite ^/blog/(.*)$ /articles/$1 last; # After rewrite, Nginx finds new location for /articles/*

# break - Stay in current location, don't re-evaluate location /api { rewrite ^/api/old/(.*)$ /api/new/$1 break; proxy_pass http://backend; # This still executes }

# permanent/redirect - Send client to new URL rewrite ^/old$ /new permanent; # Client receives 301, makes new request to /new ```

Step 4: Fix Location Block Priority

Place rewrite in correct location:

```nginx # WRONG: Rewrite in wrong location location / { # General location rewrite ^/api/old/(.*)$ /api/new/$1; }

location /api { # This location never reached for /api/old/* proxy_pass http://backend; }

# CORRECT: Rewrite in correct location location /api { rewrite ^/api/old/(.*)$ /api/new/$1 last; proxy_pass http://backend; }

# CORRECT: Rewrite before location selection server { # Server-level rewrite (processed first) rewrite ^/api/v1/(.*)$ /api/v2/$1 last;

location /api/v2 { proxy_pass http://backend; } } ```

Location priority: ```nginx # Order of location matching: # 1. Exact match: location = /path # 2. Prefix match with ^~: location ^~ /path # 3. Regex match (in order): location ~ /pattern # 4. Prefix match: location /path

# Rewrite rules are processed in order they appear # Within same context (server or location) ```

Step 5: Fix Capture Groups

Correct capture group usage:

```nginx # Single capture rewrite ^/user/([^/]+)$ /profile?name=$1 last;

# Multiple captures rewrite ^/category/([^/]+)/item/([0-9]+)$ /products?cat=$1&id=$2 last;

# Named captures (PCRE) rewrite ^/user/(?P<name>[^/]+)$ /profile?user=$name last;

# All captures to array rewrite ^/path/(.*)$ /new/$1 last; # $1 contains everything after /path/

# Common capture patterns # ([0-9]+) - Numbers only # ([a-zA-Z]+) - Letters only # ([a-zA-Z0-9-]+) - Letters, numbers, hyphens # ([^/]+) - Anything except slash # (.*) - Everything ```

Capture group errors: ```nginx # WRONG: Reference wrong capture rewrite ^/cat/([^/]+)/id/([0-9]+)$ /item?category=$2&id=$1; # $1 is category, $2 is id, but swapped

# CORRECT: Match capture to reference rewrite ^/cat/([^/]+)/id/([0-9]+)$ /item?category=$1&id=$2;

# WRONG: Missing capture rewrite ^/user/$ /profile?name=$1; # No capture group defined

# CORRECT: Define capture rewrite ^/user/([^/]+)$ /profile?name=$1; ```

Step 6: Prevent Infinite Loops

Avoid circular redirects:

```nginx # WRONG: Infinite loop rewrite ^/blog/(.*)$ /articles/$1 last; rewrite ^/articles/(.*)$ /blog/$1 last; # Creates loop: blog -> articles -> blog -> ...

# CORRECT: Use different final path rewrite ^/blog/(.*)$ /posts/$1 last; # No matching rule for /posts/

# WRONG: Self-loop rewrite ^/page$ /page redirect; # Redirects to itself

# CORRECT: Different destination rewrite ^/page$ /new-page redirect;

# Use break to prevent loop location /api { rewrite ^/api/v1/(.*)$ /api/v2/$1 break; # break prevents further rewrite processing proxy_pass http://backend; } ```

Debug loops: ```nginx # Limit internal redirects rewrite_max 10; # Default is 10

# Watch for loops in error log tail -f /var/log/nginx/error.log | grep "rewrite or internal" ```

Step 7: Handle Query Parameters

Preserve or modify query strings:

```nginx # Query string is NOT part of $uri # $uri = path only, no query string # $request_uri = full URI including query string

# Preserve query string automatically rewrite ^/old$ /new? last; # ? preserves original query string # /old?q=test -> /new?q=test

# Replace query string rewrite ^/search$ /results?q=default last; # Original query string discarded

# Combine with captures rewrite ^/category/([^/]+)$ /products?type=$1? last; # Preserves other query params # /category/books?sort=new -> /products?type=books&sort=new

# Use $args for query string rewrite ^/old$ /new?$args last; # Same as using ?

# Modify specific parameter if ($arg_version = "v1") { rewrite ^/api/(.*)$ /api/v2/$1? last; } ```

Step 8: Use if Carefully

Conditional rewrites (use sparingly):

```nginx # if is evil - use with caution # Prefer try_files or location blocks

# Safe if usage if ($request_uri ~ ^/old-page) { return 301 /new-page; }

# Check specific conditions if ($host = "old.example.com") { rewrite ^(.*)$ http://new.example.com$1 permanent; }

# For complex conditions, use map map $uri $new_uri { default ""; /old-page /new-page; /old-blog /blog; }

if ($new_uri != "") { rewrite ^ $new_uri permanent; } ```

Step 9: Test Rewrite Rules

Comprehensive testing:

```bash # Test redirect response curl -I http://localhost/old-page # Should show Location header

# Test multiple patterns for url in "/old-page" "/article/123" "/api/v1/users"; do echo "Testing $url:" curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" http://localhost$url done

# Test with query parameters curl -I "http://localhost/old?param=value" # Check if query preserved

# Test internal rewrite (URL should stay same) curl http://localhost/api/v1/users # Response from v2 but URL shows v1

# Test external redirect (URL should change) curl -L http://localhost/old-page # Final URL should be /new-page ```

Step 10: Debug Rewrite Execution

Enable rewrite debugging:

```nginx # Enable rewrite log rewrite_log on; error_log /var/log/nginx/error.log notice;

# Or debug level error_log /var/log/nginx/error.log debug; ```

```bash # Watch rewrite execution tail -f /var/log/nginx/error.log | grep -E "rewrite|matched"

# Test and see processing curl -v http://localhost/test-url

# Check if rewrite matches # Log shows: [notice] matched "/old-page" to "/new-page" ```

Verification

Confirm rewrite rules work correctly:

```bash # Test expected redirects curl -I http://localhost/old-page # Should show: HTTP/1.1 301 Moved Permanently # Location: http://localhost/new-page

# Test pattern matching curl http://localhost/article/123 # Should return content from /blog/post-123

# Check for infinite loops curl -L --max-redirs 5 http://localhost/page # Should not hit redirect limit

# Verify query preservation curl -I "http://localhost/search?q=test" curl -I "http://localhost/results?q=test" # Query should be preserved

# Test configuration syntax sudo nginx -t # Should show: syntax is ok

# Monitor access patterns tail -f /var/log/nginx/access.log | grep -E "301|302" ```

Rewrite rules should now correctly redirect or transform URLs as configured.

  • [Nginx troubleshooting: Fix Lambda Permission Denied - Complete ](fix-lambda-permission-denied)
  • [Nginx web server troubleshooting: Fix Client Max Body Size Large Upload Nginx Issue ](client-max-body-size-large-upload-nginx)
  • [Fix Apache 502 Proxy Error](fix-apache-502-proxy-error)
  • [Fix Apache LogLevel Core Debug Configuration](fix-apache-loglevel-core-debug)
  • [Fix Cloudflare 502 Bad Gateway Error](fix-cloudflare-502-bad-gateway)

<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "Nginx Rewrite Rule Not Working", "description": "Resolve Nginx rewrite rule issues with regex patterns, location block priority, and flag configuration", "url": "https://www.fixwikihub.com/fix-nginx-rewrite-rule-not-matching", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2025-11-19T18:20:05.639Z", "dateModified": "2025-11-19T18:20:05.639Z" } </script>