Caddy with Websockets (Milestone XProtect)

1. Caddy version (caddy version):


2. How I run Caddy:

Via Caddyfile

a. System environment:

Ubuntu 20.04 LTS

b. Command:

c. Service/unit/compose file:

d. My complete Caddyfile or JSON config:

#Inbound to XProtect HTTPS ( {
        tls /etc/ssl/certs/ /etc/ssl/certs/>
    header {
    Strict-Transport-Security "max-age=31536000; includeSubdomains"
    X-XSS-Protection "1; mode=block"
    Referrer-Policy "same-origin"
        reverse_proxy {
                transport http {


3. The problem I’m having:

So I am running Milestone’s XProtect through Caddy. Simply put I am having issues with the Websockets - Not that I fully understand why. On the XProtect Web interface, you can turn Websockets on/off. When going through Caddy, with Websockets ON I am not able to load previews of any camera. When I turn Websockets off, I am able to see the preview, however it’s more of a 2 second update of each camera (compared to live stream when Websockets are on).

I have stubbled across a post online, with a working NGINX config (URL below), however I don’t want to move across to NGINX, as I have learnt so much (and had success) with Caddy for more applications/uses than NGINX. The NGINX config is:

server {
        listen 443 ssl;
        ssl_certificate /etc/letsencrypt/live/<FQDN>/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/<FQDN>/privkey.pem;

        server_name <FQDN>;

        gzip off;

        location / {
                proxy_pass https://<MILESTONE IP>:8082;
                proxy_buffering off;
                proxy_http_version 1.1;

        # Video uses WebSockets and needs special headers to work.
        location /XProtectMobile/Video {
                proxy_pass https://<MILESTONE IP>:8082;
                proxy_buffering off;
                proxy_http_version 1.1;

                # These two headers come from the browser to NGINX, but they do
                # not get passed through NGINX, so we will set them manually.
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "Upgrade";

                # These two headers do not come through from the browser as the
                # Milestone server wants to see them, so we will set them manually.
                proxy_set_header Host <MILESTONE IP>:8082;
                proxy_set_header Origin https://<MILESTONE IP>:8082;


I would appreciate any help available on this!

4. Error messages and/or full log output:

I am not receiving any errors, as the config is correct, but just not functioning as well as I expect.

5. What I already tried:

I have narrowed this down to Caddy, as when I load the website locally (or externally directly to the XProtect Server) all works as should (with Websockets turned on). However, if I then redirect through Caddy the issue then appears.

I have recently added the line


into my Caddyfile through research, but this doesn’t appear to have worked. Until 8 months ago I hadn’t touched Linux, let alone a reverse proxy, but I am slowly getting there!

6. Links to relevant resources:,WebSocket%20is%20a%20web%20technology%20that%20provides%20full-duplex%20communication,from%20the%20Milestone%20Mobile%20Server.

Well that nginx config doesn’t use PHP, so adding php_fastcgi doesn’t seem right. Regardless, it will conflict with reverse_proxy because you can’t have them both handle a request. And you seem to have put php_fastcgi inside of your header directive, which doesn’t make sense.

It’s possible you need to set the Host header with header_up inside your reverse_proxy, as the nginx config suggests.

This topic was automatically closed after 30 days. New replies are no longer allowed.