Caddy Websockets Timing Out

1. The problem I’m having:

I am using caddy to proxy to an emby server (and provide ssl certs). I have a working config except for websockets. Emby uses websockets to show items such as play status / current runtime in the admin dashboard. These work for around a minute before it appears to close the connection.

i have to remove the following to get the websocket connection to work

        header_up Connection "upgrade"
        header_up Upgrade {>Upgrade}

2. Error messages and/or full log output:

No 

3. Caddy version:

4. How I installed and ran Caddy:

Installed by downloading the caddy linux amd64 package from the download page and installing with dpkg -i

a. System environment:

uname -a
Linux LDN-GW-LAN 6.1.0-31-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.128-1 (2025-02-07) x86_64 GNU/Linux

b. Command:


c. Service/unit/compose file:

[Unit]
Description=Caddy - A Fast, Portable, and Automated Web Server
Documentation=https://caddyserver.com/docs/
After=network.target

[Service]
User=caddy
Group=caddy
ExecStart=/bin/bash -c 'set -a; source /etc/caddy/cloudflare.env; exec /usr/bin/caddy run --config /etc/caddy/Caddyfile'
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile
Restart=on-failure
TimeoutStopSec=5s

# Load Environment Variables for Cloudflare DNS API Token
Environment="CADDY_DATA_DIR=/var/lib/caddy"
Environment="CADDY_CONFIG_DIR=/etc/caddy"
EnvironmentFile=/etc/caddy/cloudflare.env

# Ensure the service has the correct working directory
WorkingDirectory=/var/lib/caddy

# File and process limits
LimitNOFILE=1048576
LimitNPROC=512

# Hardening security (restrict access)
AmbientCapabilities=CAP_NET_BIND_SERVICE
ProtectHome=true
ProtectSystem=full
PrivateTmp=true
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target

d. My complete Caddy config:

### Global Configuration ###
{
    servers {
        protocols h1 h2 h3
        strict_sni_host
        keepalive_interval 20s
    }
    # Enable wildcard preference for certificates
    auto_https prefer_wildcard

}

### Load Cloudflare API Tokens and Emails from Environment File ###
(cloudflare_tls_domain_co_uk) {
    tls {
        protocols tls1.2 tls1.3
        curves x25519 secp384r1 secp256r1
        ciphers TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 \
                TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 \
                TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 \
                TLS_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        dns cloudflare {env.CLOUDFLARE_API_TOKEN_domain_co_uk}
        key_type p256
    }
}

(cloudflare_tls_domain_xyz) {
    tls {
        protocols tls1.2 tls1.3
        curves x25519 secp384r1 secp256r1
        ciphers TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 \
                TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 \
                TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 \
                TLS_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        dns cloudflare {env.CLOUDFLARE_API_TOKEN_domain_xyz}
        key_type p256
    }
}

### Security Headers ###
(x_frame_options) {
    header X-Frame-Options "SAMEORIGIN"
}

(strict_transport_security) {
    header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
}

(content_security_policy) {
    header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; frame-ancestors 'self';"
}

(permissions_policy) {
    header Permissions-Policy "camera=(), microphone=(), geolocation=()"
}

(referrer_policy) {
    header Referrer-Policy "strict-origin-when-cross-origin"
}

(x_content_type_options) {
    header X-Content-Type-Options "nosniff"
}

(minimal_security) {
    import referrer_policy
}

(basic_security) {
    import x_content_type_options
    import referrer_policy
    import permissions_policy
}

(strict_security) {
    import strict_transport_security
    import x_frame_options
    import x_content_type_options
    import referrer_policy
    import permissions_policy
}

### App Specific CSP's ###

(emby_csp) {
header Content-Security-Policy "
        default-src 'self' https://mb3admin.com https://emby.domain.xyz;
        script-src 'self' 'unsafe-inline';
        style-src 'self' 'unsafe-inline';
        frame-ancestors 'self';
        connect-src 'self' wss: https://mb3admin.com;
        media-src 'self' https://emby.domain.xyz;
    "
}


