1. The p roblem I’m having:
Hi, I’m trying to setup server with IP 193.180.210.41 as reverse proxy for multiple domains with custom destinations, proxies, etc. per domain. I want to point several domains’ DNS IPs to this “main” server and control their behaviour and further destinations on this server.
Let’s call this MASTER server. I have another Caddy server with IP 193.180.210.91. which will host some of these apps - let’s call it SLAVE server. I’ve chosen to ommit Cloudflare and another stuff and showcase only one domain for simplicity. After setting up both servers with configuration provided below I get error 400.
2. Error messages and/or full log output:
After running curl -v https://cafourek.dev
CURL OUTPUT
* Host cafourek.dev:443 was resolved.
* IPv6: (none)
* IPv4: 193.180.210.41
* Trying 193.180.210.41:443...
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: none
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256 / X25519MLKEM768 / id-ecPublicKey
* ALPN: server accepted h2
* Server certificate:
* subject: CN=cafourek.dev
* start date: Apr 22 09:36:56 2025 GMT
* expire date: Jul 21 09:36:55 2025 GMT
* subjectAltName: host "cafourek.dev" matched cert's "cafourek.dev"
* issuer: C=US; O=Let's Encrypt; CN=E6
* SSL certificate verify ok.
* Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA384
* Certificate level 1: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using sha256WithRSAEncryption
* Certificate level 2: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
* Connected to cafourek.dev (193.180.210.41) port 443
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://cafourek.dev/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: cafourek.dev]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.13.0]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: cafourek.dev
> User-Agent: curl/8.13.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Request completely sent off
< HTTP/2 400
< alt-svc: h3=":443"; ma=2592000
< via: 1.0 Caddy
< content-type: text/plain; charset=utf-8
< date: Tue, 22 Apr 2025 12:44:56 GMT
<
Client sent an HTTP request to an HTTPS server.
* Connection #0 to host cafourek.dev left intact
MASTER SERVER
{"level":" deug","ts":1745325791.0271034,"logger":"events","msg":"event","name":"tls_get_certificate","id":"6981cd74-7421-4a43-be98-3b6511b14d25","origin":"tls","data":{"client_hello":{"CipherSuites":[4866,4867,4865,49196,49200,159,52393,52392,52394,49195,49199,158,49188,49192,107,49187,49191,103,49162,49172,57,49161,49171,51,157,156,61,60,53,47],"ServerName":"cafourek.dev","SupportedCurves":[4588,29,23,30,24,25,256,257],"SupportedPoints":"AAEC","SignatureSchemes":[2309,2310,2308,1027,1283,1539,2055,2056,2074,2075,2076,2057,2058,2059,2052,2053,2054,1025,1281,1537,771,769,770,1026,1282,1538],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[772,771],"RemoteAddr":{"IP":"31.30.115.29","Port":36448,"Zone":""},"LocalAddr":{"IP":"172.18.0.2","Port":443,"Zone":""}}}}
{"level":"debug","ts":1745325791.0272899,"logger":"tls.handshake","msg":"choosing certificate","identifier":"cafourek.dev","num_choices":1}
{"level":"debug","ts":1745325791.0273001,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"cafourek.dev","subjects":["cafourek.dev"],"managed":true,"issuer_key":"acme-v02.api.letsencrypt.org-directory","hash":"3a86d5ca86480eced936ffd05910fceca8eb70bd78694f7839ef07b6ee3af622"}
{"level":"debug","ts":1745325791.0273116,"logger":"tls.handshake","msg":"matched certificate in cache","remote_ip":"31.30.115.29","remote_port":"36448","subjects":["cafourek.dev"],"managed":true,"expiration":1753090616,"hash":"3a86d5ca86480eced936ffd05910fceca8eb70bd78694f7839ef07b6ee3af622"}
{"level":"debug","ts":1745325791.0777578,"logger":"http.handlers.reverse_proxy","msg":"selected upstream","dial":"193.180.210.91:443","total_upstreams":1}
{"level":"debug","ts":1745325791.0798917,"logger":"http.handlers.reverse_proxy","msg":"upstream roundtrip","upstream":"193.180.210.91:443","duration":0.001985279,"request":{"remote_ip":"31.30.115.29","remote_port":"36448","client_ip":"31.30.115.29","proto":"HTTP/2.0","method":"GET","host":"cafourek.dev","uri":"/","headers":{"User-Agent":["curl/8.13.0"],"X-Forwarded-For":["31.30.115.29"],"X-Forwarded-Proto":["https"],"X-Forwarded-Host":["cafourek.dev"],"Via":["2.0 Caddy"],"Accept":["*/*"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"cafourek.dev"}},"headers":{},"status":400}
SLAVE SERVER
{"level":"debug","ts":1745325829.324436,"logger":"http.stdlib","msg":"http: TLS handshake error from 193.180.210.41:44604: client sent an HTTP request to an HTTPS server"}
3. Caddy version:
Both serves using:
v2.10.0 h1:fonubSaQKF1YANl8TXqGcn4IbIRUDdfAkpcsfI/vX5U=
4. How I installed and ran Caddy:
Same docker-compose on both servers
services:
caddy:
image: caddy
container_name: caddy
hostname: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443/443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./caddy_config:/data
- ./caddy_data:/config
_ ./caddy_logs:/var/log/caddy
a. System environment:
Debian GNU/Linux 12 (bookworm)
Docker version 28.1.1, build 4eba377
b. Command:
docker compose up -d && docker logs caddy -f
c. Service/unit/compose file:
d. My complete Caddy config:
MASTER SERVER 193.180.210.41
{
debug
servers {
trusted_proxies static 193.180.210.91
}
}
cafourek.dev {
reverse_proxy 193.180.210.91:443
}
SLAVE SERVER 193.180.210.91
{
debug
auto_https off
}
cafourek.dev {
respond 'OK'
}