I want to run Caddy as an HTTPS reverse proxy for a site “hidden” behind a Wireguard VPN. The goal is that unless a client has successfully established a tunnel with the server and belongs to its private network, browsing https://hidden.example.com won’t work.
To do that I’m running Wireguard on the server, which has a fixed IP in the private 10.0.0.0/8 range. Caddy is bound to that IP and uses a self-signed certificate, because LetsEncrypt is understandably not able to issue a cert for a private IP.
To finish off, on the DNS side I’ve created an A record for hidden.example.com whose value has been set to 10.0.0.1.
While this works well enough on Firefox despite the security warning, on Chrome it doesn’t. Chrome simply refuses to proceed anyway to the site, and complains about the server using HSTS (which I know isn’t true).
I was wondering if there’s a less awkward way to serve HTTPS sites from a server on a private IP that I might be missing, hopefully without the security warnings.
2. Error messages and/or full log output:
No errors
3. Caddy version:
4. How I installed and ran Caddy:
Caddy’s official APT repository.
a. System environment:
Debian 12.
b. Command:
The default systemd unit that automatically picks up /etc/caddy/Caddyfile config.
Certificates are usually bound to domain names, not IP addresses. You can still use Let’s Encrypt with Caddy behind NAT or even on a private network. If Caddy is on an isolated network you can request and renew certificates on another host and transfer the certs to Caddy. You’d need to configure Caddy to load the external certificates instead of managing them internally. tls (Caddyfile directive) — Caddy Documentation
Just serve HTTP since all possible users are known and used the encrypted wireguard tunnels. The self-signed certificate isn’t really bringing any added security in that scenario.
Also, don’t set the HSTS header as it instructs the browsers that this site should be viewed over HTTPS only, and prevent downgrading to plain HTTP.
I know. I have a real domain, the twist is that its DNS entry points to a private IP instead of a public one. That’s why I think there’s no way for the LetsEncrypt service to issue a valid certificate for that combination of domain and IP address.
This was the first thing I tried, but I found out that the web app I’m reverse proxying uses some browser features that are only allowed when the site is served over HTTPS, so TLS is a hard requirement for me despite Wireguard already encrypting the connection.
Also I’m not doing anything HSTS related (see configuration block), I was just calling BS on Chrome:
Thanks for the pointers, I didn’t know it would be possible to sign such a certificate.
I was able to get a properly signed certificate using Caddy’s built-in DNS challenge, though this required a custom Caddy binary with a DNS Challenge provider (depends on how you edit your DNS records, in my case I was lucky because I do it in CloudFlare and Caddy supports that).