$ curl -w "@curl-format.txt" -o /dev/null -s http://localhost/
time_namelookup: 0.004s
time_connect: 0.005s
time_appconnect: 0.000s
time_pretransfer: 0.005s
time_redirect: 0.000s
time_starttransfer: 0.045s ← Time to First Byte
----------
time_total: 0.052s
Testing Configuration Changes
Always validate configuration before applying:
# Test Nginx config syntax
nginx -t
# Test and show parsed config
nginx -T
# Test specific config file
nginx -t -c /etc/nginx/nginx.conf
# Reload (graceful - doesn't drop connections)
nginx -s reload
# Or using systemctl
systemctl reload nginx
# If reload fails, check what's wrong
journalctl -u nginx -n 50
Never Restart in Production
Use reload not restart. Reload gracefully applies changes; restart drops all active connections.
Network-Level Debugging
When HTTP-level debugging isn't enough:
Check What's Listening
# Show all listening ports
ss -tlnp
# Show only nginx ports
ss -tlnp | grep nginx
# Check specific port
ss -tlnp | grep :80
# Show established connections
ss -tn state established
DNS Issues
# Check DNS resolution
dig example.com
nslookup example.com
# Check what IP nginx resolves
# (Add resolver directive to nginx config)
resolver 8.8.8.8;
# Test from server
curl -v http://upstream-hostname/
tcpdump for Deep Debugging
# Capture HTTP traffic on port 80
tcpdump -i any -A port 80
# Capture and save to file
tcpdump -i any -w capture.pcap port 80
# Filter by host
tcpdump -i any host upstream.example.com
# Show only packet headers
tcpdump -i any -q port 80
Wireshark
For complex debugging, capture with tcpdump and analyze in Wireshark. Wireshark can decode HTTP, TLS, and show request/response pairs visually.
Common Error Patterns
Error
Likely Cause
First Check
connect() failed (111: Connection refused)
Upstream not running
systemctl status app
connect() failed (113: No route to host)
Network/firewall issue
ping upstream-ip
upstream timed out
Slow backend
Profile application
no live upstreams
All backends failed health checks
Check all upstream servers
Permission denied
File/socket permissions
ls -la /path
Too many open files
File descriptor exhaustion
ulimit -n
worker_connections not enough
Connection limit reached
Increase worker_connections
SSL: error
Certificate issue
openssl s_client -connect host:443
Debug Logging
Enable verbose logging temporarily to diagnose issues:
Debug logging generates massive amounts of data and impacts performance. Enable only temporarily, only for specific IPs or locations, and disable immediately after diagnosis.
Debugging Checklist
When things break, work through this systematically:
Check error log: tail -f /var/log/nginx/error.log
Check access log for the failed request
Test with curl directly to the server
Verify upstream is running: systemctl status app
Test upstream directly: curl http://127.0.0.1:3000/
Check file permissions: ls -la /var/www/
Verify configuration syntax: nginx -t
Check what's listening: ss -tlnp
Check disk space: df -h
Check memory: free -m
Check for recent config changes: git log -5 /etc/nginx/
Emergency Procedures
When production is down:
Quick Recovery
# If config is broken, revert to last known good
cp /etc/nginx/nginx.conf.backup /etc/nginx/nginx.conf
nginx -t && nginx -s reload
# If nginx won't start, check what's wrong
nginx -t 2>&1
journalctl -u nginx --no-pager -n 100
# Check if something else is using port 80
ss -tlnp | grep :80
# Kill stuck nginx processes (last resort)
pkill -9 nginx
nginx