### Wildcard Certificate for *.domain.co.uk ###
*.domain.co.uk {
    import cloudflare_tls_domain_co_uk
}

### Wildcard Certificate for *.domain.xyz ###
*.domain.xyz {
    import cloudflare_tls_domain_xyz
}

### Reverse Proxy for Emby  ###
emby.domain.xyz {
    import strict_security
    import emby_csp
    encode gzip zstd

    # Main Reverse Proxy for Emby
    reverse_proxy http://172.18.50.20:8096 {
        transport http {
            keepalive 60s
            dial_timeout 15s
            response_header_timeout 30s
            expect_continue_timeout 5s
        }
        flush_interval 1s

        # Ensure Proper Forwarding of Client IP & Headers
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up Host {host}

        # Disable Proxy Buffering (Fixes Streaming Issues)
        header_down Cache-Control "no-cache, no-store, must-revalidate"
    }

    # General access log (INFO level for all requests)
    log {
        output file /var/log/caddy/emby_access.log {
            roll true
            roll_size_mb 10
            roll_gzip true
            roll_local_time
            roll_keep 3
            roll_keep_days 7
        }
        level INFO
    }

    # Error log (Only logs errors)
    log {
        output file /var/log/caddy/emby_error.log {
            roll true
            roll_size_mb 5
            roll_gzip true
            roll_local_time
            roll_keep 5
            roll_keep_days 14
        }
        level ERROR
    }

    @http {
        protocol http
    }
    redir @http https://{host}{uri} permanent
}

5. Links to relevant resources:

This is the default, no need to set it.

Remove these. Caddy does the right thing by default.

These don’t exist and never worked for you. It’s a bug that we didn’t reject the config file as invalid. Check the accepted directives here.

No need to do this because Caddy does it by default. This is never executed at all.

Any reason for customizing these? We tend to have good defaults that rarely need manual setting, unless there’s an exceptional scenario where customization is needed.

If I were to suspect anything in your config to cause the mentioned issue, it’d be this keepalive.

2 Likes

thanks for the detailed reply! I have tweaked the config based on your feedback but unfortunately still have the same problem.

I have even taken out the rest of the custom config but the issue persists. I don’t believe its emby that is the problem as browsing directly works as expected.

### Reverse Proxy for Emby ###
emby.domain.xyz {
    import strict_security
    import emby_csp
    encode gzip zstd

    # Main Reverse Proxy for Emby
    reverse_proxy http://172.18.50.20:8096 {
        transport http {
          #  dial_timeout 15s
          # response_header_timeout 60s
          #  expect_continue_timeout 5s
        }
        # flush_interval -1

        # Disable Proxy Buffering (Fixes Streaming Issues)
      #  header_down Cache-Control "no-cache, no-store, must-revalidate"
    }

    # General access log (INFO level for all requests)
    log {
        output file /var/log/caddy/emby_access.log {
            roll_size 10MiB
            roll_local_time
            roll_keep 3
            roll_keep_for 168h
        }
        level INFO
    }

    # Error log (Only logs errors)
    log {
        output file /var/log/caddy/emby_error.log {
            roll_size 5MiB
            roll_local_time
            roll_keep 5
            roll_keep_for 326h
        }
        level ERROR
    }

}

starting to think this might be an OS level issue (its a heavily customised Debian based image) unless you have any other thoughts as to why?

scratch that, i have just set up an ubuntu minimal server 24 and get the same problem with this config.

Since you specified that it’s around a minute before you run into problems, I’m still suspicious of the keepalive setting, but I’m not even close to knowing how all of that works.

I would recommend that you add debug to your Caddyfile global options and post Caddy’s log before and after you have issues.

Happy to share debug logs but even a short few mins of a fresh log is over 3mb. Is there a way I can share this?

I’ve noticed in the firefox console log I get quite a lot of

