Skip to main content

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)

  1. = /exact — exact URI match (stops all further searching immediately)
  2. ^~ /prefix/ — longest matching prefix; if found, stops regex evaluation
  3. ~ regex or ~* regex — first matching regex wins (in config file order)
  4. /prefix/ — longest matching prefix (no regex evaluated before this)
  5. / — 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;
StepWhat Nginx Checks
$uriDoes the file exist exactly? (e.g., /about.html)
$uri/Does a directory exist? (serves index.html inside it)
/index.php?$argsFall 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

MistakeResultFix
Regex location before ^~ prefixRegex matches unexpectedlyUse ^~ to prevent regex search
try_files without fallbackNginx returns empty responseAlways end with =404, =403, or a named location
location /api without trailing slash + proxy_pass with trailing slashURI stripping mismatchMatch trailing slash on both or neither
Putting root inside every locationHard to maintainSet root in server block, inherit everywhere
Using if for logic inside locationSubtle bugsUse 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

PitfallWhat happensFix
Editing config without reloadingChanges not appliedsudo nginx -s reload after every edit
Not running nginx -t firstReload breaks with syntax errorAlways test syntax before reloading
Wrong socket path for PHP-FPM502 Bad Gatewayls /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.