Nginx location directive matches incoming request URIs and routes them. Three modifier types: exact (=), prefix (no modifier), regex (~ for case-sensitive, ~* for case-insensitive). Priority: = exact match wins, then ^~ longest prefix, then regex, then longest prefix. The team that uses = for health checks and prefix for general routing has the right pattern.
Table of contents
- The location syntax
- Priority order
- Examples
- Common pitfalls
- The named location
- Common patterns
- Testing location matches
- FAQ
The location syntax
location [modifier] pattern {
# config
}
Modifiers:
=: exact match (highest priority).^~: prefix match, no regex check.~: case-sensitive regex.~*: case-insensitive regex.- (none): prefix match.
Priority order
Per the Nginx docs:
- Exact match
=(highest priority). ^~longest prefix - if matched, no regex checked.- Longest matching prefix (without
^~). - First matching regex (
~or~*).
The team that knows the priority has predictable routing.
Examples
# Exact match for health check
location = /healthz {
return 200 'ok';
}
# Prefix: anything starting with /api/
location /api/ {
proxy_pass http://backend;
}
# Case-sensitive regex for image files
location ~ \.(gif|jpg|png)$ {
expires 30d;
}
# Case-insensitive regex
location ~* \.(JPG|PNG)$ {
expires 30d;
}
# ^~ prefix (no regex check for these)
location ^~ /static/ {
root /var/www;
}
Common pitfalls
- Greedy regex:
location ~ /api/(.*)matches/api/,/api/foo,/api/foo/bar. The team that anchors with^and$has precise matches. - Regex overhead: regex location blocks run on every request. The team that uses prefix where possible has faster routing.
=exact doesn’t match query strings. The team that needs query-string matching uses regex.
The named location
location @fallback {
proxy_pass http://backend_upstream;
}
Referenced via try_files or error_page. The team that uses named locations has clean fallback routing.
Common patterns
# React SPA - serve index.html for any non-asset
location / {
try_files $uri /index.html;
}
# API reverse proxy
location /api/ {
proxy_pass http://api_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# Static assets with long cache
location ~* \.(js|css|png|jpg|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
Testing location matches
The team that needs to debug which location matches a URL:
# Show the matched config
nginx -T | grep -A 5 "location /problem-url"
Or use the nginx location matcher tools to simulate priority.
FAQ
What’s the priority between prefix and regex location?
Exact match (highest), then ^~ longest prefix (skips regex), then longest prefix, then first matching regex.
Why isn’t my regex location matching?
Either wrong regex syntax, or a ^~ prefix matched first. The team that uses ^~ on /api/ has the regex on /api/.* never run.
Can I have multiple location blocks for the same prefix?
Yes - different modifiers or patterns. The team that uses = /healthz plus /api/ has separate routing for health and API.
What about the trailing slash?
Location pattern is matched against the normalized URI. location /api/ matches URIs starting with /api/. The team that uses trailing slash on prefixes is consistent.
How do I match query strings?
Use if ($arg_key = value) or include in regex. The team that uses query strings has to handle this case explicitly.
If you are sizing the infrastructure for the kind of project this post covers, the RunxBuild hosting calculator is the right place to model the line items. The compute, the memory, the storage, the bandwidth, the database - each one is a separate number, and the team’s mental model for the platform is the sum of those numbers. The RunxBuild dashboard is where the team sees the actual usage in one place.
Useful related references: