Ramblings of an aging IT geek
← Ramblings of an aging IT geek
homelab

reverse proxy, certs, and let's encrypt at home

How I put a single reverse proxy in front of a pile of homelab services and got real HTTPS certificates without ever opening a port to the internet.

A small server rack with patch cables

The homelab had grown the way they all do: a service here on port 8080, another there on 9000, each one a different IP and port I had to remember, none of them with a certificate, every browser visit a parade of "your connection is not private" warnings I had trained myself to click through. That last habit is the dangerous one, so over the quiet days after Christmas I fixed it properly. One reverse proxy in front, real certificates behind it, and nothing exposed to the internet.

the proxy

The front door is a single reverse proxy. I use Caddy because its automatic HTTPS does the tedious part for you, but nginx or Traefik get you to the same place with more configuration. Every service gets a proper hostname under a domain I own, and the proxy routes by that hostname to the right internal address:

grafana.home.example.com {
    reverse_proxy 10.0.0.20:3000
}

jellyfin.home.example.com {
    reverse_proxy 10.0.0.21:8096
}

That alone is worth it. I stopped memorising port numbers and started typing names. But the names still needed to be trusted, which is where the certificates come in.

certs without exposing anything

Here is the part people get stuck on. The usual way Let's Encrypt proves you own a domain is the HTTP-01 challenge, which needs an inbound connection from their servers to your box on port 80. I don't want to open a port to the internet just to serve a homelab dashboard to myself.

The answer is the DNS-01 challenge. Instead of proving control by serving a file, you prove it by creating a TXT record in your domain's DNS. Nothing inbound, nothing exposed. If your DNS provider has an API, the proxy can create and tear down that record automatically each renewal. With Caddy it is a one-line addition pointing at the relevant DNS plugin and an API token, and from then on it just happens.

A homelab shelf with networking gear

The other half of this is split-horizon DNS. The hostnames are real, public names in a domain I own, but internally my DNS resolver answers them with the proxy's private LAN address. So grafana.home.example.com resolves to 10.0.0.5 inside the house and goes nowhere at all outside it. The certificate is genuine and publicly trusted, the traffic never leaves the LAN, and there is no port forwarding anywhere in the picture.

what it bought me

A few things fell out of this that I didn't fully anticipate. Every internal service now has a green padlock and a sensible name, so I no longer train myself to ignore certificate warnings, which means a real warning would actually register. Adding a new service is three lines in the proxy config and a DNS entry, not a fresh round of port juggling. And because the proxy is the only thing terminating TLS, the certificate handling lives in exactly one place instead of being smeared across a dozen apps that each do it badly.

None of this is novel, and people far more organised than me have been doing it for years. But it is the kind of tidy-up that pays back every single day, and the DNS-01 trick in particular is worth knowing precisely because it removes the "but I'd have to open a port" objection that keeps so many homelabs on self-signed certificates and clicked-through warnings. You don't have to expose anything. You just have to let DNS do the proving.