I’ve already used Caddy successfully to securely reverse proxy various services in my home network. Now, however, I seem to be hitting a wall with a Hikvision network camera.
Reverse proxying the camera’s web interface is not an issue. The problem I’ve run into is that the live video feed is failing if the Hikvision camera is behind Caddy.
From what I’ve seen in Firefox’ dev tools and gleaned from this blog post, the Hikvision web client tries to establish a websocket connection (ws://
) on port 7681
to the camera. If the camera is behind Caddy’s automatic HTTPS, the client is clever enough to switch to encrypted wss://
(on port 7682
).
What I’m trying to do:
Have Caddy proxy wss://caddy:7682
to ws://camera:7681
.
To that end, I’ve tried the following configuration in Caddyfile
:
# Hikvision web interface
hikvision.{$MY_DOMAIN} {
reverse_proxy hikvision-int.{$MY_DOMAIN}
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
}
# Hikvision web socket
https://hikvision.{$MY_DOMAIN}:7682 {
reverse_proxy http://hikvision-int.{$MY_DOMAIN}:7681
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
}
The first part works fine; the second (web socket) half is what I’m having trouble with.
Specifically, Caddy reports HTTP status 502 (error EOF) with the following log message (debug logging enabled):
{
"level": "debug",
"ts": 1739399179.532614,
"logger": "http.handlers.reverse_proxy",
"msg": "upstream roundtrip",
"upstream": "hikvision-int.MYDOMAIN:7681",
"duration": 0.003557717,
"request": {
"remote_ip": "10.0.9.96",
"remote_port": "55549",
"client_ip": "10.0.9.96",
"proto": "HTTP/1.1",
"method": "GET",
"host": "hikvision.MYDOMAIN:7682",
"uri": "/?version=0.1&cipherSuites=0&token=SOMETOKENVALUE",
"headers": {
"Sec-Fetch-Site": [
"same-site"
],
"Sec-Websocket-Key": [
"B+0dGhT+rEhewWCKPJzEcQ=="
],
"Sec-Websocket-Version": [
"13"
],
"Sec-Websocket-Extensions": [
"permessage-deflate"
],
"Accept": [
"*/*"
],
"Accept-Encoding": [
"gzip, deflate, br, zstd"
],
"Accept-Language": [
"de,en-US;q=0.7,en;q=0.3"
],
"Pragma": [
"no-cache"
],
"User-Agent": [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0"
],
"Origin": [
"https://hikvision.MYDOMAIN"
],
"Cookie": [
"REDACTED"
],
"Sec-Fetch-Mode": [
"websocket"
],
"Connection": [
"Upgrade"
],
"Sec-Fetch-Dest": [
"empty"
],
"Upgrade": [
"websocket"
],
"Cache-Control": [
"no-cache"
]
},
"tls": {
"resumed": false,
"version": 772,
"cipher_suite": 4865,
"proto": "http/1.1",
"server_name": "hikvision.MYDOMAIN"
}
},
"error": "EOF"
}
I’ve tried to access the camera’s ws://
URL with curl on the Docker host Caddy is running on. That seems to be working fine:
curl -v "hikvision-int.MYDOMAIN:7681/?version=0.1&cipherSuites=0&token=SOMETOKENVALUE"
* Trying 10.0.11.21:7681...
* Connected to hikvision-int.MYDOMAIN (10.0.11.21) port 7681 (#0)
> GET /?version=0.1&cipherSuites=0&token=SOMETOKENVALUE HTTP/1.1
> Host: hikvision-int.MYDOMAIN:7681
> User-Agent: curl/7.88.1
> Accept: */*