# Nginx Proxy_Pass Error
The proxy_pass directive connects Nginx to upstream servers, but misconfigured URLs cause 502 errors, wrong paths, or failed connections. A trailing slash makes the difference between correct proxying and broken routing. Understanding how Nginx constructs the proxied URL is essential.
Understanding Proxy_Pass URL Construction
- 1.
proxy_passcombines two parts: - 2.The location match
- 3.The proxy_pass URL
URL construction rules:
- If proxy_pass has URI path: replace location match with proxy_pass path
- If proxy_pass has no URI path: append request URI to proxy_pass
Example 1 - proxy_pass with path:
``nginx
location /api/ {
proxy_pass http://backend/v1/;
}
Request /api/users becomes http://backend/v1/users`
Example 2 - proxy_pass without path:
``nginx
location /api/ {
proxy_pass http://backend;
}
Request /api/users becomes http://backend/api/users`
Common Cause 1: Trailing Slash Mismatch
Trailing slashes drastically change behavior.
Problematic config:
``nginx
location /api/ {
proxy_pass http://backend:3000;
}
Request /api/users:
- Location captures /api/
- Remaining URI: users
- proxy_pass has no path (no trailing slash)
- Full proxy URL: http://backend:3000/api/users
Backend receives /api/users. If backend expects /users, this fails.
Solution - add trailing slash:
``nginx
location /api/ {
proxy_pass http://backend:3000/;
}
Request /api/users:
- Location captures /api/
- proxy_pass path: /
- Remaining URI: users
- Proxy URL: http://backend:3000/users
Backend receives /users - correct.
| Config | Request | Backend Receives |
|---|---|---|
proxy_pass http://backend; (no slash) | /api/users | /api/users |
proxy_pass http://backend/; (slash) | /api/users | /users |
proxy_pass http://backend/v1/; | /api/users | /v1/users |
Common Cause 2: Location Without Trailing Slash
Location without trailing slash changes matching behavior.
Problematic config:
``nginx
location /api {
proxy_pass http://backend/;
}
Request /api/users:
- Location /api matches prefix
- Nginx doesn't strip /api from URI
- proxy_pass path / replaces nothing
- Proxy URL: http://backend/api/users
Solution: Add trailing slash to location:
``nginx
location /api/ {
proxy_pass http://backend/;
}
Common Cause 3: Regex Location with Proxy_Pass
Regex locations can't use path replacement in proxy_pass.
Problematic config:
``nginx
location ~ ^/api/(.*)$ {
proxy_pass http://backend/$1;
# Invalid - can't use capture groups in proxy_pass directly
}
Nginx error:
``
nginx: [emerg] "proxy_pass" cannot have URI part in location given by regular expression
Solution: Use rewrite or map:
``nginx
location ~ ^/api/(.*)$ {
set $path $1;
proxy_pass http://backend/$path;
}
Or use prefix location:
``nginx
location /api/ {
proxy_pass http://backend/;
}
Common Cause 4: Upstream DNS Resolution
proxy_pass with hostname requires DNS resolution.
Problematic config:
``nginx
location / {
proxy_pass http://api.example.com;
}
If DNS changes IP, Nginx keeps 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;
}
Or use upstream block: ```nginx upstream api { server api.example.com resolve; # Nginx Plus feature }
location / { proxy_pass http://api; } ```
Common Cause 5: Wrong Protocol
HTTP vs HTTPS mismatch with upstream.
Problematic config:
``nginx
location / {
proxy_pass http://backend:443;
# Backend expects HTTPS on 443
}
Upstream receives HTTP on HTTPS port - connection fails.
Solution: Use correct protocol:
``nginx
location / {
proxy_pass https://backend:443;
proxy_ssl_verify off; # For self-signed certificates
}
Common Cause 6: Missing Port
proxy_pass without port uses default (80 for HTTP, 443 for HTTPS).
Problematic config:
``nginx
location / {
proxy_pass http://backend;
# Uses port 80, backend listens on 3000
}
Solution: Specify port:
``nginx
location / {
proxy_pass http://backend:3000;
}
Common Cause 7: Upstream Block vs Direct URL
Mixing upstream block syntax with direct URL.
Problematic config: ```nginx upstream backend { server 10.0.0.1:3000; }
location /api/ { proxy_pass http://backend:3000/; # backend is upstream name, not host # Actually works, but confusing } ```
Solution: Use consistent approach: ```nginx upstream backend_servers { server 10.0.0.1:3000; server 10.0.0.2:3000; }
location /api/ { proxy_pass http://backend_servers/; } ```
Or direct:
``nginx
location /api/ {
proxy_pass http://10.0.0.1:3000/;
}
Common Cause 8: Path Encoding Issues
URL encoding handled differently by Nginx and backend.
Problematic config:
``nginx
location /api/ {
proxy_pass http://backend/;
}
Request /api/search?q=hello%20world:
- %20 is space
- Some backends expect decoded URL
Solution: Control encoding:
``nginx
location /api/ {
proxy_pass http://backend/;
proxy_set_header X-Original-URI $request_uri;
}
Backend can receive both encoded and decoded variants.
Common Cause 9: IPv6 Address Syntax
IPv6 addresses need brackets in proxy_pass.
Problematic config:
``nginx
location / {
proxy_pass http://::1:3000; # Invalid syntax
}
Solution: Use brackets:
``nginx
location / {
proxy_pass http://[::1]:3000;
}
Verification Steps
- 1.Test proxy_pass URL:
- 2.```bash
- 3.curl -v http://localhost/api/users 2>&1 | grep ">"
- 4.
` - 5.Check upstream receives:
- 6.```bash
- 7.# On backend server
- 8.tail -f /var/log/backend/access.log
- 9.
` - 10.Add diagnostic header:
- 11.```nginx
- 12.location /api/ {
- 13.proxy_pass http://backend/;
- 14.add_header X-Proxied-URL $upstream_uri always;
- 15.}
- 16.
` - 17.Test configuration:
- 18.```bash
- 19.sudo nginx -t
- 20.
` - 21.Check error log:
- 22.```bash
- 23.tail -f /var/log/nginx/error.log
- 24.
` - 25.Verify backend connection:
- 26.```bash
- 27.# Test backend directly
- 28.curl http://backend:3000/users
- 29.
`
Complete Working Configurations
Simple proxy:
``nginx
location /api/ {
proxy_pass http://backend:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
With upstream block: ```nginx upstream backend { server 10.0.0.1:3000; server 10.0.0.2:3000 backup; keepalive 32; }
location /api/ { proxy_pass http://backend/; proxy_http_version 1.1; proxy_set_header Connection ""; } ```
With path rewrite:
``nginx
location /old-api/ {
rewrite ^/old-api/(.*)$ /v1/$1 break;
proxy_pass http://backend:3000/;
}
Dynamic upstream: ```nginx location /api/ { resolver 8.8.8.8 valid=30s;
set $upstream_host api.example.com; proxy_pass http://$upstream_host/; } ```
Quick Reference
| Config | Request | Backend URL |
|---|---|---|
location /api/ { proxy_pass http://backend; } | /api/users | /api/users |
location /api/ { proxy_pass http://backend/; } | /api/users | /users |
location /api/ { proxy_pass http://backend/v1/; } | /api/users | /v1/users |
location /api { proxy_pass http://backend/; } | /api/users | /api/users |
location ~ ^/api/(.*)$ { proxy_pass http://backend/$1; } | Invalid! | Use set variable |
The critical rule: trailing slash on proxy_pass strips the location prefix; no trailing slash preserves it. Match your location and proxy_pass slashes to what the backend expects.