Location Blocks
Learning Focus
Leave this lesson with a working understanding of location blocks that you can apply immediately in production.
Location blocks define what Nginx does with requests based on their URI. The matching rules have a precise priority order — understanding this is one of the most important Nginx concepts.
Location Matching Syntax
location = /exact { } # Exact match — highest priority
location ^~ /prefix/ { } # Prefix match — stops regex search if matched
location ~ \.php$ { } # Case-sensitive regex
location ~* \.(jpg|png)$ { } # Case-insensitive regex
location /fallback/ { } # Prefix match (lower priority than ^~)
location / { } # Catch-all prefix (lowest priority)
Priority Order (Highest to Lowest)
= /exact— exact URI match (stops all further searching immediately)^~ /prefix/— longest matching prefix; if found, stops regex evaluation~ regexor~* regex— first matching regex wins (in config file order)/prefix/— longest matching prefix (no regex evaluated before this)/— catch-all (used if nothing else matches)
Priority Is Confusing
The most common mistake is assuming longer prefix matches beat regexes. They don't — unless you use ^~. Always test with nginx -T and a curl request.
Common Location Patterns
Catch-All with try_files (Most Sites)
location / {
try_files $uri $uri/ =404;
}
try_files checks in order: file at $uri → directory at $uri/ → return 404.
WordPress / CMS Routing
location / {
try_files $uri $uri/ /index.php?$args;
}
Falls back to index.php with the original query args — standard for any PHP CMS.
PHP-FPM Pass
location ~ \.php$ {
include fastcgi.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
Exact Match for Favicon (Skip Logging)
location = /favicon.ico {
access_log off;
log_not_found off;
}
Static Assets with Long Cache
location ~* \.(css|js|png|jpg|jpeg|gif|ico|webp|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
Block Sensitive Files
location ~* \.(env|log|ini|bak|sql|sh|conf|htpasswd)$ {
deny all;
return 404;
}
location ~ /\. {
deny all; # Block all hidden files/dirs (.git, .env, etc.)
}
Reverse Proxy to Node.js
location /api/ {
proxy_pass http://127.0.0.1:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Named Location (for internal redirects)
location @fallback {
proxy_pass http://127.0.0.1:3000;
}
location / {
try_files $uri $uri/ @fallback;
}
try_files Explained
try_files is one of the most powerful directives. It tests a list of paths and serves the first one that exists.
try_files $uri $uri/ /index.php?$args;
| Step | What Nginx Checks |
|---|---|
$uri | Does the file exist exactly? (e.g., /about.html) |
$uri/ | Does a directory exist? (serves index.html inside it) |
/index.php?$args | Fall back to this — pass to PHP |
# For SPAs (React, Vue, Angular) — always serve index.html
location / {
try_files $uri $uri/ /index.html;
}
Debugging Location Matching
# See entire merged config (check which locations are defined)
sudo nginx -T | grep -A3 "location"
# Test a specific URL routing (nginx -t doesn't test routing, use curl)
curl -v http://localhost/test.php -H "Host: example.com" 2>&1 | grep -E "< HTTP|< Content"
# Add temporary debug logging to a location
location ~ \.php$ {
access_log /var/log/nginx/php-requests.log;
# ... rest of config
}
Common Mistakes
| Mistake | Result | Fix |
|---|---|---|
Regex location before ^~ prefix | Regex matches unexpectedly | Use ^~ to prevent regex search |
try_files without fallback | Nginx returns empty response | Always end with =404, =403, or a named location |
location /api without trailing slash + proxy_pass with trailing slash | URI stripping mismatch | Match trailing slash on both or neither |
Putting root inside every location | Hard to maintain | Set root in server block, inherit everywhere |
Using if for logic inside location | Subtle bugs | Use map at http level instead |
Hands-On Practice
# Verify Nginx is running
sudo systemctl status nginx
# Test config syntax
sudo nginx -t
# Reload without downtime
sudo nginx -s reload
# Check error log
sudo tail -20 /var/log/nginx/error.log
Common Pitfalls
| Pitfall | What happens | Fix |
|---|---|---|
| Editing config without reloading | Changes not applied | sudo nginx -s reload after every edit |
Not running nginx -t first | Reload breaks with syntax error | Always test syntax before reloading |
| Wrong socket path for PHP-FPM | 502 Bad Gateway | ls /run/php/ and verify the exact socket filename |
What's Next
- Continue to the next lesson in this module, or go to the module index for an overview.
- Use the Cheatsheets for quick CLI reference.