Reverse proxy / TLS¶
You only need this if you want to reach pi-remote over HTTPS — e.g. to embed it in an HTTPS Home Assistant, expose it through one hostname, or sit it behind a homelab proxy alongside other services (Proxmox/MS-01, etc.).
pi-remote and go2rtc run on the Pi; the proxy (on another host) terminates TLS and forwards to the Pi over the LAN.
What to proxy¶
| Backend | On the Pi | Why |
|---|---|---|
| pi-remote | http://<pi-ip>:8800 |
the remote UI + API + HLS preview |
| go2rtc | http://<pi-ip>:1984 |
the WebRTC player + signaling |
Then set on the Pi (so the embedded WebRTC player URL is HTTPS):
PI_REMOTE_GO2RTC_PUBLIC=https://cam.example.com
Two things that always apply
- WebRTC media (port 8555) does NOT go through the proxy — it flows directly between the browser and the Pi. Keep 8555 reachable on the LAN. HLS, by contrast, is fully proxied (same origin as pi-remote).
- The cert must be browser-trusted. Self-signed /
tls internalcerts silently fail inside an iframe — use Let's Encrypt (a real domain; DNS-01 works for internal-only hostnames).
Caddy (simplest)¶
Automatic HTTPS, ~2 lines per route. Caddyfile:
remote.example.com {
reverse_proxy 127.0.0.1:8800
}
cam.example.com {
reverse_proxy 127.0.0.1:1984
}
(Replace 127.0.0.1 with the Pi's IP if Caddy runs on a different host.) Embed
https://remote.example.com/?token=YOURKEY in HA; set
PI_REMOTE_GO2RTC_PUBLIC=https://cam.example.com.
Traefik (good when fronting many services)¶
Best when you're already routing several apps. Because pi-remote/go2rtc are not containers, define them with the file provider (Docker labels are for your containerized apps).
Static config — traefik.yml¶
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
providers:
docker: {} # your containerized services (via labels)
file:
directory: /etc/traefik/dynamic
watch: true
certificatesResolvers:
le:
acme:
email: you@example.com
storage: /etc/traefik/acme.json
dnsChallenge: # DNS-01 also issues certs for internal-only hosts
provider: cloudflare
Dynamic config — /etc/traefik/dynamic/pi-remote.yml¶
http:
routers:
pi-remote:
rule: "Host(`remote.example.com`)"
entryPoints: [websecure]
service: pi-remote
tls:
certResolver: le
go2rtc:
rule: "Host(`cam.example.com`)"
entryPoints: [websecure]
service: go2rtc
tls:
certResolver: le
services:
pi-remote:
loadBalancer:
servers:
- url: "http://<pi-ip>:8800" # the Pi
go2rtc:
loadBalancer:
servers:
- url: "http://<pi-ip>:1984" # the Pi
Running Traefik (Docker Compose)¶
services:
traefik:
image: traefik:v3.1
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
- CF_DNS_API_TOKEN=__your_cloudflare_token__ # for the DNS-01 challenge
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./dynamic:/etc/traefik/dynamic:ro
- ./acme.json:/etc/traefik/acme.json
touch acme.json && chmod 600 acme.json before first start. Swap the DNS provider
/ token for whatever runs your domain.
Containerized services on the same host¶
Those use labels instead of the file provider, e.g.:
labels:
- "traefik.enable=true"
- "traefik.http.routers.foo.rule=Host(`foo.example.com`)"
- "traefik.http.routers.foo.entrypoints=websecure"
- "traefik.http.routers.foo.tls.certresolver=le"
WebRTC behind a proxy — recap¶
- Proxy go2rtc's HTTP (1984) for the player + signaling (done above).
- WebRTC media uses 8555 directly between browser and Pi — keep it open on the LAN. Same-LAN host candidates work out of the box; cross-subnet/remote access would need go2rtc WebRTC candidate/TURN configuration (out of scope).
- If WebRTC still won't embed, fall back to the HLS button — it's fully proxied and needs no extra ports.