V2Ray over WebSocket and TLS with Caddy
This setup runs a V2Ray server that accepts VMess over WebSocket on a local port, and puts Caddy in front of it. Caddy obtains a TLS certificate for your domain automatically and forwards only WebSocket upgrade requests to V2Ray, so the traffic looks like ordinary HTTPS on port 443. Both services run with Docker Compose.
You need a domain (proxy.example.com below) whose DNS record points at the server, and ports 80 and 443 open so Caddy can complete the ACME challenge.
V2Ray
Configuration
Generate a client ID:
uuidgen
Put it in config.json:
{
"log": {
"loglevel": "warning"
},
"inbound": {
"port": 10086,
"listen": "0.0.0.0",
"protocol": "vmess",
"settings": {
"clients": [
{
"id": "<your-uuid>",
"alterId": 64
}
]
},
"streamSettings": {
"network": "ws"
}
},
"outbound": {
"protocol": "freedom",
"settings": {}
}
}
Set loglevel to debug while you troubleshoot. Clients must use the same alterId. Newer V2Ray clients use VMess AEAD with alterId 0. In that case, set it to 0 here as well.
Docker Compose
services:
v2ray:
image: v2ray/official
volumes:
- ./config.json:/etc/v2ray/config.json
ports:
- 127.0.0.1:10086:10086
restart: unless-stopped
The port is bound to 127.0.0.1, so the unencrypted WebSocket endpoint is reachable only through Caddy.
Caddy
Caddyfile
{
# debug
}
proxy.example.com {
# Serve a static site for normal visitors (optional)
# root * /var/www/website
# file_server
@v2ray_websocket {
path /
header Connection *Upgrade*
header Connection *upgrade*
header Upgrade websocket
}
reverse_proxy @v2ray_websocket 127.0.0.1:10086
}
The @v2ray_websocket matcher selects WebSocket upgrade requests on /. Caddy treats the two Connection values as alternatives, so either capitalization matches. Uncomment root and file_server to serve a decoy website from ./fake_root to everyone else.
Docker Compose
services:
caddy:
image: caddy:2-alpine
restart: unless-stopped
network_mode: host
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./fake_root:/var/www/website
- ./data:/data
- ./config:/config
network_mode: host lets Caddy reach V2Ray on 127.0.0.1:10086 and listen on ports 80 and 443 directly. Keep ./data, because it stores the certificates.
Client settings
| Field | Value |
|---|---|
| Protocol | VMess |
| Address | proxy.example.com |
| Port | 443 |
| UUID | <your-uuid> |
| alterId | same as the server |
| Network | ws |
| Path | / |
| TLS | enabled |
| SNI / Host | proxy.example.com |
Enjoy Reading This Article?
Here are some more articles you might like to read next: