Route both websocket and https

1. The problem I’m having:

I have a caddyfile ok with basic auth on https requests for port 12345.
now I have a service running websocket on port 23456, how to add it into the caddyfile?

2. Error messages and/or full log output:

3. Caddy version:

v2.6.4 h1:2hwYqiRwk1tf3VruhMpLcYTg+11fCdr8S3jhNAdnPy8=

4. How I installed and ran Caddy:

a. System environment:

Ubuntu22.04

b. Command:

sudo caddy fmt Caddyfile --overwrite
sudo caddy reload Caddyfile

c. Service/unit/compose file:

d. My complete Caddy config:

The original caddyfile that works:

this_is_the_doman_name.xyz {
        basicauth {
                admin secret
        }

        reverse_proxy 127.0.0.1:12345

        handle_errors {
                respond "{http.error.status_code} {http.error.status_text}"
        }

        # Log startup messages
        log {
                output file /var/log/caddy/vitalik.log
                format json
        }
}

Now I modify it to

this_is_the_doman_name.xyz {
    @port12345 {
        host this_is_the_doman_name.xyz
        header Host ":12345"
    }
    route @port12345 {
        basicauth {
            admin1 secret
        }
        reverse_proxy 127.0.0.1:12345
    }

    # Route for WebSocket requests on port 23456
    @port23456 {
        host this_is_the_doman_name.xyz
        header Host ":23456"
    }
    route @port23456 {
        basicauth {
            admin2 secret
        }
        reverse_proxy 127.0.0.1:23456
    }
        handle_errors {
                respond "{http.error.status_code} {http.error.status_text}"
        }

        # Log startup messages
        log {
                output file /var/log/caddy/vitalik.log
                format json
        }
}

Now both routes not responding.

5. Links to relevant resources:

Please upgrade to the latest version, v2.7.4.

Your Caddyfile is listening on port 80 and 443, not on port 12345. That doesn’t make sense, your config doesn’t match what you say.

You can use the same port, and use a matcher to match websocket requests.

example.com {
	@websockets `header({'Connection':'*Upgrade*','Upgrade':'websocket'})`
	handle @websockets {
		reverse_proxy 127.0.0.1:23456
	}

	handle {
		reverse_proxy 127.0.0.1:12345
	}
}

thank you ser! i also find it in the matcher document.

now i have the caddyfile:

ws.example.com {
	@websockets `header({'Connection':'*Upgrade*','Upgrade':'websocket'})`
	handle @websockets {
		reverse_proxy 127.0.0.1:23456
	}
}

tp.example.com {
	handle {
		reverse_proxy 127.0.0.1:12345
	}
}

after setting this, i am confused about how to establish ws connection.

simple test program:

import asyncio
import json
import websockets

async def hello():
    async with websockets.connect('ws://localhost:23456') as websocket:
        payload = somethng
        await websocket.send(payload)

        response = await websocket.recv()
        print(f"< {response}")

asyncio.get_event_loop().run_until_complete(hello())

if i am on my server environment, i can simply run this.

but if i am not in the server environment, which mean i should replace ws://localhost:23456 with ws://ws.example.com , it’s not working. it reports:

websockets.exceptions.RedirectHandshake: redirect to https://ws.example.com/

You need to use wss:// (i.e. websockets + TLS). And also you don’t need a separate subdomain, you can put both in the same site block.