HLS Error: Type: networkError Details: fragLoadTimeOut Fatal: false Reason: 

followed by

POST
https://emby.domain.xyz/emby/Sessions/Playing/Progress?X-Emby-Client=Emby+Web&X-Emby-Device-Name=Firefox+Windows&X-Emby-Device-Id=f31af3e9-90fd-4feb-833f-c9e53217ab34&X-Emby-Client-Version=4.9.0.38&X-Emby-Token=27c9cffd23ca47a88966098aa3f5b95d&X-Emby-Language=en-gb&reqformat=json
[HTTP/1.1 502 Bad Gateway 76522ms]
Response { type: "basic", url: "https://emby.domain.xyz/emby/Sessions/Playing/Progress?X-Emby-Client=Emby+Web&X-Emby-Device-Name=Firefox+Windows&X-Emby-Device-Id=f31af3e9-90fd-4feb-833f-c9e53217ab34&X-Emby-Client-Version=4.9.0.38&X-Emby-Token=27c9cffd23ca47a88966098aa3f5b95d&X-Emby-Language=en-gb&reqformat=json", redirected: false, status: 502, ok: false, statusText: "Bad Gateway", headers: Headers(3), body: ReadableStream, bodyUsed: false }

I have also tried binding it to one network interface (eg the one that the external connections come in on) but that has not helped

I’ve setup nginx reverse proxy on the same host with a config that does the exact same and its not giving me any errors.

Any pointers on how to debug further?

Add debug to your global configuration. Caddy will be very verbose with that.

I have done and have a rather large log file - trouble is i am not sure what i am looking for

example of some of the errors

