Trouble with multiple domains serving the same static page

1. The problem I’m having:

Context: I have a small Bluesky/ATProto PDS running my fork of the official PDS repo.

The bluesky-social/pds repo on GitHub uses Caddy as part of its setup.

My fork has a few modifications to expose the /srv folder to Docker environment. I made this to serve a static page which can be accessed through my domain, caramelo.social.br. It is currently accessible over web.

I have a second domain, bisca.net.br, which I want to redirect to the same server and receive the same static page in response. Sounds simple, right?

The problem I’m having is: this second domain always returns a SSL error, while the first works fine.

I tried to follow the instructions provided here: Multiple domains, but it didn’t work for me.

2. Error messages and/or full log output:

curl -vL https://bisca.net.br:

*   Trying 191.252.210.214:443...
* Connected to bisca.net.br (191.252.210.214) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Unknown (21):
* TLSv1.3 (IN), TLS alert, internal error (592):
* error:0A000438:SSL routines::tlsv1 alert internal error
* Closing connection 0
curl: (35) error:0A000438:SSL routines::tlsv1 alert internal error

docker logs pds --follow:

{"level":30,"time":1767888545803,"pid":7,"hostname":"lf-proto-pds.vps-kinghost.net","name":"pds","req":{"id":12,"method":"GET","url":"/tls-check?domain=bisca.net.br","query":{"domain":"bisca.net.br"},"params":{},"headers":{"host":"localhost:3000","user-agent":"Go-http-client/1.1","accept-encoding":"gzip"}},"res":{"statusCode":400,"headers":{"x-powered-by":"Express","access-control-allow-origin":"*","content-type":"application/json; charset=utf-8","content-length":"78","etag":"W/\"4e-B4TQPtiXf/PDKkAoOJY4fpev3Wc\"","vary":"Accept-Encoding"}},"responseTime":2,"msg":"request completed"}

3. Caddy version:

docker compose exec caddy caddy version:

v2.10.2 h1:g/gTYjGMD0dec+UgMw8SnfmJ3I9+M2TdvoRL/Ovu6U8=

4. How I installed and ran Caddy:

a. System environment:

Debian 12, Linux kernel 6.1.0-41-amd64
Docker version 29.1.3, build f52814d

b. Command:

systemctl start pds.service

c. Service/unit/compose file:

cat /etc/systemd/system/pds.service:

[Unit]
Description=Bluesky PDS Service
Documentation=https://github.com/bluesky-social/pds
Requires=docker.service
After=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/pds
ExecStart=/usr/bin/docker compose --file /pds/compose.yaml up --detach
ExecStop=/usr/bin/docker compose --file /pds/compose.yaml down

[Install]
WantedBy=default.target

compose.yaml:

version: '3.9'
services:
  caddy:
    container_name: caddy
    image: caddy:2
    network_mode: host
    depends_on:
      - pds
    restart: unless-stopped
    volumes:
      - type: bind
        source: /pds/caddy/data
        target: /data
      - type: bind
        source: /pds/caddy/etc/caddy
        target: /etc/caddy
      - type: bind
        source: /srv
        target: /srv
  pds:
    container_name: pds
    image: ghcr.io/bluesky-social/pds:0.4
    network_mode: host
    restart: unless-stopped
    volumes:
      - type: bind
        source: /pds
        target: /pds
    env_file:
      - /pds/pds.env
  watchtower:
    container_name: watchtower
    image: ghcr.io/nicholas-fedor/watchtower:latest
    network_mode: host
    volumes:
      - type: bind
        source: /var/run/docker.sock
        target: /var/run/docker.sock
    restart: unless-stopped
    environment:
      WATCHTOWER_CLEANUP: true
      WATCHTOWER_SCHEDULE: "@midnight"

d. My complete Caddy config:

{
        debug
        email lf********@yahoo.com
        on_demand_tls {
                ask http://localhost:3000/tls-check
        }
}

*.caramelo.social.br, caramelo.social.br {
        root * /srv
        tls {
                on_demand
        }
        @pds {
                path /xrpc/*
                path /.well-known/atproto-did
                path /.well-known/oauth-protected-resource
                path /.well-known/oauth-authorization-server
                path /oauth/*
                path /@atproto/*
                path /gate/*
        }
        reverse_proxy @pds http://localhost:3000
        encode
        templates
        file_server
}

*.bisca.net.br, bisca.net.br {
        root * /srv
        tls {
                on_demand
        }
        @pds {
                path /xrpc/*
                path /.well-known/atproto-did
                path /.well-known/oauth-protected-resource
                path /.well-known/oauth-authorization-server
                path /oauth/*
                path /@atproto/*
                path /gate/*
        }
        reverse_proxy @pds http://localhost:3000
        encode
        templates
        file_server
}

5. Links to relevant resources:

Bluesky PDS repo: GitHub - bluesky-social/pds: Bluesky PDS (Personal Data Server) container image, compose file, and documentation
My PDS fork: GitHub - luan-u/bsky-pds at caramelo

I don’t think you can mix wildcards with on demand TLS. Use the DNS-01 challenge to get a wildcard certificate issued in advance.

I didn’t have problems with the “caramelo” domain and wildcards, only with the “bisca” domain. Weird.

However, based on your suggestion, I edited my Caddyfile to remove TLS on-demand only for bisca.net.br:

{
        debug
        email lf********@yahoo.com
        on_demand_tls {
                ask http://localhost:3000/tls-check
        }
}

*.caramelo.social.br, caramelo.social.br {
        root * /srv
        tls {
                on_demand
        }
        @pds {
                path /xrpc/*
                path /.well-known/atproto-did
                path /.well-known/oauth-protected-resource
                path /.well-known/oauth-authorization-server
                path /oauth/*
                path /@atproto/*
                path /gate/*
        }
        reverse_proxy @pds http://localhost:3000
        encode
        templates
        file_server
}

*.bisca.net.br, bisca.net.br {
        root * /srv
        @pds {
                path /xrpc/*
                path /.well-known/atproto-did
                path /.well-known/oauth-protected-resource
                path /.well-known/oauth-authorization-server
                path /oauth/*
                path /@atproto/*
                path /gate/*
        }
        reverse_proxy @pds http://localhost:3000
        encode
        templates
        file_server
}

It seems to have solved my problem for now, since my main concern was to just open the web page in a browser. Thanks for the tip.