Requests and static files

Match location blocks

Predict prefix, exact, and regular-expression location selection before adding nested request rules.

8 minute lesson

~~~

Inside a server block, the request URI selects one location. A request is handled by the location selected through Nginx matching rules, not just the first location in the file. Learn the rules once, and configurations stop surprising you.

There are four kinds of location:

location = /health { }      # exact match
location ^~ /assets/ { }    # prefix match, regex-proof
location ~ \.php$ { }       # case-sensitive regex
location /docs/ { }         # plain prefix match

The selection algorithm works like this. Exact matches use = and win immediately when the URI is identical. Otherwise Nginx finds the longest matching prefix among the prefix locations. If that prefix is marked ^~, it wins and regexes are skipped. Otherwise Nginx checks the regex locations in the order they appear in the file, and the first regex that matches wins. Only when no regex matches does the remembered longest prefix get used.

Read that last part again, because it’s the surprise: a regex beats a longer prefix. A location ~ \.png$ defined anywhere in the server block will capture /assets/logo.png even if you wrote a location /assets/ that looks more specific. When you want a prefix to be immune to regexes, mark it ^~.

Here is a small server that covers the common cases:

server {
  listen 80;
  server_name app.example.com;

  location = /health {
    return 200 "ok\n";
  }

  location ^~ /assets/ {
    root /var/www/app/public;
  }

  location / {
    proxy_pass http://127.0.0.1:3000;
  }
}

Exactly /health answers directly. Everything under /assets/ is served from disk, protected from any regex added later. The remaining application paths fall through to location /, which matches every URI as the shortest possible prefix.

Verify each rule, including a near miss:

curl -s http://app.example.com/health      # ok
curl -I http://app.example.com/assets/app.css   # 200 from disk
curl -I http://app.example.com/healthz     # proxied to the app, not the health check

The /healthz case matters. The = location matches only the exact URI, so /healthz skips it and lands in location /. If you expected prefix behavior there, this test catches it.

My advice is to keep the location set small enough that you can predict the winner for any URI in your head. When you can’t, run the curl tests before you trust the config.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →