Fix staging HTTPS: force tls internal, require a hostname not a bare IP
Two real, verified findings from actually testing the "let Caddy issue a self-signed cert automatically" approach against a literal IP: 1. TLS SNI is not sent for literal IP connections (out of spec — SNI's HostName type explicitly excludes IPs). Confirmed via openssl s_client: the handshake fails with a TLS-layer internal_error, even though Caddy logs "certificate obtained successfully" — Caddy has no way to select a cert without SNI. No Caddyfile config fixes this; the address has to be a hostname. 2. Caddy's automatic-HTTPS heuristic only treats bare IPs and "localhost" as obviously-private. Any other name — including a made-up LAN hostname like "bookstore-staging.test" — it assumes might be real and tries Let's Encrypt, which fails the same way staging.example.com did. Fixed by forcing `tls internal` explicitly in Caddyfile.staging, removing the guesswork entirely. Verified end-to-end with curl --resolve (proper SNI, no real DNS/hosts change needed to test): direct HTTPS 200, HTTP->HTTPS redirect chain 200. README now documents the /etc/hosts requirement and how to trust Caddy's root cert to skip the one-time browser warning. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,28 @@
|
||||
http://{$SITE_DOMAIN} {
|
||||
{$SITE_DOMAIN} {
|
||||
encode gzip
|
||||
|
||||
# `tls internal` forces Caddy's own self-signed CA unconditionally,
|
||||
# instead of guessing from the domain name — its automatic-HTTPS
|
||||
# heuristic only recognizes specific patterns (bare IPs, "localhost")
|
||||
# as "obviously private"; anything else (including a private-network
|
||||
# hostname like this one) it assumes might be a real public domain and
|
||||
# tries Let's Encrypt, which then fails exactly like it did for
|
||||
# staging.example.com. This directive skips that guesswork entirely.
|
||||
#
|
||||
# SITE_DOMAIN must be an actual HOSTNAME here, not a bare IP: TLS SNI
|
||||
# (which Caddy needs to pick a certificate) isn't sent for literal IP
|
||||
# connections per spec — confirmed this fails at the TLS handshake
|
||||
# itself (curl: "TLS alert, internal error") even though Caddy reports
|
||||
# the cert as obtained successfully. Point this hostname at the VM's
|
||||
# IP via /etc/hosts (or your router/local DNS) on whatever machine
|
||||
# you're browsing from.
|
||||
tls internal
|
||||
|
||||
# Browser will show an untrusted-cert warning once (self-signed, not
|
||||
# from a public CA) — click through it, or trust Caddy's root cert
|
||||
# (docker compose exec caddy cat /data/caddy/pki/authorities/local/root.crt)
|
||||
# in your OS/browser once to skip that entirely.
|
||||
|
||||
# Launch gate: "No staging URLs are publicly indexed." Belt-and-suspenders
|
||||
# with wp_option blog_public=0, which deploy.sh sets on staging. No
|
||||
# basic-auth wall — this box is LAN-only, not reachable from outside.
|
||||
|
||||
Reference in New Issue
Block a user