Troubleshooting Matrix
Learning Focus
Leave this lesson with a working understanding of troubleshooting matrix that you can apply immediately in production.
Find your symptom, get the fix. Start with the diagnostic commands, then jump to the relevant section.
First Response — Always Run These
# 1. Config syntax OK?
sudo nginx -t
# 2. What's in the error log?
sudo tail -50 /var/log/nginx/error.log
# 3. Service running?
sudo systemctl status nginx
# 4. Ports listening?
sudo ss -tlnp | grep nginx
# 5. PHP-FPM OK?
sudo systemctl status php8.4-fpm
ls -la /run/php/php8.4-fpm.sock
# 6. Test response
curl -I http://localhost/
curl -H "Host: example.com" -I http://localhost/
502 Bad Gateway
Meaning: Nginx can't reach the backend (PHP-FPM or upstream app).
| Cause | Error in Log | Fix |
|---|---|---|
| PHP-FPM not running | connect() to ... failed: No such file | systemctl start php8.4-fpm |
| Wrong socket path | connect() failed ... No such file or directory | ls /run/php/ and fix path in nginx config |
| Socket permission denied | connect() failed ... Permission denied | Set listen.owner = www-data in FPM pool config |
| Upstream app not running | connect() to 127.0.0.1:3000 failed | Start your Node.js/Python app |
| FPM out of workers | upstream timed out under load | Increase pm.max_children |
# Diagnose 502 step by step
sudo tail -30 /var/log/nginx/error.log | grep "502\|upstream\|connect"
# Is PHP-FPM running?
sudo systemctl status php8.4-fpm
# Does the socket exist?
ls -la /run/php/php8.4-fpm.sock
# What socket path does Nginx expect?
sudo grep "fastcgi_pass" /etc/nginx/sites-enabled/*.conf
# Is the upstream app responding?
curl -I http://127.0.0.1:3000/
504 Gateway Timeout
Meaning: Backend responded too slowly — Nginx gave up waiting.
| Cause | Fix |
|---|---|
| Slow PHP script | Increase fastcgi_read_timeout in Nginx config |
| Slow database query | Check slow query log, add indexes |
| Upstream app slow | Increase proxy_read_timeout |
PHP max_execution_time too low | Increase in php.ini |
# In location block — increase timeouts
fastcgi_read_timeout 300; # For PHP
proxy_read_timeout 300; # For upstream apps
proxy_connect_timeout 30;
# Check PHP execution time limit
php8.4 -r "echo ini_get('max_execution_time');"
# Watch what PHP is doing under load
sudo tail -f /var/log/php8.4-fpm-slow.log
403 Forbidden
| Cause | Symptom | Fix |
|---|---|---|
| Wrong file permissions | 403 on all pages | chmod -R 755 /var/www/site/ |
| Wrong ownership | 403 on all pages | chown -R www-data:www-data /var/www/site/ |
| No index file | 403 on / | Create index.html or index.php in docroot |
autoindex off + no index | 403 on directory | Add index index.html index.php to server block |
deny all rule | 403 on specific path | Check location blocks for deny all |
| Hidden file blocked correctly | 403 on .env | This is correct — not a bug |
# Fix permissions
sudo chown -R www-data:www-data /var/www/example.com/
sudo find /var/www/example.com -type d -exec chmod 755 {} \;
sudo find /var/www/example.com -type f -exec chmod 644 {} \;
# Check what user Nginx runs as
ps aux | grep "nginx: worker" | head -2 | awk '{print $1}'
# Test if www-data can read the file
sudo -u www-data cat /var/www/example.com/public/index.php | head -3
404 Not Found
| Cause | Fix |
|---|---|
Wrong root path | Check root in server block; verify directory exists |
| Site not enabled | ln -s sites-available/site sites-enabled/site and reload |
Wrong server_name | Verify server_name matches the request hostname |
| Missing rewrite rules | Add try_files $uri $uri/ /index.php?$args |
alias vs root confusion | alias = replaces location prefix; root = appends URI |
# Verify root directory exists and has files
grep "root" /etc/nginx/sites-enabled/example.com.conf
ls -la /var/www/example.com/public/
# Check which server block handles the request
sudo nginx -T | grep -B5 "server_name.*example.com"
# Test with curl
curl -v http://localhost/404test 2>&1 | grep "< HTTP"
SSL / TLS Errors
Cloudflare 525 (SSL Handshake Failed)
# Cause: Nginx has no SSL listener or bad cert
# Check SSL listener
grep "ssl" /etc/nginx/sites-enabled/example.com.conf
# Verify cert and key files exist
ls -la /etc/ssl/certs/example.com.crt
ls -la /etc/ssl/private/example.com.key
# Verify cert is valid
openssl x509 -in /etc/ssl/certs/example.com.crt -noout -dates
# Verify key matches cert
openssl x509 -noout -modulus -in /etc/ssl/certs/example.com.crt | openssl md5
openssl rsa -noout -modulus -in /etc/ssl/private/example.com.key | openssl md5
# Both hashes must match
Cloudflare 526 (Invalid SSL Certificate)
# Cause: Self-signed cert but Cloudflare is in Full (Strict) mode
# Fix: Use Cloudflare Origin Certificate
openssl x509 -in /etc/ssl/certs/example.com.crt -noout -issuer
# If issuer is self-signed → replace with Cloudflare Origin Cert
ERR_SSL_PROTOCOL_ERROR
# Cause: Nginx not listening on 443 or bad SSL config
sudo ss -tlnp | grep :443
openssl s_client -connect example.com:443 -servername example.com
Nginx Won't Start
# Check config syntax
sudo nginx -t
# What's the exact error?
sudo nginx -t 2>&1
# Port already in use?
sudo ss -tlnp | grep ':80\|:443'
sudo lsof -i :80
# Common: Apache occupying port 80
sudo systemctl stop apache2
sudo systemctl start nginx
Config Change Not Taking Effect
# Did you actually reload?
sudo nginx -s reload
# Was there an error during reload? Check log
sudo tail -10 /var/log/nginx/error.log
# Verify the config file is included
sudo nginx -T | grep example.com
# Is the site enabled?
ls -la /etc/nginx/sites-enabled/ | grep example.com
Log Location Reference
| Log | Path | What to Look For |
|---|---|---|
| Server errors | /var/log/nginx/error.log | All errors — check first |
| Access log | /var/log/nginx/access.log | Status codes, slow requests |
| Per-site error | /var/log/nginx/example.com.error.log | Site-specific errors |
| PHP-FPM | /var/log/php8.4-fpm.log | FPM pool errors |
| PHP slow log | /var/log/php8.4-fpm-slow.log | Slow PHP requests |
| PHP errors | Value of error_log in php.ini | PHP-level errors |