Common Gotchas
Nginx's error messages are terse and its silences are terser. These five traps account for most "Nginx is broken" hours ever logged — each with the tell that identifies it and the fix.
💡 The Bottom Line
When Nginx misbehaves: did you actually reload? Is the right server block even matching?
Is a different location block winning? Check those three before touching anything else —
and keep /var/log/nginx/error.log open in a second terminal while you do.
1. You edited, but never reloaded
The most common gotcha is the least glamorous: config edits do nothing until
systemctl reload nginx. The tell — "my change has no effect at all, not even
breaking things." In Docker, remember the config is often mounted read-only from outside, and
the container needs docker exec nginx-lab nginx -s reload (or a restart) too.
2. The proxy_pass trailing slash
Promised in Lesson 4, delivered here as the #1 config-content gotcha:
location /api/ {
proxy_pass http://localhost:3000; # → backend sees /api/users
}
location /api/ {
proxy_pass http://localhost:3000/; # → backend sees /users
}
One character decides whether the location prefix is kept or stripped. The tell: the backend 404s through Nginx but answers curl directly — you're sending it paths it doesn't have routes for. Decide which shape your backend expects, and re-check this line first when proxied 404s appear.
3. Location matching isn't top-to-bottom
Nginx picks the winning location by rules, not file order:
exact match (= /path) wins outright; else the longest matching prefix
is remembered; but a matching regex location (~/~*) —
checked in order — overrides the prefix match. The tell: your carefully written
location /downloads/ is being ignored because a
location ~* \.(png|pdf)$ block higher up grabs those requests. When routing
baffles you, list the location blocks and apply the rules — not the line numbers.
4. 403 Forbidden that "makes no sense"
Two causes cover nearly all of it: Unix permissions (Lesson 3's Watch Out — the worker
user needs read on files and execute on every parent directory), or a directory URL with no
index file and no autoindex — Nginx won't list directories by
default, so it 403s. The error log states which; reading it beats guessing every time.
5. The default-server surprise
If no server_name matches a request's Host header, Nginx doesn't refuse —
it silently serves the default server (the first one defined for that port, unless
another says listen 80 default_server). The tell: a domain you never configured
(or a raw IP visit) serves one of your sites. Fix: define an explicit catch-all that returns
444 (Nginx's "close the connection" code), so only intended hostnames reach real sites:
server {
listen 80 default_server;
server_name _;
return 444;
}
🙋 There Are No Dumb Questions
Q: What are 502 Bad Gateway and 504 Gateway Timeout actually telling me?
A: Both mean "Nginx is fine; the backend isn't." 502 — the backend refused
the connection or answered garbage: it's crashed, not started yet, or on a different
port than proxy_pass says. 504 — the backend accepted but didn't answer in
time (default 60s): it's hung or genuinely slow. Either way, debug the app behind the
proxy, not the proxy.
⚠️ Watch Out
Uploads failing with 413 Request Entity Too Large is Nginx's default
1 MB request-body cap, not your app's bug. Raise it where you need it:
client_max_body_size 20m; in the server or location block. It's on this list
because it always strikes in production — the first time a user uploads a real photo.
🔧 Try It Yourself
Meet a 502 on purpose so you recognize it instantly later. In the lab container, add a proxy to a port where nothing listens:
location /broken/ {
proxy_pass http://localhost:9999;
}
Reload, request /broken/, get the 502 — then read
/var/log/nginx/error.log and see how precisely it names the failure
("connect() failed ... connection refused while connecting to upstream"). The error log
almost always says exactly what's wrong; the habit is looking.
✅ Quick Check
Nginx returns 502 Bad Gateway on a proxied route. Where is the problem?
Track complete. The recap pulls the whole picture together — and points at what usually sits on either side of Nginx in production.