Location Blocks & Request Routing In Depth
Nginx In Depth
Chapter 3 · Location Blocks & Request Routing In Depth
Web Servers Fundamentals Chapter 4 introduced a plain location /path/ {} block as a way to match a request path. In practice, Nginx supports several different kinds of location match, each with its own precedence — and getting that precedence wrong is one of the most common real-world sources of "why isn't this location matching what I expect" confusion.
Location Matching Types
Nginx supports five distinct forms: an exact match (=), a prefix match that stops further checking (^~), a case-sensitive regex (~), a case-insensitive regex (~*), and a plain prefix match with no modifier at all. Each of these is a different kind of test against the request path, not just a different way of writing the same thing.
Matching Precedence: The Actual Order Nginx Checks
Nginx evaluates these in a fixed order, regardless of what order they're written in the config file: first, any exact match (=) — if one matches, Nginx stops immediately. Next, the longest matching prefix among plain and ^~ prefixes is found; if that longest match used the ^~ modifier, Nginx stops there too, skipping regex entirely. Otherwise, every regex location (~/~*) is checked in the order they appear in the config file, and the first one that matches wins. Only if no regex matches at all does Nginx fall back to that earlier plain-prefix match. Regex order genuinely matters — prefix order does not.
try_files
try_files checks a list of candidates in order and serves the first one that actually exists — conceptually the same idea as Web Servers Fundamentals Chapter 5's directory-index list, but applied to serving a request generally, not just a bare-directory request specifically. The line above is the standard single-page-application pattern: try the exact URI as a real file, then as a directory, and if neither exists, fall back to serving index.html and letting the client-side app's own router take over from there.
rewrite
rewrite uses a regular expression to rewrite the request URI itself. Adding permanent sends the client an actual HTTP 301 redirect to the new URL; redirect sends a 302; omitting both flags rewrites the URI internally — Nginx serves the new path directly without the client ever seeing a redirect at all, which is a very different, easy-to-miss outcome from what the directive's name might suggest.
| Syntax | Type | Precedence |
|---|---|---|
location = /path | Exact match | Checked first, wins immediately |
location ^~ /path/ | Prefix, stops regex checking | Checked second, wins if longest prefix |
location ~ pattern | Regex, case-sensitive | Checked in file order, first match wins |
location ~* pattern | Regex, case-insensitive | Same tier as ~, file order |
location /path/ | Plain prefix | Fallback if no regex matches |
try_files $uri $uri/ /index.html; pattern shown above once deployed behind a real Nginx server — without it, refreshing the browser on any client-side route other than the homepage returns a 404, since that path genuinely doesn't exist as a file on disk and only the app's own JavaScript router knows how to handle it.
location blocks strictly in the order they're written, the way a firewall's rule list works. That's not what happens: per this chapter's own precedence section, exact matches and the longest prefix are found algorithmically regardless of file order, and only among regex blocks specifically does file order actually decide a tie. Writing location blocks assuming top-to-bottom evaluation is one of the most common real sources of "my more specific rule isn't matching" bugs.
Hands-On Exercises
Write the Nginx location block(s) needed to serve a single-page application: any request that matches a real file or directory should be served directly, and everything else should fall back to /index.html.
📄 View solutionA config has both `location /images/ { ... }` and `location ~* \.(jpg|png)$ { ... }`, with the plain prefix block written first in the file. A request comes in for /images/photo.jpg. Explain which block actually handles it, and why the order they're written in doesn't decide the outcome here.
📄 View solutionExplain the practical difference between `rewrite ^/old/(.*)$ /new/$1 permanent;` and the same rewrite with no flag at all, from the perspective of what the client's browser actually sees.
📄 View solutionChapter 3 Quick Reference
- Five location match types: exact (=), priority prefix (^~), regex (~/~*), and plain prefix
- Precedence order: exact match → longest prefix (stopping if
^~) → regex blocks in file order → fallback to the longest plain prefix - Regex order matters; prefix order does not — a common source of routing bugs
- try_files checks candidates in order, serving the first that exists — the standard SPA fallback pattern is
try_files $uri $uri/ /index.html; - rewrite with
permanent/redirectsends a real HTTP redirect; with no flag, it rewrites the URI internally with no redirect visible to the client