If I want to serve http and websockets on the same port, do I need to do anything extra for websockets?

1. Output of caddy version:

2.6.2-alpine (in podman)

2. How I run Caddy:

podman run -d \
 --name caddy \
 --pod proxy \
 --secret source=test-crt,target=/certs/test.crt,type=mount \
 --secret source=test-key,target=/certs/test.key,type=mount \
 --secret source=trust-pem,target=/certs/trust.pem,type=mount \
 --volume ./Caddyfile:/etc/caddy/Caddyfile \
 --volume ./errors:/var/www/errors \
 --env "OUTER_HOST=$(hostname)" \
 docker.io/library/caddy:2.6.2-alpine

a. System environment:

Linux (Manjaro), Podman

b. Command:

Command listed above.

c. Service/unit/compose file:

podman secret create --driver=file test-p12 ./certs/test.p12
podman secret create --driver=file test-crt ./certs/test.crt
podman secret create --driver=file test-key ./certs/test.key
podman secret create --driver=file trust-jks ./certs/truststore.jks
podman secret create --driver=file trust-pem ./certs/myCA.pem

podman network create services_network

podman pod create \
 --name proxy \
 --network podman,services_network \
 --publish 8443:8443

podman pod create \
 --name greeting-service \
 --network services_network

podman pod create \
 --name portainer \
 --network services_network \
 --publish 9443:9443

podman run -d \
 --name greeting_service \
 --pod greeting-service \
 --secret source=test-p12,target=/certs/test.p12,type=mount \
 --secret source=trust-jks,target=/certs/trust.jks,type=mount \
 --env "OUTER_HOST=$(hostname)" \
 docker.io/library/greeting-service

podman run -d \
 --name portainer_ce \
 --pod portainer \
 --secret source=test-p12,target=/certs/test.p12,type=mount \
 --secret source=trust-jks,target=/certs/trust.jks,type=mount \
 --volume /run/user/1000/podman/podman.sock:/var/run/docker.sock:Z \
 docker.io/portainer/portainer-ce

podman run -d \
 --name caddy \
 --pod proxy \
 --secret source=test-crt,target=/certs/test.crt,type=mount \
 --secret source=test-key,target=/certs/test.key,type=mount \
 --secret source=trust-pem,target=/certs/trust.pem,type=mount \
 --volume ./Caddyfile:/etc/caddy/Caddyfile \
 --volume ./errors:/var/www/errors \
 --env "OUTER_HOST=$(hostname)" \
 docker.io/library/caddy:2.6.2-alpine

d. My complete Caddy config:

{
    debug
    auto_https off
    ocsp_stapling off
}

# Snippet to include in matchers that need these proxy headers
# and not have to repeat this info every time
(proxy_headers) {
    header_up Host                   {$OUTER_HOST}
    header_up X-ProxiedEntitiesChain {xpechain}
    header_up X-ProxiedIssuersChain  {xpichain}
}

# Snippet to configure tls parameters for importing so that
# the info does not have to be repeated every time
(proxy_http) {
    transport http {
        tls_server_name              {$OUTER_HOST}
        tls_trusted_ca_certs         /certs/trust.pem
        tls_client_auth              /certs/test.crt /certs/test.key
    }
}

:8443 {
    tls /certs/test.crt /certs/test.key {
        client_auth {
            mode                   require_and_verify
            trusted_ca_cert_file   /certs/trust.pem
        }
    }

    # set this for the path to static error page content
    # look at what the nginx config is setting for this
    root /* /var/www
    file_server

    # Named matcher for websockets, but I do not know if
    # I will need to use this, or not
	@websockets {
		header Connection *Upgrade*
		header Upgrade    websocket
	}

    map {header.X-ProxiedEntitiesChain} {xpechain} {
        ~^(.+)$ {header.X-ProxiedEntitiesChain}<{tls_client_subject}>
        default <{tls_client_subject}>
    }

    map {header.X-ProxiedIssuersChain} {xpichain} {
        ~^(.+)$ {header.X-ProxiedIssuersChain}<{tls_client_issuer}>
        default <{tls_client_issuer}>
    }

    handle /greeting-service/* {
        reverse_proxy https://greeting-service:8443 {
            import proxy_headers
            import proxy_http
        }
    }

    handle_errors {
        @custom_err file /errors/err-{err.status_code}.html /errors/err.html
        handle @custom_err {
            rewrite * {file_match.relative}
            file_server
        }
        respond "{err.status_code} {err.status_text}"
    }
}

3. The problem I’m having:

I have the named matcher for websockets in the Caddyfile, but I am not using it. If I want to have websockets on the same port as HTTP, do I need to use that matcher, or do anything special for the websockets, or should they just work?

4. Error messages and/or full log output:

N/A

5. What I already tried:

I was unable to find the answer in the documentation, and I also searched in the forums. I saw one post that seemed to suggest that nothing special was necessary, but I am hoping to confirm before chasing my tail.

6. Links to relevant resources:

This is the post that led me to believe that I might not have to do anything additional to get websockets working in the same matcher that handles HTTP.

You only need this if you want to serve different content at the same URLs, e.g. a file server and websockets at your root path.

If you’re just using reverse_proxy to a server that handles both HTTP and WebSockets, then there’s nothing to do at all, since Caddy will handle both transparently.

First, you should use root * /var/www (i.e. *, not /*; the difference is * actually means “all requests” and /* means “a path matcher which matches all paths”, and the latter is subtly less efficient).

Second, you should probably put this in a handle as a fallback (a handle with no matcher), instead of at the top level, because then it will ensure mutual exclusivity with your other handle routes.

I’d recommend also adding root to your handle_errors for clarify.

2 Likes

Ok, thanks for the advice. I moved the setting of root into a fallback handler. When you suggest adding root to the handle_errors block, I admit that I don’t know what you mean. Repeat the line root * /var/www, or something else?

Yep. Because it wouldn’t have run in other handle blocks, so there wouldn’t be a root set when you get to the error handling otherwise. It’s needed for the file matcher and file_server to work correctly.

1 Like

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