HTTP falls back to HTTP/2 even though HTTP/3 is enabled

I have a good configuration from v2 that redirects all :80 traffic to :443 and has a wildcard certificate for the domain. HTTP/2 has worked flawlessly for a long time. My configuration is relatively simple (a few services in a docker compose file) so upgrading to v3 required no changes. I enabled HTTP/3 on my TLS entrypoint (:443) and opened port 443 for UDP traffic in the compose file.

netstat shows the service listening on UDP port 443 locally and nmap/netcat from another host on the LAN indicate traffic is not blocked by any firewalls (neither device nor network). Firefox and curl connect to the server but fail to upgrade the connection to HTTP/3.

curl -I shows the svc-adv seems to be coming through OK:

alt-svc: h3=":443"; ma=2592000

curl --http3 works because it falls back to HTTP/2 while curl --http3-only displays the following verbose output after a 30 second timeout:

* Host my.domain:443 was resolved.
* IPv6: (none)
* IPv4: 192.168.0.2
*   Trying 192.168.0.2:443...
* QUIC cipher selection: TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* QUIC connection has been shut down
* QUIC connect to 192.168.0.2 port 443 failed: Couldn't connect to server
* Failed to connect to my.domain port 443 after 30123 ms: Couldn't connect to server
* Closing connection
curl: (7) QUIC connection has been shut down

Any tips would be appreciated. Thanks!

Share your full Traefik static and dynamic config, and docker-compose.yml if used.

Thanks bluepuma77, docker-compose.yml:

version: "3.7"

networks:

  # network for services proxied by traefik
  frontend:
    ipam:
      config:
        - subnet: 172.31.0.0/24
        # gateway will be 172.31.0.1

secrets:
  namecheap_key:
    file: secrets/namecheap_key
  namecheap_user:
    file: secrets/namecheap_user

volumes:
  traefik-acme:

services:

  traefik:
    image: traefik:v3.0
    container_name: traefik 
    restart: unless-stopped
    command:
      - --global.checknewversion=false
      - --global.sendanonymoususage=false
      - --api=true
      - --entrypoints.web.address=:80
      - --entrypoints.web.http.redirections.entryPoint.to=websecure
      - --entrypoints.portal.address=:8880
      - --entrypoints.portal.http.redirections.entryPoint.to=websecure
      - --entrypoints.websecure.address=:443
      - --entrypoints.websecure.http.tls.certResolver=leresolver
      - --entrypoints.websecure.http.tls.domains[0].main=$DOMAIN
      - --entrypoints.websecure.http.tls.domains[0].sans=*.$DOMAIN
      - --entrypoints.websecure.http3
      - --certificatesresolvers.leresolver.acme.email=admin@$DOMAIN
      - --certificatesresolvers.leresolver.acme.storage=/acme/acme.json
      - --certificatesresolvers.leresolver.acme.dnschallenge.provider=namecheap
      - --certificatesresolvers.leresolver.acme.dnschallenge.delaybeforecheck=90
      - --providers.docker=true
      - --providers.docker.defaultRule=Host(`{{ index .Labels "com.docker.compose.service"}}.$DOMAIN`)
      - --providers.docker.network=docker_frontend
      - --providers.file.directory=/config
    environment:
      NAMECHEAP_API_KEY_FILE: /run/secrets/namecheap_key
      NAMECHEAP_API_USER_FILE: /run/secrets/namecheap_user
      TZ:
    networks:
      frontend:
        ipv4_address: 172.31.0.254
    ports:
      - "80:80"
      - "443:443/tcp"
      - "443:443/udp"
      - "8880:8880"
    volumes:
      - traefik-acme:/acme
      - $DOCKERDIR/traefik/config:/config
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    secrets:
      - namecheap_key
      - namecheap_user
    labels:
      - traefik.enable=true
      - traefik.http.routers.traefik.entrypoints=websecure
      - traefik.http.routers.traefik.service=api@internal

  unifi:
    user: unifi
    image: ghcr.io/jacobalberty/unifi-docker
    container_name: unifi
    restart: unless-stopped
    ports:
      - "8080:8080"
      - "8443:8443"
      - "3478:3478/udp"
    environment:
      TZ:
    networks:
      frontend:
    volumes:
      - $DOCKERDIR/unifi:/unifi
    labels:
      - traefik.enable=true
      - traefik.http.routers.unifi.entrypoints=websecure
      - traefik.http.routers.unifi.service=unifi-docker
      - traefik.http.services.unifi-docker.loadbalancer.server.port=8443
      - traefik.http.services.unifi-docker.loadbalancer.server.scheme=https
      - traefik.http.services.unifi-docker.loadbalancer.serverstransport=unifi@file
      - traefik.http.routers.portal.rule=Host(`portal.$DOMAIN`)
      - traefik.http.routers.portal.entrypoints=websecure
      - traefik.http.routers.portal.service=portal-docker
      - traefik.http.services.portal-docker.loadbalancer.server.port=8880
      - traefik.http.services.portal-docker.loadbalancer.server.scheme=http

dynamic configuration:

http:
  routers:
    dsm:
      entrypoints:
      - websecure
      service: dsm-synology
      rule: Host(`dsm.my.domain`)

  services:
    dsm-synology:
      loadBalancer:
        servers:
        - url: http://172.31.0.1:5000/

  serversTransports:
    unifi:
      insecureSkipVerify: true

  middlewares:
    https-redirect:
      redirectScheme:
        scheme: https
        permanent: true
        port: "443"

    secure-headers:
      headers:
        contentTypeNosniff: true
        customFrameOptionsValue: "SAMEORIGIN"
        stsSeconds: 31536000
        stsIncludeSubdomains: true
        referrerPolicy: "same-origin"
        customResponseHeaders:
          server: ""
          Permissions-Policy: "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()"

Maybe try

--entryPoints.name.http3=true

Did you read the note (doc):

As HTTP/3 actually uses UDP, when traefik is configured with a TCP entryPoint on port N with HTTP/3 enabled, the underlying HTTP/3 server that is started automatically listens on UDP port N too. As a consequence, it means port N cannot be used by another UDP entryPoint. Since HTTP/3 requires the use of TLS, only routers with TLS enabled will be usable with HTTP/3.

Adding =true didn't seem to have any effect. Yes, I saw that note. You'll see in my static (cli) config that there are no UDP entrypoints defined--just the three http entrypoints. And yes, all of the routers using the websecure entrypoint (which are all of them) use the static TLS configuration (letsencrypt resolver) and the traefik GUI is showing TLS=true for all routers.

Maybe check this pull request.

Hmm. Doesn't seem like much there except a convenient link to the docs we both have read. I'll give it a few more days and if nobody has any ideas here I'll probably file a bug report. I suspect there is something non-obvious about my configuration that is to blame, but given that you've also looked over my work and don't see anything obviously wrong, that suggests either the docs need improving or there is a legit bug.

An issue has been created: https://github.com/traefik/traefik/issues/10686

1 Like