Nginx Location Matching Rules Explained with Real Examples
You've probably tweaked an Nginx config, reloaded, and wondered why your location block just doesn't seem to apply. Maybe a static file request is hitting your PHP handler, or an API route is being swallowed by a catch-all. The culprit is often Nginx's location matching algorithm—it's not as straightforward as it looks.
In this guide, we'll break down how Nginx selects a location block, with real examples you can test. By the end, you'll know exactly which block wins and why.
How Nginx Location Matching Works
When a request comes in, Nginx compares the URI against all defined location blocks. The matching process follows a specific order:
- Exact match (
=) — highest priority. If found, Nginx stops and uses it. - Longest prefix match — Nginx remembers the longest matching prefix location.
- Regular expression match (
~or~*) — checked in order of appearance. The first regex that matches wins, overriding the prefix match (unless^~is used). - Prefix match with
^~— if the longest matching prefix has^~, Nginx skips regex checking and uses it. - If no regex matches, the longest prefix match is used.
That's the core algorithm. Let's see it in action.
Location Modifiers: A Quick Reference
| Modifier | Syntax | Match Type | Priority |
|---|---|---|---|
= | location = /path | Exact | Highest |
^~ | location ^~ /path | Prefix (no regex) | High |
~ | location ~ \.php$ | Regex (case-sensitive) | Medium |
~* | location ~* \.(jpg|png)$ | Regex (case-insensitive) | Medium |
| none | location /path | Prefix | Lowest |
Real-World Example 1: Static Files vs. PHP
Consider this common setup:
server {
listen 80;
server_name example.com;
location / {
root /var/www/html;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
}
A request for /index.php matches the regex \.php$, so it goes to PHP-FPM. A request for /logo.png doesn't match the regex, so it falls back to the prefix / and serves the static file. Simple, right?
But what if you want to serve /uploads/photo.php as a static file (maybe it's actually an image)? You can add an exact match:
location = /uploads/photo.php {
root /var/www/html;
}
Now that exact match takes precedence over the regex.
Real-World Example 2: API Routing with Prefix and Regex
Suppose you have an API under /api/ and you want to proxy all requests to a backend, except for a health check that returns a static response.
location = /api/health {
return 200 "OK";
}
location /api/ {
proxy_pass http://backend;
}
location ~ ^/api/v[0-9]+/special {
proxy_pass http://special-backend;
}
Let's trace /api/health: exact match wins, returns 200. For /api/users: no exact, no regex (doesn't match special), so longest prefix /api/ is used. For /api/v1/special: regex matches, so it overrides the prefix and goes to special-backend.
This demonstrates how regex can override a prefix match. If you wanted the prefix to always win, you'd use ^~ instead.
Real-World Example 3: The Power of ^~
Imagine you have a directory /static/ with files that should never be processed by PHP, even if they end with .php. Using ^~ ensures Nginx doesn't check regex locations.
location ^~ /static/ {
root /var/www/html;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
A request for /static/script.php matches the prefix ^~ /static/. Because of ^~, Nginx skips regex checking and serves the file directly. Without ^~, the regex would match and PHP-FPM would execute it—a potential security risk.
Common Pitfalls and How to Avoid Them
- Regex order matters: Nginx checks regex locations in the order they appear in the config. Put more specific patterns first.
- Missing trailing slashes:
location /apiandlocation /api/are different. The former matches/apiand/apix, while the latter matches/api/and/api/users. Be precise. - Overlapping prefixes: The longest prefix wins, so
location /api/v1takes precedence overlocation /apifor/api/v1/users. - Forgetting
^~for static assets: Use it to prevent regex from hijacking static file requests. - Case sensitivity:
~is case-sensitive,~*is not. Use~*for file extensions.
Debugging Location Matching
If you're unsure which location is being used, enable debug logging in Nginx. Add error_log /var/log/nginx/error.log debug; in your server block, reload, and check the log. You'll see lines like test location: "/api/users" and using configuration "/api/".
Alternatively, use return 200 "matched: /api/"; temporarily in each location to see which one responds.
FAQ
What is the priority order of Nginx location modifiers?
Exact match (=) is highest, then longest prefix with ^~, then regex (~ or ~*) in order, then the longest prefix match without ^~.
Can a regex location override a prefix location?
Yes, unless the prefix location uses ^~. Regex locations are checked after the longest prefix match, and if a regex matches, it takes precedence over a regular prefix.
How do I match a location only for a specific file?
Use an exact match with =, e.g., location = /favicon.ico. This ensures only that exact URI is matched.
Master Your Nginx Configuration
Understanding location matching is key to a reliable Nginx setup. Test your configs with nginx -t and use debug logs when in doubt. If you're dealing with Nginx logs and need to analyze traffic patterns, try our Nginx Log Analyzer to parse and visualize your logs quickly.