yum upgrades for production use, this is the repository for you.
Active subscription is required.
Understanding how NGINX routes requests to different location blocks is essential for anyone configuring web servers. The location directive is one of the most powerful yet frequently misunderstood features in NGINX configuration. Getting NGINX location priority wrong can break your application, create security vulnerabilities, or cause subtle bugs that are difficult to diagnose.
This comprehensive guide explains how NGINX location priority actually works, backed by insights from the NGINX source code. You’ll learn the exact matching algorithm, understand when to use each modifier, and discover common pitfalls that even experienced administrators fall into.
The Five Location Modifiers
NGINX provides five ways to define a location block, each with different matching behavior. Understanding these modifiers is the first step to mastering NGINX location priority:
| Modifier | Type | Priority Level | Description |
|---|---|---|---|
= |
Exact match | Highest | Request URI must match exactly |
^~ |
Prefix (no regex) | High | Prefix match that prevents regex evaluation |
~ |
Regex (case-sensitive) | Medium | Regular expression with case-sensitive matching |
~* |
Regex (case-insensitive) | Medium | Regular expression ignoring case |
| (none) | Prefix | Lower | Standard prefix match |
Exact Match (=) — Highest Priority
The exact match modifier = requires the request URI to match the location precisely. In the NGINX location priority order, exact matches are always checked first and return immediately.
# Only matches exactly "/"
location = / {
return 200 "Homepage";
}
# Only matches exactly "/login"
location = /login {
proxy_pass http://auth-backend;
}
With exact match:
– / matches location = / but /index.html does not
– /login matches location = /login but /login/ does not
Prefix Match with No Regex (^~)
The ^~ modifier affects NGINX location priority by telling NGINX to use this location if the prefix matches, and skip all regex locations entirely. This is critical for performance-sensitive paths where you don’t want regex evaluation overhead.
# Matches /images/ and all paths starting with /images/
# Regex locations will NOT be checked even if they match
location ^~ /images/ {
root /var/www/static;
}
# This regex will NEVER match URLs starting with /images/
location ~ \.(gif|jpg|png)$ {
expires 30d;
}
When a request for /images/photo.jpg arrives:
1. NGINX finds the ^~ prefix match for /images/
2. Because of ^~, it stops and uses this location
3. The regex \.(gif|jpg|png)$ is never evaluated
Case-Sensitive Regex (~)
The tilde ~ modifier enables regular expression matching with case sensitivity. In the NGINX location priority algorithm, regex locations are evaluated after prefix matching (unless ^~ is used).
# Matches .php at the end (case-sensitive)
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
}
This location matches /index.php but not /INDEX.PHP or /index.PHP.
For PHP-FPM configuration, see our guide on NGINX FastCGI Keepalive for optimal performance.
Case-Insensitive Regex (~*)
The ~* modifier enables regular expression matching while ignoring case differences.
# Matches .jpg, .JPG, .Jpg, etc.
location ~* \.(jpg|jpeg|gif|png|webp)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
For more on caching headers, see our comprehensive NGINX Browser Caching Guide.
Standard Prefix Match (No Modifier)
Without any modifier, NGINX performs a standard prefix match. This has lower priority in NGINX location priority than ^~ and allows regex locations to override the match.
# Matches /api/ and anything starting with /api/
location /api/ {
proxy_pass http://api-backend;
}
For proxy configuration details, check our NGINX Reverse Proxy Guide.
The Complete NGINX Location Priority Algorithm
The matching algorithm follows a specific order that determines which location block handles each request. Understanding this NGINX location priority order is crucial for correct configuration.
The Official Priority Order
- Exact match (
=) — If found, use it immediately and stop - Longest prefix match — Find the longest matching prefix (with or without
^~) - If the longest prefix has
^~— Use it and stop searching - Regex locations (
~and~*) — Check in order of definition; first match wins - Use the remembered longest prefix — If no regex matched, fall back to prefix
Visualizing the Algorithm
Request: /images/photo.jpg
Step 1: Check exact matches (=)
└─ No match found → continue
Step 2: Find longest prefix match
├─ location / (length: 1)
├─ location /images/ (length: 8) ← longest
└─ Remember this match
Step 3: Check if longest prefix has ^~
└─ If yes → USE IT and stop
└─ If no → continue to regex
Step 4: Check regex locations IN ORDER
├─ location ~ \.php$ → no match
├─ location ~* \.(jpg|png)$ → MATCH!
└─ Use first matching regex
Step 5: (Only if no regex matched)
└─ Use the remembered prefix match
Real-World NGINX Location Priority Example
Consider this configuration:
server {
listen 80;
server_name example.com;
location = / {
return 200 "A: Exact root";
}
location / {
return 200 "B: Prefix root";
}
location /api/ {
return 200 "C: Prefix api";
}
location ^~ /static/ {
return 200 "D: Prefix noregex static";
}
location ~ \.php$ {
return 200 "E: Regex PHP";
}
location ~* \.(jpg|png|gif)$ {
return 200 "F: Regex images";
}
}
Here’s what matches each request based on NGINX location priority rules:
| Request | Matched | Why |
|---|---|---|
/ |
A | Exact match takes highest priority |
/index.html |
B | Prefix / is longest, no regex matches |
/api/users |
C | Prefix /api/ is longest, no regex matches |
/api/export.php |
E | Prefix /api/ found, but regex \.php$ wins |
/static/style.css |
D | ^~ prefix prevents regex evaluation |
/static/image.jpg |
D | ^~ blocks the image regex too! |
/photos/cat.jpg |
F | Prefix / found, regex \.(jpg|png|gif)$ wins |
/test.PHP |
B | Regex \.php$ is case-sensitive, doesn’t match |
How NGINX Implements Location Matching Internally
NGINX uses a balanced binary search tree for prefix locations and a linear array for regex locations. This internal implementation explains NGINX location priority performance:
- Prefix matching is O(log n) — Very fast even with hundreds of locations
- Regex matching is O(n) — Each regex is checked in order until one matches
The source code in ngx_http_core_module.c reveals the data structure:
struct ngx_http_location_tree_node_s {
ngx_http_location_tree_node_t *left; // BST left subtree
ngx_http_location_tree_node_t *right; // BST right subtree
ngx_http_location_tree_node_t *tree; // Nested locations
ngx_http_core_loc_conf_t *exact; // Exact match (=)
ngx_http_core_loc_conf_t *inclusive; // Prefix match
u_short len;
u_char name[1]; // Location path
};
NGINX builds this tree at configuration load time. The tree structure means:
– Location block order in your config doesn’t affect prefix matching
– The longest prefix always wins, regardless of definition order
– Only regex order matters — the first matching regex wins
Regex Best Practices
Always Anchor Your Patterns
One of the most common mistakes is forgetting to anchor regex patterns. Without anchors, patterns can match anywhere in the URI, creating security vulnerabilities.
Dangerous (unanchored):
# WRONG: Matches .php ANYWHERE in the URL
location ~ \.php {
fastcgi_pass unix:/run/php-fpm/www.sock;
}
This matches:
– /index.php ✓ (intended)
– /index.phpx ✓ (unintended!)
– /path/.php/evil ✓ (security risk!)
Safe (anchored):
# CORRECT: Matches .php only at the END
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
}
The $ anchor ensures the pattern only matches at the end of the URI.
Place More Specific Regex First
Since regex locations are evaluated in definition order, place more specific patterns before general ones:
# CORRECT ORDER: More specific patterns first
location ~ ^/api/v2/admin/.*\.json$ {
# Most specific - checked first
auth_basic "Admin API";
}
location ~ ^/api/v2/.*\.json$ {
# Less specific - checked second
proxy_pass http://api-v2;
}
location ~ ^/api/.*\.json$ {
# Least specific - checked last
proxy_pass http://api-legacy;
}
If you reverse this order, requests to /api/v2/admin/users.json would match the general /api/ pattern first.
Avoid Catastrophic Backtracking (ReDoS)
Certain regex patterns can cause exponential backtracking, leading to denial of service:
# DANGEROUS: Nested quantifiers cause ReDoS
location ~ ^/(a+)+$ {
# This pattern can take exponential time on crafted input
}
# DANGEROUS: Overlapping alternatives
location ~ ^/(foo|foobar)+$ {
# Can also cause catastrophic backtracking
}
Safe patterns avoid:
– Nested quantifiers like (a+)+ or (.*)*
– Overlapping alternatives like (a|ab)+
– Unbounded repetition of groups with optional elements
Security Considerations
The Alias Path Traversal Vulnerability
When using alias with prefix locations, always include a trailing slash on both the location and alias path:
# VULNERABLE: Missing trailing slash on location
location /files {
alias /var/www/uploads/;
}
# Attack: /files../etc/passwd → /var/www/uploads/../etc/passwd
# SAFE: Trailing slash on both
location /files/ {
alias /var/www/uploads/;
}
Avoid “if” Inside Location Blocks
The if directive inside location blocks is notorious for unexpected behavior. NGINX documentation explicitly warns: “If is Evil.”
# PROBLEMATIC: if inside location
location / {
if ($request_uri ~* "\.php$") {
proxy_pass http://php-backend;
}
proxy_pass http://default-backend;
}
# BETTER: Use separate location blocks
location ~ \.php$ {
proxy_pass http://php-backend;
}
location / {
proxy_pass http://default-backend;
}
The only safe directives inside if blocks are:
– return
– rewrite ... last
– rewrite ... redirect
– rewrite ... permanent
Named Locations
Named locations (prefixed with @) are special locations that can only be reached through internal redirects:
server {
location / {
try_files $uri $uri/ @backend;
}
location @backend {
proxy_pass http://app-server;
}
}
Named locations:
– Cannot be accessed directly by clients
– Are used by try_files, error_page, and internal redirects
– Don’t participate in the normal matching algorithm
Nested Locations
NGINX supports nested location blocks for complex configurations:
location /api/ {
proxy_set_header X-Real-IP $remote_addr;
location /api/public/ {
proxy_pass http://public-api;
}
location /api/private/ {
auth_basic "Private API";
proxy_pass http://private-api;
}
}
Rules for nested locations:
– Nested locations must be prefixes of the parent location
– Regex locations cannot contain nested locations
– Exact match (=) locations cannot contain nested locations
– Child locations inherit configuration from parents
Debugging Location Matching
Using Debug Logging
Enable debug logging to see exactly which location NGINX selects:
error_log /var/log/nginx/error.log debug;
The debug log shows the matching process:
[debug] 1234#0: *1 test location: "/"
[debug] 1234#0: *1 test location: "/api/"
[debug] 1234#0: *1 using configuration "/api/"
Testing with nginx -T
The nginx -T command outputs the complete configuration after processing includes:
nginx -T
Using curl for Testing
Test individual URLs to verify matching behavior:
curl -I http://localhost/api/users
curl -ILs http://localhost/old-path
curl -s http://localhost/test-endpoint
Validating Configuration with Gixy
Gixy is a static analyzer for NGINX configurations that detects security vulnerabilities and common misconfigurations:
- Unanchored regex patterns
- Alias path traversal vulnerabilities
- HTTP response splitting
- Host header spoofing
- ReDoS patterns
Installing Gixy
pip3 install gixy
Running Gixy
gixy /etc/nginx/nginx.conf
gixy -f json /etc/nginx/nginx.conf
Example output:
==================== Results ===================
>> Problem: [alias_traversal] Path traversal via misconfigured alias.
Severity: HIGH
server {
location /i {
alias /data/images/;
}
}
==================== Summary ===================
Total issues:
High: 1
For continuous monitoring with automated security analysis, GetPageSpeed Amplify integrates Gixy checks along with performance metrics and SSL certificate monitoring.
Performance Optimization Tips
Use ^~ for Static Files
When serving static files from a specific path, use ^~ to skip regex evaluation:
location ^~ /static/ {
root /var/www;
expires 1y;
}
location ~* \.(css|js|jpg|png|gif)$ {
expires 30d;
}
Prefer Prefix Over Regex When Possible
Prefix matching uses binary search (O(log n)), while regex uses linear search (O(n)):
# FASTER: Prefix match
location /api/v1/ {
proxy_pass http://api-v1;
}
# SLOWER: Regex match
location ~ ^/api/v[0-9]+/ {
proxy_pass http://api-generic;
}
Use Exact Match for High-Traffic Endpoints
For frequently accessed URLs, exact match provides the fastest lookup:
location = / {
proxy_pass http://homepage-backend;
}
location = /health {
access_log off;
return 200 "OK";
}
For rate limiting, see our NGINX Rate Limiting Guide.
Common Configuration Patterns
PHP-FPM with Security
location ~ /\. {
deny all;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $document_root;
internal;
}
location ~ \.php$ {
return 404;
}
Static Files with Caching
location ^~ /assets/ {
root /var/www;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ {
expires 30d;
add_header Cache-Control "public";
}
API Gateway Pattern
location /api/v3/ {
proxy_pass http://api-v3;
}
location /api/v2/ {
proxy_pass http://api-v2;
}
location /api/v1/ {
return 301 /api/v2$request_uri;
}
location /api/ {
proxy_pass http://api-v3;
}
Summary
Understanding NGINX location priority is essential for building secure and performant web configurations. Remember these key points:
- Exact match (
=) has highest priority and is the fastest - Longest prefix is found first, regardless of config order
^~prefix stops regex evaluation completely- Regex locations are checked in order — first match wins
- Always anchor regex patterns with
^and$to prevent security issues - Use Gixy to validate your configuration for security vulnerabilities
- Prefer prefix over regex when possible for performance
The location directive’s flexibility is powerful, but with that power comes responsibility. Take time to understand the NGINX location priority algorithm, test your configurations thoroughly, and use tools like Gixy to catch potential issues before they reach production.
For more NGINX configuration guides, explore our articles on NGINX load balancing and TLS hardening.