{
    "level": "error",
    "ts": 1740481228.9533484,
    "logger": "http.log.access.log2",
    "msg": "handled request",
    "request": {
        "remote_ip": "10.100.0.108",
        "remote_port": "59538",
        "client_ip": "10.100.0.108",
        "proto": "HTTP/2.0",
        "method": "POST",
        "host": "emby.domain.xyz",
        "uri": "/emby/Sessions/Playing/Progress?X-Emby-Client=Emby+Web&X-Emby-Device-Name=Firefox+Windows&X-Emby-Device-Id=f31af3e9-90fd-4feb-833f-c9e53217ab34&X-Emby-Client-Version=4.9.0.38&X-Emby-Token=27c9cffd23ca47a88966098aa3f5b95d&X-Emby-Language=en-gb&reqformat=json",
        "headers": {
            "Content-Type": ["text/plain"],
            "Origin": ["https://emby.domain.xyz"],
            "Sec-Fetch-Dest": ["empty"],
            "Priority": ["u=4"],
            "Accept": ["*/*"],
            "Accept-Encoding": ["gzip, deflate, br, zstd"],
            "Referer": ["https://emby.domain.xyz/web/index.html"],
            "Sec-Fetch-Mode": ["cors"],
            "Accept-Language": ["en-GB,en;q=0.5"],
            "Content-Length": ["584"],
            "Sec-Fetch-Site": ["same-origin"],
            "Te": ["trailers"],
            "User-Agent": ["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0"]
        },
        "tls": {
            "resumed": false,
            "version": 772,
            "cipher_suite": 4867,
            "proto": "h2",
            "server_name": "emby.domain.xyz"
        }
    },
    "bytes_read": 584,
    "user_id": "",
    "duration": 45.563165862,
    "size": 0,
    "status": 502,
    "resp_headers": {
        "Referrer-Policy": ["strict-origin-when-cross-origin"],
        "Permissions-Policy": ["camera=(), microphone=(), geolocation=()"],
        "Server": ["Caddy"],
        "Alt-Svc": ["h3=\":443\"; ma=2592000"],
        "Strict-Transport-Security": ["max-age=63072000; includeSubDomains; preload"],
        "X-Frame-Options": ["SAMEORIGIN"],
        "X-Content-Type-Options": ["nosniff"]
    }
}{
    "level": "error",
    "ts": 1740481228.953416,
    "logger": "http.log.access.log2",
    "msg": "handled request",
    "request": {
        "remote_ip": "10.100.0.108",
        "remote_port": "59538",
        "client_ip": "10.100.0.108",
        "proto": "HTTP/2.0",
        "method": "POST",
        "host": "emby.domain.xyz",
        "uri": "/emby/Sessions/Playing/Progress?X-Emby-Client=Emby+Web&X-Emby-Device-Name=Firefox+Windows&X-Emby-Device-Id=f31af3e9-90fd-4feb-833f-c9e53217ab34&X-Emby-Client-Version=4.9.0.38&X-Emby-Token=27c9cffd23ca47a88966098aa3f5b95d&X-Emby-Language=en-gb&reqformat=json",
        "headers": {
            "User-Agent": ["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0"],
            "Accept-Encoding": ["gzip, deflate, br, zstd"],
            "Content-Length": ["585"],
            "Priority": ["u=4"],
            "Content-Type": ["text/plain"],
            "Accept-Language": ["en-GB,en;q=0.5"],
            "Referer": ["https://emby.domain.xyz/web/index.html"],
            "Te": ["trailers"],
            "Accept": ["*/*"],
            "Origin": ["https://emby.domain.xyz"],
            "Sec-Fetch-Dest": ["empty"],
            "Sec-Fetch-Mode": ["cors"],
            "Sec-Fetch-Site": ["same-origin"]
        },
        "tls": {
            "resumed": false,
            "version": 772,
            "cipher_suite": 4867,
            "proto": "h2",
            "server_name": "emby.domain.xyz"
        }
    },
    "bytes_read": 585,
    "user_id": "",
    "duration": 25.354842886,
    "size": 0,
    "status": 502,
    "resp_headers": {
        "Strict-Transport-Security": ["max-age=63072000; includeSubDomains; preload"],
        "X-Frame-Options": ["SAMEORIGIN"],
        "X-Content-Type-Options": ["nosniff"],
        "Referrer-Policy": ["strict-origin-when-cross-origin"],
        "Permissions-Policy": ["camera=(), microphone=(), geolocation=()"],
        "Server": ["Caddy"],
        "Alt-Svc": ["h3=\":443\"; ma=2592000"]
    }
}{
    "level": "error",
    "ts": 1740481228.9534748,
    "logger": "http.log.access.log2",
    "msg": "handled request",
    "request": {
        "remote_ip": "10.100.0.108",
        "remote_port": "59538",
        "client_ip": "10.100.0.108",
        "proto": "HTTP/2.0",
        "method": "POST",
        "host": "emby.domain.xyz",
        "uri": "/emby/Sessions/Playing/Progress?X-Emby-Client=Emby+Web&X-Emby-Device-Name=Firefox+Windows&X-Emby-Device-Id=f31af3e9-90fd-4feb-833f-c9e53217ab34&X-Emby-Client-Version=4.9.0.38&X-Emby-Token=27c9cffd23ca47a88966098aa3f5b95d&X-Emby-Language=en-gb&reqformat=json",
        "headers": {
            "Sec-Fetch-Site": ["same-origin"],
            "Accept": ["*/*"],
            "Accept-Language": ["en-GB,en;q=0.5"],
            "Accept-Encoding": ["gzip, deflate, br, zstd"],
            "Sec-Fetch-Dest": ["empty"],
            "Content-Type": ["text/plain"],
            "Referer": ["https://emby.domain.xyz/web/index.html"],
            "Content-Length": ["584"],
            "Origin": ["https://emby.domain.xyz"],
            "Sec-Fetch-Mode": ["cors"],
            "Te": ["trailers"],
            "Priority": ["u=4"],
            "User-Agent": ["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0"]
        },
        "tls": {
            "resumed": false,
            "version": 772,
            "cipher_suite": 4867,
            "proto": "h2",
            "server_name": "emby.domain.xyz"
        }
    },
    "bytes_read": 584,
    "user_id": "",
    "duration": 35.462349445,
    "size": 0,
    "status": 502,
    "resp_headers": {
        "Server": ["Caddy"],
        "Alt-Svc": ["h3=\":443\"; ma=2592000"],
        "Strict-Transport-Security": ["max-age=63072000; includeSubDomains; preload"],
        "X-Frame-Options": ["SAMEORIGIN"],
        "X-Content-Type-Options": ["nosniff"],
        "Referrer-Policy": ["strict-origin-when-cross-origin"],
        "Permissions-Policy": ["camera=(), microphone=(), geolocation=()"]
    }
}{
    "level": "error",
    "ts": 1740481228.9535627,
    "logger": "http.log.access.log2",
    "msg": "handled request",
    "request": {
        "remote_ip": "10.100.0.108",
        "remote_port": "59538",
        "client_ip": "10.100.0.108",
        "proto": "HTTP/2.0",
        "method": "POST",
        "host": "emby.domain.xyz",
        "uri": "/emby/Sessions/Playing/Progress?X-Emby-Client=Emby+Web&X-Emby-Device-Name=Firefox+Windows&X-Emby-Device-Id=f31af3e9-90fd-4feb-833f-c9e53217ab34&X-Emby-Client-Version=4.9.0.38&X-Emby-Token=27c9cffd23ca47a88966098aa3f5b95d&X-Emby-Language=en-gb&reqformat=json",
        "headers": {
            "Accept": ["*/*"],
            "Te": ["trailers"],
            "User-Agent": ["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0"],
            "Sec-Fetch-Dest": ["empty"],
            "Sec-Fetch-Site": ["same-origin"],
            "Accept-Encoding": ["gzip, deflate, br, zstd"],
            "Referer": ["https://emby.domain.xyz/web/index.html"],
            "Origin": ["https://emby.domain.xyz"],
            "Sec-Fetch-Mode": ["cors"],
            "Priority": ["u=4"],
            "Accept-Language": ["en-GB,en;q=0.5"],
            "Content-Type": ["text/plain"],
            "Content-Length": ["584"]
        },
        "tls": {
            "resumed": false,
            "version": 772,
            "cipher_suite": 4867,
            "proto": "h2",
            "server_name": "emby.domain.xyz"
        }
    },
    "bytes_read": 584,
    "user_id": "",
    "duration": 55.6437483,
    "size": 0,
    "status": 502,
    "resp_headers": {
        "Alt-Svc": ["h3=\":443\"; ma=2592000"],
        "Strict-Transport-Security": ["max-age=63072000; includeSubDomains; preload"],
        "X-Frame-Options": ["SAMEORIGIN"],
        "X-Content-Type-Options": ["nosniff"],
        "Referrer-Policy": ["strict-origin-when-cross-origin"],
        "Permissions-Policy": ["camera=(), microphone=(), geolocation=()"],
        "Server": ["Caddy"]
    }
}{
    "level": "error",
    "ts": 1740481228.9536536,
    "logger": "http.log.access.log2",
    "msg": "handled request",
    "request": {
        "remote_ip": "10.100.0.108",
        "remote_port": "59538",
        "client_ip": "10.100.0.108",
        "proto": "HTTP/2.0",
        "method": "POST",
        "host": "emby.domain.xyz",
        "uri": "/emby/Sessions/Playing/Progress?X-Emby-Client=Emby+Web&X-Emby-Device-Name=Firefox+Windows&X-Emby-Device-Id=f31af3e9-90fd-4feb-833f-c9e53217ab34&X-Emby-Client-Version=4.9.0.38&X-Emby-Token=27c9cffd23ca47a88966098aa3f5b95d&X-Emby-Language=en-gb&reqformat=json",
        "headers": {
            "Accept-Language": ["en-GB,en;q=0.5"],
            "Origin": ["https://emby.domain.xyz"],
            "Accept-Encoding": ["gzip, deflate, br, zstd"],
            "Priority": ["u=4"],
            "Accept": ["*/*"],
            "Sec-Fetch-Site": ["same-origin"],
            "User-Agent": ["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0"],
            "Referer": ["https://emby.domain.xyz/web/index.html"],
            "Content-Type": ["text/plain"],
            "Content-Length": ["584"],
            "Sec-Fetch-Dest": ["empty"],
            "Sec-Fetch-Mode": ["cors"],
            "Te": ["trailers"]
        },
        "tls": {
            "resumed": false,
            "version": 772,
            "cipher_suite": 4867,
            "proto": "h2",
            "server_name": "emby.domain.xyz"
        }
    },
    "bytes_read": 584,
    "user_id": "",
    "duration": 65.755039975,
    "size": 0,
    "status": 502,
    "resp_headers": {
        "Server": ["Caddy"],
        "Alt-Svc": ["h3=\":443\"; ma=2592000"],
        "Strict-Transport-Security": ["max-age=63072000; includeSubDomains; preload"],
        "X-Frame-Options": ["SAMEORIGIN"],
        "X-Content-Type-Options": ["nosniff"],
        "Referrer-Policy": ["strict-origin-when-cross-origin"],
        "Permissions-Policy": ["camera=(), microphone=(), geolocation=()"]
    }
}

