Nginx Location Blocks Explained: Routing Web Traffic

Nginx location blocks are the backbone of efficient web traffic routing. This comprehensive guide breaks down the five different matching modifiers (prefix, exact, longest prefix, regex) and explains the strict processing order Nginx follows. Learn how to accurately route static assets, proxy API calls, and implement security rules using practical configuration examples. Mastering location blocks is key to precise traffic control, ensuring fast server performance and robust configuration management.

Nginx Location Blocks Explained: Routing Web Traffic

Nginx location blocks decide what happens after a request lands in the right server block. They are the reason /static/app.css can be served from disk, /api/users can be proxied to an application, and /.git/config can be denied before it leaks something sensitive.

Most mistakes with location blocks are not syntax mistakes. They are priority mistakes. A regex catches a request you expected a prefix block to handle. A root path appends more URI than you thought. A proxy_pass with a trailing slash rewrites the upstream URI differently than the same directive without one. The examples below focus on those real failure points.

The Role and Anatomy of a Location Block

A location block defines how Nginx should respond to requests based on the Request URI (Uniform Resource Identifier). These blocks are always nested within a server block.

When a client makes a request (e.g., GET /images/logo.png), Nginx checks the request URI against all defined location blocks within the listening server block to determine the appropriate handling, such as serving a file, redirecting the client, or proxying the request to an application server.

Basic Syntax

The syntax requires a modifier (or lack thereof) followed by a pattern (URI):

location [modifier] [pattern] {
    # Configuration directives (e.g., root, index, proxy_pass)
}

Understanding Location Match Types (The Modifiers)

Nginx offers a small set of location match styles. The choice affects both routing precision and how much work Nginx does before it chooses a handler.

1. Prefix Match (No Modifier)

This is the default match type. Nginx searches for the longest starting string that matches the request URI.

Modifier Example Behavior Best Use Case
(none) location /blog/ Matches URIs starting with /blog/ (e.g., /blog/post/1). General purpose, defining large sections of a site.

Example:

location /docs/ {
    root /var/www/html/public;
    # If the URI is /docs/manual.pdf, Nginx looks for /var/www/html/public/docs/manual.pdf
}

2. Exact Match (=)

This modifier forces an exact match between the URI and the pattern. If matched, Nginx immediately stops searching for other locations. This is the fastest match type.

Modifier Example Behavior Best Use Case
= location = /favicon.ico Only matches the URI /favicon.ico exactly. Handling specific, frequently requested files or default pages.

3. Longest Prefix, Non-Regex (^~)

This is a specialized prefix match. If Nginx finds the longest prefix match using ^~, it immediately stops checking any regular expression (regex) location blocks, effectively overriding them.

Modifier Example Behavior Best Use Case
^~ location ^~ /assets/ Matches URIs starting with /assets/ and prevents slower regex matches from being checked. Serving static assets quickly and ensuring asset directories are handled predictably.

4. Case-Sensitive Regular Expression (~)

This uses Perl Compatible Regular Expressions (PCRE) for matching. It is powerful but slower than prefix matches. The first matching regex block wins.

Modifier Example Behavior Best Use Case
~ location ~ \.php$ Matches any URI ending in .php. Specific file type processing (e.g., passing PHP scripts to PHP-FPM).

5. Case-Insensitive Regular Expression (~*)

Identical to ~, but the matching ignores the case of the URI.

Modifier Example Behavior Best Use Case
~* `location ~* .(jpg gif png)$`

The Critical Location Processing Order

Understanding the order in which Nginx processes location blocks is vital to avoid unexpected behavior. Nginx does not simply read configuration files top-to-bottom. It uses a strict hierarchy:

  1. Exact Match (=): Nginx first checks all exact match blocks. If a match is found, processing stops immediately, and the request is handled by that block.
  2. Longest Prefix Candidate: Nginx finds the longest matching prefix location, including plain prefix locations and ^~ locations.
  3. Regex Skip for ^~: If that best prefix match uses ^~, Nginx uses it and skips regex checks.
  4. Regular Expressions (~ and ~*): If the best prefix was not ^~, Nginx checks regex locations in the order they appear in the configuration file. The first matching regex block wins.
  5. Longest Standard Prefix Match: If no regex match wins, Nginx uses the longest prefix candidate it already found. In many configs, location / is the final fallback.

