Nginx Location Regex Optimization: Combining Multiple Path Rules for Static and Media Files


2 views

When working with Nginx configurations, it's common to encounter situations where you need to handle multiple similar paths. The initial approach often looks like this:

location ^~ /media/ {
  proxy_pass http://backend.example.com;
}

location ^~ /static/ {
  proxy_pass http://backend.example.com;
}

The optimal way to combine these is using regular expressions in a single location block:

location ~ ^/(static|media)/ {
  proxy_pass http://backend.example.com;
}

The issue wasn't with the regex pattern itself, but rather with location matching precedence in Nginx. The key points:

  • Regular expression locations (with ~ or ~*) are checked after prefix locations
  • If another regex location matched first, your rule would never execute

When not using proxy_pass, the same pattern works perfectly for serving local files:

location ~ ^/(static|media)/ {
  root /home/project_root;
}

From your access logs, we can see 404 errors indicating the location wasn't matching as expected:

xx.xx.xx.xx - - [31/Dec/2013:13:48:18 +0000] "GET /static/font-awesome/css/font-awesome.min.css HTTP/1.1" 404 200
xx.xx.xx.xx - - [31/Dec/2013:13:48:18 +0000] "GET /static/bootstrap/css/bootstrap.min.css HTTP/1.1" 404 200

The solution was to ensure your regex location appears before other matching regex locations in the config file:

server {
  # Other directives...
  
  location ~ ^/(static|media)/ {
    proxy_pass http://backend.example.com;
  }
  
  # Other location blocks...
}

Remember that in Nginx configuration, order matters when using regular expression locations. The first matching regex will be used, so you need to ensure your combined static/media location appears before any other regex locations that might capture these paths.


When working with Nginx configurations, it's common to encounter situations where multiple location blocks share identical proxy settings. Consider this typical scenario:

location ^~ /media/ {
  proxy_pass http://backend.example.com;
}

location ^~ /static/ {
  proxy_pass http://backend.example.com;
}

While this works, it violates the DRY (Don't Repeat Yourself) principle and makes maintenance harder. The logical solution would be combining them into a single regex-based location block.

The standard approach using regex matching often fails when dealing with proxy_pass:

location ~ ^/(static|media)/ {
  proxy_pass http://backend.example.com;
}

From the error logs shown, we can see 404 responses being returned despite the configuration looking correct:

xx.xx.xx.xx - - [31/Dec/2013:13:48:18 +0000] "GET /static/font-awesome/css/font-awesome.min.css HTTP/1.1" 404 200
xx.xx.xx.xx - - [31/Dec/2013:13:48:18 +0000] "GET /static/bootstrap/css/bootstrap.min.css HTTP/1.1" 404 200

The root cause wasn't the regex pattern itself, but rather the evaluation order of location blocks in Nginx. The working solution reveals the actual problem:

location ~ ^/(static|media)/ {
  root /home/project_root;
}

The issue occurs when another location block with regex matching (~ or ~*) appears earlier in the configuration and inadvertently captures these requests.

Here's the proper way to implement this, considering all edge cases:

# Put this before any other regex locations
location ~* ^/(static|media)/ {
  proxy_pass http://backend.example.com;
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

# Example of other location blocks that must come AFTER
location ~ \.php$ {
  # PHP processing
}
  • The case-insensitive match (~*) is often safer for static assets
  • Order of location blocks matters significantly
  • Always include proper proxy headers
  • Test with different asset paths (nested directories, various extensions)

When troubleshooting:

# Check which location block is being matched
add_header X-Location-Match $uri;

# Log all requests to see the matching process
log_format location_debug '$remote_addr - $remote_user [$time_local] '
                         '"$request" $status $body_bytes_sent '
                         '"$http_referer" "$http_user_agent" $request_uri '
                         'Location: $request_uri -> $uri';