# [[Websocket friendly proxy config for Nginx and Caddy]]
_Created: 2025-10-05_ | #nginx #reverseproxy #caddy| [[010 System Administration MOC|System Administration]]
Some backend apps need the proxy to be aware of websockets transport.
## Nginx
This configuration allows Nginx to act as a reverse proxy to an application running on port `3000`. It supports both **normal HTTP traffic** and **WebSocket upgrades**.
```nginx
server {
listen 80;
server_name app.example.com;
location / {
proxy_http_version 1.1;
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;
# Upgrade headers only matter if client requests upgrade
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass http://localhost:3000;
}
}
```
### **Explanation of directives**
- **`proxy_http_version 1.1;`**
Ensures Nginx speaks HTTP/1.1 with the backend. This is required for WebSockets and other upgraded connections, because HTTP/1.0 does not support them.
- **`proxy_set_header Host $host;`**
Preserves the original Host header (e.g., app.example.com) instead of replacing it with localhost. Useful when the backend app does host-based routing or generates absolute URLs.
- **`proxy_set_header X-Real-IP $remote_addr;`**
Passes the actual client IP address to the backend. Without this, the backend would only see Nginx’s IP.
- **`proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;`**
Adds the client’s IP to the X-Forwarded-For chain. This is important if multiple proxies are in the path and you want to track the original source.
- **`proxy_set_header X-Forwarded-Proto $scheme;`**
Indicates whether the original request came in as http or https. Helps apps generate correct redirects or absolute links.
- **`proxy_set_header Upgrade $http_upgrade;`**
Forwards the Upgrade header from the client (e.g., Upgrade: websocket). Required to initiate protocol upgrades.
- **`proxy_set_header Connection $connection_upgrade;`**
Works with the Upgrade header. It automatically sets `Connection: upgrade` if an upgrade is requested, or `Connection: close` otherwise. This avoids sending upgrade headers to backends that don’t expect them.
- **`proxy_pass http://localhost:3000;`**
Sends all traffic to the backend service running on port 3000.
## Caddy
Caddy handles most of the headers for proxy automatically. The `X-Real-IP` is apparently non-standard and mostly seen in nginx configs. Caddy follows the more popular `X-Forwarded-For` header convention, which has a list of IP addresses in case a request is proxied by more than one host.
```
app.example.com {
reverse_proxy localhost:3000 {
# Caddy already sets X-Forwarded-For
# Only keep this if your app requires it
header_up X-Real-IP {remote_host}
}
}
```
In fact, you don't need a config at all. Caddy can do it from commandline.
```bash
caddy reverse-proxy --from http://app.example.com --to http://localhost:3000
```