Updated:

Nginx location priority

If Nginx serves the wrong file, wrong headers, or wrong cache policy, the reason is often that a different location was selected than expected.

Knowing the selection order helps.

Main location types

location = /exact { ... }
location ^~ /assets/ { ... }
location /prefix/ { ... }
location ~ \.php$ { ... }
location / { ... }

Simplified selection order

  1. location = /exact — exact match.
  2. Longest prefix match.
  3. If that prefix used ^~, regex locations are skipped.
  4. Regex locations ~ and ~*.
  5. Normal prefix fallback, such as location /.

Exact match

location = /404.html {
    internal;
}

Exact location is useful for a single file or endpoint.

It matches only:

/404.html

Prefix match

location /assets/ {
    ...
}

Matches:

/assets/app.js
/assets/style.css

^~ prefix

location ^~ /assets/ {
    ...
}

This means: if the path starts with /assets/, select this location and do not check regex locations.

Useful when assets must not be intercepted by regex rules.

Regex location

location ~* \.(css|js)$ {
    ...
}

Regex is flexible, but it can intercept files unexpectedly.

For a simple static site, explicit prefix locations are often easier to reason about.

Typical static setup

location ^~ /assets/ {
    try_files $uri =404;
    add_header Cache-Control "public, max-age=31536000, immutable" always;
}

location = /404.html {
    internal;
}

location / {
    try_files $uri $uri/ =404;
    add_header Cache-Control "public, must-revalidate" always;
}

Why put /assets/ above generic rules

Order is not always the deciding factor, but configs are easier to read when specific rules are above the general fallback.

Good order:

  1. exact service files;
  2. assets;
  3. special static paths;
  4. generic location /.

How to see which location matched

A simple method is to temporarily add a debug header:

add_header X-Debug-Location "assets" always;

Then check:

curl -kI https://getsrv.app/assets/example.js | grep -i x-debug-location

Remove the debug header after testing.

Common mistakes

Mistake 1. Regex intercepts assets

Example:

location ~* \.(js|css|html)$ {
    add_header Cache-Control "public, must-revalidate" always;
}

JS/CSS may receive HTML cache policy instead of immutable asset policy.

Mistake 2. Generic location is too clever

location / {
    try_files $uri /index.html;
}

This can hide real 404 responses.

Mistake 3. add_header is not inherited

If a location defines its own add_header, parent add_header directives may not apply.

That is why important locations should be checked with curl -I.

Mistake 4. Multiple server blocks for one server_name

Check:

sudo grep -RIn "server_name getsrv.app" /etc/nginx/sites-enabled /etc/nginx/conf.d

If the same domain is defined in multiple places, you may be reading the wrong config.

Minimal check

curl -kI https://getsrv.app/
curl -kI https://getsrv.app/en/docs/
curl -kI https://getsrv.app/no-such-page

ASSET="$(find /var/www/getsrv.app/assets -maxdepth 1 -type f | head -n 1 | sed 's#^/var/www/getsrv.app##')"
curl -kI "https://getsrv.app$ASSET"

Check:

  • status code;
  • content-type;
  • cache-control;
  • security headers.

If HTML and assets have different expected headers, location routing is working correctly.