Nginx Rewrite Flags: Key Differences Between ‘break’ and ‘last’ Explained with Practical Examples


10 views

The rewrite directive in Nginx is powerful but often misunderstood, particularly when it comes to flag behavior. Many developers struggle with subtle differences between break and last flags, which can lead to unexpected routing behavior.

# Example showing different processing paths
location /test {
    rewrite ^/test/foo /bar last;
    rewrite ^/test/bar /baz break;
    
    # These lines will only execute for 'break' case
    proxy_pass http://backend;
}

The critical distinction lies in how Nginx continues processing after the rewrite:

  • last: Stops current processing and starts new location search
  • break: Stops all rewrite processing immediately

Consider this configuration for handling both static assets and API requests:

location /api {
    rewrite ^/api/v1/(.*)$ /v1_handler.php?endpoint=$1 last;
    rewrite ^/api/(.*)$ /legacy_handler.php?path=$1 break;
    
    fastcgi_pass php_backend;
}

location /v1_handler.php {
    internal;
    # Special processing for modern API
}

Add these directives to observe the processing flow:

rewrite_log on;
error_log /var/log/nginx/rewrite.log notice;

Using last incorrectly can create loops:

# DON'T DO THIS - infinite loop risk
location /admin {
    rewrite ^/admin/(.*) /admin/$1 last;
}

Instead, use break when you need to prevent further rewrites:

location /secure {
    rewrite ^/secure/(.*) /internal/$1 break;
    proxy_pass http://internal_server;
}

The choice impacts Nginx processing:

  • last causes additional location matching phase
  • break continues with current location's remaining directives

Here's a complete working example demonstrating the difference:

server {
    listen 80;
    server_name example.com;
    
    location /shop {
        rewrite ^/shop/cart /checkout last;
        rewrite ^/shop/(.*) /products/$1 break;
        
        # This only executes for break case
        try_files $uri $uri/ =404;
    }
    
    location /checkout {
        # Process checkout requests
    }
}

Use last when:

  • You need to change processing context
  • Redirecting to another named location
  • Implementing API versioning

Use break when:

  • Final URL should be processed in current location
  • Preventing further rewrites
  • Handling static file rewrites

In Nginx URL rewriting, both break and last flags terminate current rewrite processing, but their subsequent behavior differs significantly:

rewrite ^/old-path /new-path break;
rewrite ^/old-path /new-path last;

The last flag stops current rewrite phase and starts a new matching cycle against the new URI, while break stops processing altogether without initiating a new cycle.

Consider this configuration showing different outcomes:

location /test/ {
    rewrite ^/test/(.*) /new/$1 last;
    rewrite ^/new/(.*) /final/$1;
}

location /new/ {
    # This block will be processed with 'last'
}

With break instead of last, Nginx would never reach the second rewrite rule nor the /new/ location block.

When to use 'last':

# Use when you want subsequent location blocks to process the rewritten URI
rewrite ^/api/v1/(.*) /internal/$1 last;

When to use 'break':

# Use for final URI processing within current context
rewrite ^/static/(.*)$ /cdn/$1 break;

Remember that last initiates a new matching cycle, which has slightly more overhead than break. For high-traffic scenarios with many rewrites, this can become noticeable.

Add these directives to observe the rewrite flow:

rewrite_log on;
error_log /var/log/nginx/rewrite.log notice;