# Nginx Reverse Proxy Not Working
Nginx as a reverse proxy should forward requests to your backend and return responses. Instead, you get 502 Bad Gateway, 504 Gateway Timeout, or responses with missing data. The proxied application shows incorrect client IPs or can't connect at all.
Understanding Reverse Proxy Issues
Reverse proxy problems fall into categories: - Connectivity: Nginx can't reach the upstream - Headers: Wrong or missing forwarded headers - Timeouts: Requests take too long - SSL: Certificate issues between Nginx and backend
Check the error log first:
``bash
tail -f /var/log/nginx/error.log
Common Cause 1: Upstream Not Reachable
The backend service isn't running or isn't accessible.
Diagnosis: ```bash # Check if backend is running curl http://localhost:3000/health
# Check if backend port is listening netstat -tlnp | grep 3000
# Check Nginx can reach backend curl --interface 127.0.0.1 http://localhost:3000/ ```
Error log shows:
``
connect() failed (111: Connection refused) while connecting to upstream
Solutions:
- 1.Backend not running:
- 2.```bash
- 3.sudo systemctl start myapp
- 4.sudo systemctl enable myapp
- 5.
` - 6.Wrong port or host:
- 7.```nginx
- 8.# Verify the upstream address
- 9.location / {
- 10.proxy_pass http://127.0.0.1:3000; # Correct address?
- 11.}
- 12.
` - 13.Backend not started yet:
- 14.```nginx
- 15.# Use upstream block with fail_timeout
- 16.upstream backend {
- 17.server 127.0.0.1:3000 fail_timeout=10s;
- 18.keepalive 32;
- 19.}
server { location / { proxy_pass http://backend; } } ```
Common Cause 2: Upstream DNS Resolution
When using domain names for upstream, DNS might not resolve.
Problematic config:
``nginx
location / {
proxy_pass http://api.example.com;
}
If DNS changes, Nginx keeps the old IP until reload.
Solution: Use resolver directive:
``nginx
location / {
resolver 8.8.8.8 valid=30s;
set $upstream http://api.example.com;
proxy_pass $upstream;
}
This re-resolves DNS periodically.
Or use upstream block: ```nginx upstream api { server api1.example.com:443 resolve; server api2.example.com:443 resolve; }
server { location / { proxy_pass https://api; } } ```
Common Cause 3: Missing Host Header
Backend expects a specific Host header but receives localhost or the upstream address.
Problem:
``nginx
location / {
proxy_pass http://backend:3000;
# Backend receives Host: backend:3000
}
Solution: Forward the original host:
``nginx
location / {
proxy_pass http://backend:3000;
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;
}
Or set specific host:
``nginx
location / {
proxy_pass http://backend:3000;
proxy_set_header Host "api.example.com";
}
Common Cause 4: Wrong Upstream Protocol
Mixing HTTP and HTTPS between Nginx and backend.
Backend expects HTTPS: ```nginx # Wrong proxy_pass http://api.example.com;
# Correct proxy_pass https://api.example.com; ```
SSL verification issues:
``nginx
proxy_pass https://self-signed.example.com;
proxy_ssl_verify off; # For self-signed certificates
Or trust specific CA:
``nginx
proxy_pass https://api.example.com;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-bundle.crt;
proxy_ssl_verify on;
proxy_ssl_verify_depth 2;
Common Cause 5: Timeout Issues
Backend takes too long to respond.
Error log:
``
upstream timed out (110: Connection timed out) while reading response header from upstream
Solution: Increase timeouts:
``nginx
location / {
proxy_pass http://backend:3000;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
For long-polling or SSE:
``nginx
location /events {
proxy_pass http://backend:3000;
proxy_read_timeout 86400s; # 24 hours
proxy_buffering off;
}
Common Cause 6: Buffer Overflow
Large responses exceed buffer sizes.
Error log:
``
upstream sent too big header while reading response header from upstream
Solution: Increase buffer sizes:
``nginx
location / {
proxy_pass http://backend:3000;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
}
For large response bodies:
``nginx
proxy_max_temp_file_size 0;
proxy_buffering off;
Common Cause 7: WebSocket Not Working
WebSocket upgrades not being passed through.
Solution: ```nginx map $http_upgrade $connection_upgrade { default upgrade; '' close; }
server { location /ws { proxy_pass http://backend:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_read_timeout 86400s; } } ```
Common Cause 8: Client IP Not Forwarded
Backend sees all requests coming from Nginx's IP.
Problem:
``nginx
location / {
proxy_pass http://backend:3000;
}
Backend receives:
``
Remote-Addr: 127.0.0.1 (Nginx's IP)
Solution: Forward real IP:
``nginx
location / {
proxy_pass http://backend:3000;
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;
}
Backend must trust these headers:
Express.js example:
``javascript
app.set('trust proxy', true);
app.set('trust proxy', 'loopback');
Django example:
``python
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
Common Cause 9: Compression Issues
Double compression or incompatible encoding.
Problem: ```nginx gzip on; gzip_types text/plain application/json;
location /api { proxy_pass http://backend:3000; # Backend also compresses -> double compression } ```
Solution: ```nginx # Option 1: Let backend handle compression gzip off;
# Option 2: Tell backend not to compress location /api { proxy_pass http://backend:3000; proxy_set_header Accept-Encoding ""; # Strip encoding gzip on; gzip_types application/json; } ```
Common Cause 10: Connection Reuse Issues
Keepalive connections to upstream not configured.
Solution: ```nginx upstream backend { server 127.0.0.1:3000; keepalive 32; # Keep 32 connections open }
server { location / { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ""; } } ```
Complete Working Configuration
```nginx upstream backend { server 127.0.0.1:3000 max_fails=3 fail_timeout=30s; keepalive 32; }
server { listen 80; server_name example.com;
location / { proxy_pass http://backend; proxy_http_version 1.1;
# Headers 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; proxy_set_header Connection "";
# Timeouts proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s;
# Buffers proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; } } ```
Verification Steps
- 1.Test backend directly:
- 2.```bash
- 3.curl http://localhost:3000/health
- 4.
` - 5.Test through Nginx:
- 6.```bash
- 7.curl -v http://example.com/
- 8.
` - 9.Check response headers:
- 10.```bash
- 11.curl -I http://example.com/
- 12.
` - 13.Monitor access and error logs:
- 14.```bash
- 15.tail -f /var/log/nginx/access.log /var/log/nginx/error.log
- 16.
` - 17.Test with specific headers:
- 18.```bash
- 19.curl -H "Host: example.com" http://localhost/
- 20.
`
Quick Reference
| Error | Cause | Fix |
|---|---|---|
| Connection refused | Backend not running | Start backend service |
| upstream timed out | Slow backend | Increase proxy_read_timeout |
| too big header | Response headers too large | Increase proxy_buffer_size |
| 400 Bad Request | Missing Host header | Set proxy_set_header Host |
| Wrong client IP | Headers not forwarded | Set X-Forwarded-For headers |
| 502 Bad Gateway | Backend crashed/restarted | Check backend logs |
| SSL errors | Protocol mismatch | Use https:// in proxy_pass |
Reverse proxy issues usually come down to connectivity, headers, or timeouts. Verify the backend works directly, then ensure Nginx passes all required information.