I can see the status is 502 going to the upstream emby server but the emby server is working without issue. Not under load, i am accessing it direct via its IP at the same time, no errors in the chrome console that way

The import strict_security and import emby_csp lines indicates you have additional security configurations defined elsewhere. You should check that these configurations are not blocking or interfering with the requests to the Emby server. The continuous 502 Bad Gateway usually means the backend service is down or unreachable.

Are there any differing configurations in Caddy compared to Nginx?

Apologies I should have said, I have commented out / removed all the addition parts at this point.

Current config:

emby.domain.xyz {
#    import strict_security
#   import emby_csp
#    encode gzip zstd

    # Main Reverse Proxy for Emby
    reverse_proxy http://172.18.50.20:8096 {
        transport http {
          #  dial_timeout 15s
          # response_header_timeout 60s
          #  expect_continue_timeout 5s
        }
        # flush_interval -1

        # Disable Proxy Buffering (Fixes Streaming Issues)
      #  header_down Cache-Control "no-cache, no-store, must-revalidate"
    }

    # General access log (INFO level for all requests)
    log {
        output file /var/log/caddy/emby_access.log {
            roll_size 10MiB
            roll_local_time
            roll_keep 3
            roll_keep_for 168h
        }
        level INFO
    }

    # Error log (Only logs errors)
    log {
        output file /var/log/caddy/emby_error.log {
            roll_size 5MiB
            roll_local_time
            roll_keep 5
            roll_keep_for 326h
        }
        level ERROR
    }

}

