Running Next.js with next-ws for websockets on Caddy

1. The problem I’m having:

Hi! I’m running a Next.js application using Caddy for reverse proxy:

placemat.veryroundbird.house {
        reverse_proxy localhost:3501
}

This works almost entirely fine, except that I’m serving a websocket from an API route (/api/streamMessages/[username]) using next-ws, and something about the way Caddy handles that isn’t producing the right set of response headers, I think. (The reason I suspect this is that just running it off a port on my local machine works just fine.)

Specifically, these are the headers I get on the websocket connection on my local machine using next run dev:

Connection: Upgrade
Sec-Websocket-Accept: 5BizIpU0OhKb5KA3qY4KO041E4w=
Upgrade: websocket

Whereas what I get returned on the Caddy server is:

Alt-Svc: h3=“:443”; ma=2592000
Date: Sat, 31 May 2025 04:05:55 GMT
Server: Caddy

So that’s the mystery! Why is it doing this.

2. Error messages and/or full log output:

n/a (there are no server errors, it’s just not doing the right thing)

3. Caddy version:

v2.10.0 h1:fonubSaQKF1YANl8TXqGcn4IbIRUDdfAkpcsfI/vX5U=

4. How I installed and ran Caddy:

I’m pretty sure I just installed it per the Debian/Ubuntu/Raspbian instructions in the install guide.

a. System environment:

Ubuntu 24.04.1 LTS (GNU/Linux 6.8.0-60-generic x86_64)

b. Command:

caddy start

c. Service/unit/compose file:

n/a

d. My complete Caddy config:

blotter.veryroundbird.house {
        # Set this path to your site's directory.
        root * /var/www/html/blotter/public

        try_files {path} {path}.html {path}/ =404

        # Enable the static file server.
        file_server

        encode gzip

        handle_errors {
                rewrite * /{err.status_code}.html
                file_server
        }

        # Another common task is to set up a reverse proxy:
        # reverse_proxy localhost:8080

        # Or serve a PHP site through php-fpm:
        # php_fastcgi localhost:9000
}

spoverlay.veryroundbird.house {
        root * /var/www/html/streamplace-overlay
        file_server
        encode gzip
}

spoverlayapi.veryroundbird.house {
        reverse_proxy localhost:8080
}

smallbird.live {
        root * /var/www/html/smallbirdlive
        file_server

        encode gzip
}

at.veryroundbird.house {
        root * /var/www/html/at
        file_server

        encode gzip
}

placemat.veryroundbird.house {
        reverse_proxy localhost:3501
}

dev.placemat.veryroundbird.house {
        reverse_proxy localhost:3001
}

listmatcher.veryroundbird.house {
        root * /var/www/html/listmatcher
        file_server

        encode gzip
}

5. Links to relevant resources:

realized I can’t edit but possibly this will help for context? this is the application I’m working on