Troubleshooting Nginx Common Errors: A Practical Guide
Encounter Nginx errors? This practical guide helps you diagnose and resolve common issues. Learn to tackle configuration problems, permission denied errors, connection refused, 502/504 gateway errors, and more. We provide clear explanations, actionable solutions, and essential Nginx commands to keep your site accessible and running smoothly.
Troubleshooting Nginx Common Errors: A Practical Guide
Troubleshooting Nginx common errors gets much easier when you stop treating the status code as the whole problem. A 502 might be a crashed app, a wrong socket path, or a backend that returned broken headers. A 403 might be file permissions, a missing index file, or a deny rule that matched more than you expected.
Start with evidence: the error log, the access log, nginx -t, and the backend logs if Nginx is proxying to an app. Guessing from the browser page alone wastes time.
Start With the Fast Checks
Run these before changing the config:
sudo nginx -t
sudo systemctl status nginx --no-pager
sudo tail -n 80 /var/log/nginx/error.log
sudo tail -n 80 /var/log/nginx/access.log
nginx -t tells you whether the config can be parsed. The service status tells you whether Nginx is running. The logs tell you what Nginx saw while handling the request.
If the problem is tied to one URL, reproduce it with curl so you can see headers and status clearly:
curl -I https://example.com/problem/path
curl -v https://example.com/problem/path
If the issue only happens for a specific host name, include it:
curl -I -H 'Host: app.example.com' http://127.0.0.1/
That separates Nginx routing from DNS, CDN, and load balancer behavior.
Nginx Will Not Start or Reload
A startup failure is usually a syntax error, duplicate directive in the wrong context, missing include file, or certificate path problem.
Use:
sudo nginx -t
sudo journalctl -u nginx -n 100 --no-pager
Common messages are direct:
unknown directivemeans a typo, unsupported module, or directive placed in the wrong Nginx build.directive is not allowed heremeans the directive is in the wrong context, such as putting anhttp-only directive insideserver.cannot load certificatemeans the path is wrong, the file is missing, or permissions block Nginx from reading it.bind() to 0.0.0.0:80 failedoften means another process already uses the port.
Check ports with:
sudo ss -tulnp | grep ':80\|:443'
Do not reload until nginx -t passes. A failed reload usually keeps the old worker process running, but a failed restart can take the site down.
(13: Permission denied) and 403 Forbidden
Permission denied appears in the error log when the Nginx worker user cannot read a file or traverse a directory. A browser may see 403 Forbidden.
First find the worker user:
grep -R '^user ' /etc/nginx/nginx.conf
On Debian and Ubuntu it is often www-data. On RHEL-compatible systems it is often nginx.
For a static root like /var/www/html, Nginx needs execute permission on every parent directory and read permission on the file:
sudo find /var/www/html -type d -exec chmod 755 {} \;
sudo find /var/www/html -type f -exec chmod 644 {} \;
Avoid reflexively running chown -R www-data:www-data on application directories. It may make the error disappear while giving the web server write access it does not need. A safer pattern is to keep deploy ownership separate and grant Nginx read access.
If permissions look fine, check whether the request is for a directory with no index file:
location / {
root /var/www/html;
index index.html index.htm;
}
If /docs/ has no index.html and autoindex is off, Nginx returns 403. That is correct behavior.
SELinux can also cause permission-style failures on RHEL-family systems. If file permissions are correct but Nginx still cannot read or proxy, check audit logs before disabling SELinux:
sudo ausearch -m avc -ts recent
(111: Connection refused) and 502 Bad Gateway
Connection refused means Nginx tried to connect to an upstream and the target actively refused it. The app may be down, listening on a different address, or using a socket path that does not exist.
For this proxy:
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
check:
sudo ss -tulnp | grep 3000
curl -I http://127.0.0.1:3000
sudo systemctl status your-app --no-pager
If Nginx uses a Unix socket, check the socket file:
ls -l /run/gunicorn/app.sock
The worker user must be able to read and write the socket.
A 502 Bad Gateway is broader. Nginx reached something upstream, but the response was invalid, incomplete, closed too early, or not what Nginx expected. Check the app logs at the same timestamp as the Nginx error. That timestamp match matters; otherwise you end up chasing yesterday's exception.
For PHP-FPM, verify fastcgi_pass matches the configured pool socket or port. For example:
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
Then check:
sudo systemctl status php8.3-fpm --no-pager
ls -l /run/php/php8.3-fpm.sock
504 Gateway Timeout
A 504 means Nginx waited for the upstream and did not get a timely response. Increasing timeouts can be valid for reports, imports, or slow admin operations, but it should not be the only fix for a public endpoint that is slow on every request.
location /api/reports/ {
proxy_pass http://127.0.0.1:3000;
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 180s;
}
Then look at backend timing. Add upstream timing to access logs if you do not already have it:
log_format proxy_timing '$remote_addr "$request" $status '
'request_time=$request_time '
'upstream_time=$upstream_response_time';
If $upstream_response_time is high, the app or database is probably where to focus. If Nginx shows timeouts but the app logs say it responded quickly, check network hops, container networking, DNS resolution inside the host, or a load balancer between Nginx and the app.
413 Request Entity Too Large
413 is usually straightforward: the request body is larger than Nginx allows. Uploads, large JSON payloads, and form posts can trigger it.
Set client_max_body_size in the narrowest useful place:
server {
server_name example.com;
location /upload/ {
client_max_body_size 100M;
proxy_pass http://127.0.0.1:3000;
}
}
Setting a huge global limit is rarely a good idea. Most routes do not need it, and larger allowed bodies increase the amount of work a bad client can force onto your stack.
SSL and Certificate Errors
TLS failures often look different depending on where you notice them. Nginx may fail to reload, browsers may warn about the certificate, or clients may fail a handshake.
Check certificate dates:
openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -dates -subject -issuer
Check what the public service presents:
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -subject -issuer
If those two commands show different certificates, a load balancer, CDN, or different host may be serving traffic.
400 Bad Request and Header Problems
400 usually means Nginx did not accept the request syntax. With real browsers it is less common, but bots, old clients, unusual proxies, or oversized headers can cause it.
Look for messages such as client sent too large request header. If a legitimate SSO or application cookie is too large, you may need to adjust header buffer settings. Treat that as a targeted fix, not a first response. Oversized cookies are often better fixed in the application.
A Practical Order of Operations
When an Nginx issue is live, use a repeatable order:
- Confirm the exact URL, host, status code, and time.
- Check
nginx -tand service status. - Match the request in the access log.
- Match the same timestamp in the error log.
- If proxying, check the upstream service status and logs.
- Test the upstream directly from the Nginx host.
- Make the smallest config change that fits the evidence.
- Run
nginx -t, reload, and watch logs while retesting.
Most Nginx problems become ordinary once you know where the failure happened: before Nginx, inside Nginx, between Nginx and the upstream, or inside the upstream service.