Nginx

Nginx Location Block Matching Order and Priority Explained

4 min read by DebuggedIt

Quick answer

Quick answer: Nginx first checks for an exact match with the equals modifier. Otherwise it finds the longest matching prefix, and if that prefix uses the ^~...

Quick answer: Nginx first checks for an exact match with the equals modifier. Otherwise it finds the longest matching prefix, and if that prefix uses the ^~ modifier it stops there. If not, it tests regex locations in the order they appear in the file and uses the first match, falling back to the longest prefix when no regex matches. File order matters only for regex locations.

How Does Nginx Choose a Location?

Many configuration surprises come from assuming that the first or the most specific-looking location wins. The real algorithm has distinct phases, and prefix and regex locations are treated differently. Understanding them removes most of the guesswork.

Nginx begins by searching all prefix locations, the ones without a tilde, and remembers the longest match. If a location uses the exact modifier and matches the request URI completely, processing stops immediately and that location is used. If the longest prefix match carries the ^~ modifier, Nginx also stops and skips the regex check entirely.

Otherwise it evaluates regex locations, which start with a tilde, in the order they are written in the configuration. The first regex that matches wins. If none match, Nginx uses the longest prefix location it remembered. This means that a regex can override a longer prefix location, which is often unexpected.

  • Exact (=): matches only the full URI and wins immediately.
  • Preferential prefix (^~): a prefix match that prevents regex locations from being checked.
  • Regex (~ and ~*): case-sensitive and case-insensitive patterns, evaluated in file order.
  • Plain prefix: the fallback, chosen by longest match when no regex matches.

What Does This Look Like in Practice?

Consider a server that serves an API, static files, and a health check. The configuration below shows each type doing a job it is suited for:

server {
    location = /healthz {
        return 200 "ok";
    }

    location ^~ /static/ {
        root /var/www/app;
    }

    location ~* \.(png|jpg|css|js)$ {
        expires 7d;
    }

    location /api/ {
        proxy_pass http://app_backend;
    }

    location / {
        try_files $uri /index.html;
    }
}

A request for /static/logo.png matches the ^~ prefix, so the regex for image extensions is never consulted, and its caching rule does not apply there. A request for /images/logo.png has no preferential prefix, so the regex matches and wins over the catch-all prefix. A request for /api/users uses the /api/ prefix, because the regex does not match.

When two regex locations could match, order decides. Place more specific patterns first, and test with real URIs. Using the exact modifier for hot, simple paths such as a health check also makes the intent obvious and slightly speeds up matching.

How Does proxy_pass Treat the Path?

Location matching also interacts with proxy_pass in a way that trips many people. If proxy_pass contains a URI part, even just a trailing slash, Nginx replaces the matched location prefix with that URI. If proxy_pass has no URI part, the original request path is forwarded unchanged.

So a location /api/ with proxy_pass set to http://backend/ strips the /api/ prefix, and a request for /api/users reaches the backend as /users. With proxy_pass set to http://backend and no trailing slash, the backend receives /api/users. The rules differ for regex locations and when variables are used, so consult the official documentation before relying on a rewrite.

Avoid the if directive inside location blocks for anything beyond simple returns and rewrites. It behaves in non-obvious ways and has caused many hard-to-debug issues. Prefer separate locations, map blocks, or try_files for conditional behavior.

Frequently Asked Questions

Does the order of location blocks matter?

Only for regex locations, which are checked in file order. Prefix locations are selected by longest match regardless of where they appear.

What does ^~ do in a location block?

It marks a prefix location as preferential. If it is the longest matching prefix, Nginx uses it and does not check any regex locations.

Why is my regex location overriding a more specific prefix?

Because regex locations are checked after prefix matching and take precedence unless the prefix uses ^~ or the exact modifier. Add ^~ to protect the prefix.

How can I debug which location handled a request?

Add a custom response header or log field temporarily, or use the error log at a debug level if your build supports it. Test with curl against real URIs.

Summary

Nginx checks exact matches first, then the longest prefix, then regex locations in file order, unless a preferential prefix stops the search. Mind how proxy_pass URIs change forwarded paths, and avoid if inside locations. Confirm details in the official Nginx documentation for your version.