nginx pitfalls
nginx does exactly what you wrote. Most of the time the surprise is that what you wrote is not what you meant.
Location matching is not top to bottom
nginx picks a location by precedence, not by order in the file:
location = /path— exact match, wins immediatelylocation ^~ /path— prefix match that stops regex checkinglocation ~ /regexand~*— regexes, in file order, first match winslocation /path— longest matching prefix, used only if no regex matched
This is why a regex block for \.(css|js)$ silently steals requests
from a prefix block you thought was handling a directory. If a prefix block must win,
write it as ^~.
root versus alias
root appends the whole request path to the value. alias
replaces the matched part of it.
location /img/ { root /var/www; } serves
/var/www/img/x.png
location /img/ { alias /var/data/; } serves
/var/data/x.png
With alias, keep the trailing slash on both sides or you get path
traversal surprises. If you can express it with root, do.
add_header does not inherit
The moment a block contains one add_header, it stops inheriting
all headers from its parent. Your security headers set at server level
vanish inside that location, which nobody notices until a scan reports it.
Put the headers in a snippet and include it in every block that sets any header of its own.
if is evil, mostly
if inside a location is only reliably safe with
return and rewrite ... last. Anything else —
especially setting variables or adding headers inside it — behaves in ways that
look like bugs. Use map, try_files or a separate
location instead.
Proxying: four headers and a trap
Behind a proxy, your application sees the proxy unless you tell it otherwise:
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;
And only trust those headers from your own proxy. An application that believes
X-Forwarded-For from anyone has no working rate limiting and no reliable
logs. See load balancing.
The trap: a trailing slash on proxy_pass changes the meaning.
proxy_pass http://app; passes the full URI;
proxy_pass http://app/; strips the matched location prefix.
Your change did not take effect
- Did you reload?
nginx -t && systemctl reload nginx - Old workers finish their connections after a reload. Wait a few seconds before concluding anything.
- Is the file actually included?
nginx -Tprints the complete configuration as nginx sees it. That one command settles most arguments. - Is something caching? Your browser, a CDN, or
proxy_cache.
Still stuck? Mail support@novogara.com — an engineer answers, at any hour. Back to the knowledge base