TLS and trust boundaries
Use HTTPS upstreams
Verify an HTTPS backend instead of disabling certificate checks between the proxy and application.
10 minute lesson
So far the proxy talked plain HTTP to its upstreams. On a loopback interface that’s fine. But when the backend lives on another machine — a different host, a cloud VM, someone else’s network — the second connection deserves the same protection as the first.
An HTTPS upstream adds encryption and identity verification on the proxy-to-backend connection. And identity is the part people forget: the certificate’s name must match the upstream TLS server name, and it must chain to a CA the proxy trusts. Otherwise you’re encrypting traffic to a server you never verified.
Proxy to a named HTTPS backend
Give the lab backend a name and a certificate signed by a lab CA (the TLS course on this site walks through creating one). Then proxy to it by name:
reverse_proxy https://backend.lab.test:4443 {
transport http {
tls_trust_pool file lab-ca.crt
}
}
Two things changed from the plain setup. The upstream address now carries https:// and the TLS port. And the transport declares which CA to trust: tls_trust_pool file lab-ca.crt points Caddy at the lab CA certificate instead of the system trust store, which knows nothing about your lab.
Verify both directions of the check
First the success path:
curl https://app.lab.test/
# {"port":4443,"path":"/"}
Now make it fail on purpose, because a check you’ve never seen fail isn’t verified. Point tls_trust_pool at the wrong CA file and reload. The proxy returns a 502, and its log shows a certificate verification error: it refused to talk to an upstream it couldn’t authenticate. Do the same by proxying to the backend’s IP address instead of backend.lab.test — name mismatch, same refusal.
Confirm the proxy fails with the wrong CA or hostname and succeeds with the intended trust root and certificate. Those two failures are the security feature working.
The shortcut to refuse
When this fails in production, you’ll find advice suggesting tls_insecure_skip_verify. It makes the error disappear by skipping verification entirely — the proxy will then accept any certificate from anyone, including an attacker between it and the backend.
Avoid tls_insecure_skip_verify. The error is telling you trust or identity is misconfigured. Repair trust and identity so the upstream connection remains authenticated: fix the trust pool, fix the certificate name, or reissue the certificate. Every one of those is a real fix. The flag is not.
Lesson completed