This is why ^~ /static/ is common. Without ^~, a later regex such as location ~* \.(css|js)$ can still win for /static/app.css. Sometimes that is fine. Sometimes it bypasses the cache headers, root path, or access rules you expected for the whole /static/ directory.

Practical Configuration Scenarios

1. Prioritizing Static Assets for Performance

To ensure Nginx serves static files directly and quickly, preventing slower regex checks and unnecessary processing by the application server, use the ^~ modifier.

server {
    listen 80;
    server_name myapp.com;

    # 1. Exact match for the main page (highest priority)
    location = / {
        proxy_pass http://backend_app_server;
    }

    # 2. Fast handling for static assets, bypassing regex checks
    location ^~ /static/ {
        root /var/www/site;
        expires 30d;
    }

    # 3. Regular expression for common media files
    location ~* \.(gif|ico|css|js)$ {
        root /var/www/site;
        expires 7d;
    }

    # 4. Fallback for all other dynamic requests
    location / {
        proxy_pass http://backend_app_server;
    }
}

2. Routing and Proxying API Traffic

When using Nginx as a reverse proxy, location blocks direct traffic to the correct upstream application server.

location /api/v1/ {
    # Ensure Nginx respects the client connection settings
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;

    # Route all traffic starting with /api/v1/ to the backend service
    proxy_pass http://api_backend_service/v1/;

    # With a URI in proxy_pass, Nginx replaces the matching prefix.
    # /api/v1/users becomes /v1/users upstream.
}

That trailing slash behavior is worth testing. These two examples are different:

location /api/ {
    proxy_pass http://api_backend;
}

location /api/ {
    proxy_pass http://api_backend/;
}

The first form passes the original URI, including /api/..., to the upstream. The second form replaces the matched /api/ prefix with /. If your upstream suddenly starts returning 404s after a small config edit, check this before blaming the app.

3. Protecting Sensitive Directories

Location blocks can be used to deny external access to sensitive internal directories, such as configuration files or hidden directories like .git.

# Deny access to files starting with a dot (hidden files)
location ~ /\.(ht|svn|git) {
    deny all;
    return 404; # Return 404 instead of 403 to avoid revealing their existence
}

# Deny access to specific configuration directories
location /app/config/ {
    deny all;
}

For hidden files, many teams use a broader deny rule:

location ~ /\.(?!well-known/) {
    deny all;
    return 404;
}

The exception keeps /.well-known/ available for things like ACME HTTP-01 certificate challenges while still blocking most dotfiles. Test this carefully if your site serves other legitimate dot-prefixed paths.

Security Warning: Using alias vs. root

When configuring file paths within a location block, be mindful of the difference between root and alias.

  • root: Appends the full request URI to the defined path. (e.g., location /images/ + root /data/ leads to /data/images/filename.jpg)
  • alias: Replaces the matched portion of the URI with the defined path. This is often necessary when the location block uses a regex or needs to strip part of the path before serving the file. (e.g., location /static/ + alias /opt/app/files/ leads to /opt/app/files/filename.jpg)

4. Handling Trailing Slashes and Redirects

It is often desirable to enforce a consistent URL structure, such as ensuring directories always end with a trailing slash (/).

# Force a trailing slash for directory paths if missing
location ~* /[a-z0-9\-_]+$ {
    # If the URI matches a file, Nginx will try to serve it. If not, treat it as a directory.
    # Check if the requested URI maps to a directory on disk:
    if (-d $request_filename) {
        return 301 $uri/;
    }
}

A Good Way to Debug Location Routing

When a request reaches the wrong place, reduce the problem to one URL and one server block. Run nginx -T to see the full rendered configuration, including included files, then search for every location that could match that URI. Pay special attention to regex blocks because their order in the file matters.

For static files, confirm the filesystem path Nginx will build from root or alias. For proxied requests, confirm whether proxy_pass includes a URI part after the upstream name. Then reload only after nginx -t passes.

Location blocks are predictable once you internalize the priority rules, but they are unforgiving when a config grows by copy and paste. Use exact matches for tiny hot paths, ^~ for directories that should bypass regex handling, regex only where pattern matching is genuinely needed, and a plain location / as the clear fallback.