From d0412b21f6a5a5033de244d3eb57017558c77cec Mon Sep 17 00:00:00 2001 From: dolonet Date: Fri, 10 Apr 2026 10:50:26 +0000 Subject: [PATCH] Fix ACME HTTP-01 passthrough in HAProxy config Add an ACL that routes /.well-known/acme-challenge/ requests on :80 to Caddy instead of redirecting to HTTPS, so Let's Encrypt certificate issuance works out of the box. Also simplify Caddyfile to use Caddy's http_port/https_port directives. --- contrib/sni-router/Caddyfile | 18 +++++------------- contrib/sni-router/README.md | 13 +++---------- contrib/sni-router/docker-compose.yml | 1 + contrib/sni-router/haproxy.cfg | 11 ++++++++++- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/contrib/sni-router/Caddyfile b/contrib/sni-router/Caddyfile index 9621307..426a3ad 100644 --- a/contrib/sni-router/Caddyfile +++ b/contrib/sni-router/Caddyfile @@ -1,19 +1,11 @@ { - # Caddy listens on 8443 behind HAProxy, which passes raw TLS through. - # Caddy terminates TLS itself and auto-obtains a Let's Encrypt certificate. - # - # If your domain's DNS already points to this server, ACME HTTP-01 challenge - # works through the HAProxy http frontend (:80 → redirect). For DNS-01 - # or other ACME methods, see https://caddyserver.com/docs/automatic-https + # Caddy sits behind HAProxy which passes raw TLS through on :8443. + # ACME HTTP-01 challenges arrive on :80 via HAProxy's acl passthrough. + http_port 80 + https_port 8443 } -{$DOMAIN}:8443 { - tls { - # Use the ACME HTTP-01 challenge on port 80. - # HAProxy forwards :80 as HTTP, so Caddy can answer the challenge - # if you add an acl exception in haproxy.cfg (see README), or use - # DNS-01 instead. - } +{$DOMAIN} { root * /srv file_server } diff --git a/contrib/sni-router/README.md b/contrib/sni-router/README.md index 2ddcede..19b99b1 100644 --- a/contrib/sni-router/README.md +++ b/contrib/sni-router/README.md @@ -45,16 +45,9 @@ docker compose exec mtg mtg access /config/config.toml ## ACME (Let's Encrypt) notes -Caddy needs to answer the ACME HTTP-01 challenge on port 80. The -default `haproxy.cfg` redirects all `:80` traffic to HTTPS. If Caddy -cannot obtain a certificate, either: - -1. Temporarily stop HAProxy, let Caddy bind `:80` directly for the - initial certificate, then start the full stack; or -2. Use DNS-01 validation in the Caddyfile (requires a DNS provider - plugin); or -3. Add an HAProxy ACL that passes `/.well-known/acme-challenge/` - requests to the Caddy backend instead of redirecting. +HAProxy passes `/.well-known/acme-challenge/` requests on `:80` to +Caddy so that HTTP-01 validation works out of the box. Make sure your +domain's DNS A/AAAA record points to this server before starting. ## Architecture diff --git a/contrib/sni-router/docker-compose.yml b/contrib/sni-router/docker-compose.yml index 6216a00..a8198dd 100644 --- a/contrib/sni-router/docker-compose.yml +++ b/contrib/sni-router/docker-compose.yml @@ -44,6 +44,7 @@ services: - caddy_data:/data - ./www:/srv:ro expose: + - "80" - "8443" environment: DOMAIN: ${DOMAIN:-example.com} diff --git a/contrib/sni-router/haproxy.cfg b/contrib/sni-router/haproxy.cfg index 4762074..dabc8f9 100644 --- a/contrib/sni-router/haproxy.cfg +++ b/contrib/sni-router/haproxy.cfg @@ -20,11 +20,16 @@ defaults timeout client 60s timeout server 60s -# --- HTTP :80 — redirect to HTTPS ------------------------------------------- +# --- HTTP :80 — ACME challenges + redirect ----------------------------------- frontend http bind *:80 mode http + + # Let Caddy answer ACME HTTP-01 challenges for Let's Encrypt. + acl is_acme path_beg /.well-known/acme-challenge/ + use_backend web_acme if is_acme + http-request redirect scheme https code 301 # --- TLS :443 — SNI-based routing ------------------------------------------- @@ -45,3 +50,7 @@ backend mtg backend web server web web:8443 + +backend web_acme + mode http + server web web:80