the nginx config is

server {
    listen 443 ssl http2;
    server_name emby.domain.xyz;

    ssl_certificate /etc/ssl/domainxyz/fullchain.cer;
    ssl_certificate_key /etc/ssl/domain.xyz/domain.xyz.pem;

    # Security Headers for Emby

    # Reverse Proxy for Emby
    location / {
        proxy_pass http://172.18.50.20:8096;
        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;
        proxy_buffering off;
        proxy_request_buffering off;
        proxy_hide_header X-Powered-By;  


        # Allow Seeking
        proxy_set_header Range $http_range;
        proxy_set_header If-Range $http_if_range;
        proxy_set_header X-Real-IP $remote_addr;

        # Allow Websockets
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $http_connection;


        # Disable access to Swagger interface
        location ^~ /swagger {   ## Disables access to swagger interface
                return 404;

            }
        }
    # Logging
    access_log /var/log/nginx/emby_access.log;
    error_log /var/log/nginx/emby_error.log error;
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name emby.domain.xyz;
    return 301 https://$host$request_uri;
}

I have noticed in the emby server debug logs the following socket closes

2025-02-26 01:27:36.208 Error HttpServer: Error sending websocket message
	*** Error Report ***
	Version: 4.9.0.38
	Command line: /opt/emby-server/system/EmbyServer.dll -programdata /var/lib/emby -ffdetect /opt/emby-server/bin/ffdetect -ffmpeg /opt/emby-server/bin/ffmpeg -ffprobe /opt/emby-server/bin/ffprobe -restartexitcode 3 -updatepackage emby-server-deb_{version}_amd64.deb
	Operating system: Linux version 6.8.0-53-generic (buildd@lcy02-amd64-046) (x86_64-linux-gnu-gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0, GNU ld (GNU Binutils for Ubunt
	OS/Process: x64/x64
	Framework: .NET 8.0.11
	Runtime: opt/emby-server/system/System.Private.CoreLib.dll
	Processor count: 6
	Data path: /var/lib/emby
	Application path: /opt/emby-server/system
	System.Net.WebSockets.WebSocketException: System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake.
	 ---> System.Net.Sockets.SocketException (110): Connection timed out
	   at System.IO.Pipelines.Pipe.GetFlushResult(FlushResult& result)
	   at System.IO.Pipelines.Pipe.FlushAsync(CancellationToken cancellationToken)
	   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1OutputProducer.FlushAsync(CancellationToken cancellationToken)
	   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.FlushPipeAsync(CancellationToken cancellationToken)
	   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpUpgradeStream.FlushAsync(CancellationToken cancellationToken)
	   at System.Net.WebSockets.ManagedWebSocket.SendFrameFallbackAsync(MessageOpcode opcode, Boolean endOfMessage, Boolean disableCompression, ReadOnlyMemory`1 payloadBuffer, Task lockTask, CancellationToken cancellationToken)
	   at System.Net.WebSockets.ManagedWebSocket.SendFrameFallbackAsync(MessageOpcode opcode, Boolean endOfMessage, Boolean disableCompression, ReadOnlyMemory`1 payloadBuffer, Task lockTask, CancellationToken cancellationToken)
	   at Emby.Server.Implementations.HttpServer.KestrelHost.KestrelWebSocket.SendAsync(ReadOnlyMemory`1 text, Boolean endOfMessage, CancellationToken cancellationToken)
	Source: System.Net.WebSockets
	TargetSite: Void MoveNext()
	InnerException: System.Net.Sockets.SocketException: Connection timed out
	Source: System.Private.CoreLib
	TargetSite: Void Throw()
	   at System.IO.Pipelines.Pipe.GetFlushResult(FlushResult& result)
	   at System.IO.Pipelines.Pipe.FlushAsync(CancellationToken cancellationToken)
	   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1OutputProducer.FlushAsync(CancellationToken cancellationToken)
	   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.FlushPipeAsync(CancellationToken cancellationToken)
	   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpUpgradeStream.FlushAsync(CancellationToken cancellationToken)
	   at System.Net.WebSockets.ManagedWebSocket.SendFrameFallbackAsync(MessageOpcode opcode, Boolean endOfMessage, Boolean disableCompression, ReadOnlyMemory`1 payloadBuffer, Task lockTask, CancellationToken cancellationToken)
	
2025-02-26 01:27:36.210 Error App: Error sending web socket message Sessions
	*** Error Report ***
	Version: 4.9.0.38
	Command line: /opt/emby-server/system/EmbyServer.dll -programdata /var/lib/emby -ffdetect /opt/emby-server/bin/ffdetect -ffmpeg /opt/emby-server/bin/ffmpeg -ffprobe /opt/emby-server/bin/ffprobe -restartexitcode 3 -updatepackage emby-server-deb_{version}_amd64.deb
	Operating system: Linux version 6.8.0-53-generic (buildd@lcy02-amd64-046) (x86_64-linux-gnu-gcc-13 (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0, GNU ld (GNU Binutils for Ubunt
	OS/Process: x64/x64
	Framework: .NET 8.0.11
	Runtime: opt/emby-server/system/System.Private.CoreLib.dll
	Processor count: 6
	Data path: /var/lib/emby
	Application path: /opt/emby-server/system
	System.Net.WebSockets.WebSocketException: System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake.
	 ---> System.Net.Sockets.SocketException (110): Connection timed out
	   at System.IO.Pipelines.Pipe.GetFlushResult(FlushResult& result)
	   at System.IO.Pipelines.Pipe.FlushAsync(CancellationToken cancellationToken)
	   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1OutputProducer.FlushAsync(CancellationToken cancellationToken)
	   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.FlushPipeAsync(CancellationToken cancellationToken)
	   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpUpgradeStream.FlushAsync(CancellationToken cancellationToken)
	   at System.Net.WebSockets.ManagedWebSocket.SendFrameFallbackAsync(MessageOpcode opcode, Boolean endOfMessage, Boolean disableCompression, ReadOnlyMemory`1 payloadBuffer, Task lockTask, CancellationToken cancellationToken)
	   at System.Net.WebSockets.ManagedWebSocket.SendFrameFallbackAsync(MessageOpcode opcode, Boolean endOfMessage, Boolean disableCompression, ReadOnlyMemory`1 payloadBuffer, Task lockTask, CancellationToken cancellationToken)
	   at Emby.Server.Implementations.HttpServer.KestrelHost.KestrelWebSocket.SendAsync(ReadOnlyMemory`1 text, Boolean endOfMessage, CancellationToken cancellationToken)
	   at MediaBrowser.Controller.Net.BasePeriodicWebSocketListener`2.SendData(Tuple`3 tuple, TReturnDataType data)
	Source: System.Net.WebSockets
	TargetSite: Void MoveNext()
	InnerException: System.Net.Sockets.SocketException: Connection timed out
	Source: System.Private.CoreLib
	TargetSite: Void Throw()
	   at System.IO.Pipelines.Pipe.GetFlushResult(FlushResult& result)
	   at System.IO.Pipelines.Pipe.FlushAsync(CancellationToken cancellationToken)
	   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Http1OutputProducer.FlushAsync(CancellationToken cancellationToken)
	   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.FlushPipeAsync(CancellationToken cancellationToken)
	   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpUpgradeStream.FlushAsync(CancellationToken cancellationToken)
	   at System.Net.WebSockets.ManagedWebSocket.SendFrameFallbackAsync(MessageOpcode opcode, Boolean endOfMessage, Boolean disableCompression, ReadOnlyMemory`1 payloadBuffer, Task lockTask, CancellationToken cancellationToken)
	
2025-02-26 01:27:36.210 Debug App: SessionInfosWebSocketListener stop transmitting over websocket to <external ip>
2025-02-26 01:28:12.574 Info Server: http/1.1 POST http://host1/emby/Sync/data. Source Ip: host2, UserAgent: okhttp/4.12.0
2025-02-26 01:28:12.576 Info Server: http/1.1 Response 200 to host2. Time: 3ms. POST http://host1/emby/Sync/data

I’m not entirely sure. @Mohammed90, would you know?

What happens if you run curl http://172.18.50.20:8096 --head in the terminal, do you get content back or an error?

1 Like
curl http://172.18.50.20:8096 --head
HTTP/1.1 302 Found
Date: Fri, 07 Mar 2025 17:17:29 GMT
Location: web/index.html

I’m still intrigued by this issue and don’t know what’s going on. Have you managed to resolve it in the past 12 days? I wonder if it’s an MTU issue :thinking: Can you run wireshark/tcpdump and observe the traffic there?

1 Like

I have managed to resolve it but only by switching from caddy to nginx. I tested it on both Debian and Ubuntu and got the same results. Not at all sure what the issue was, but can get some tcpdumps in the future if useful