A homelab collects web interfaces the way a junk drawer collects cables – and nobody remembers which port opens which one. A reverse proxy gives every service a proper name and puts them all behind one front door.
The short answer
A reverse proxy is a single service that accepts all web requests, reads which name the client asked for, and hands each request to the right service behind it. It also ends the encryption in one place, so you manage certificates once instead of once per service. It makes a good front door, but it is not a complete security concept on its own.
How it works
- Reverse versus forward. A forward proxy works on behalf of clients: the client knows about it and, for example, uses the
CONNECTmethod to reach outside sites. A reverse proxy works on behalf of servers: clients are not aware of it and simply talk to what looks like the service [5]. In a homelab you almost always want the reverse kind – one address, many services behind it. For this guide we assume that names likedienst.homelab.examplealready resolve to the proxy; how that works is the topic of our article on DNS name resolution. - TLS ends at the proxy, the name picks the route. When a client opens a connection, TLS itself has no way to say which server it wants. The SNI extension fills that gap: RFC 6066 describes it for servers that host several virtual servers behind one network address, and says a server may use it to choose the certificate [13]. Inside the encrypted connection, the HTTP
Hostheader carries the name again, and the proxy uses it to pick the backend. Caddy's documentation describes both values as what servers use for routing and certificate selection [1]. After termination, Caddy by default talks plain HTTP to the backend [1]. NIST SP 800-207 holds that network location alone does not imply trust and that communication should be secured regardless of location [14]. So decide per service whether the last hop needs TLS as well, and remember that anhttps://upstream may need a matchingHostvalue [1]. - Certificates for internal names, option one: a public CA with DNS-01. ACME (RFC 8555) is the protocol that automates proving control of a name and issuing the certificate [11]. Let's Encrypt offers HTTP-01, which needs port 80 reachable from the internet and cannot issue wildcards, and DNS-01, which proves control through a TXT record, supports wildcards and works for servers that are not exposed [9]. Public CAs only issue for names you actually control: Caddy lists
localhost,.local,.internaland.home.arpaamong the names that do not qualify for publicly trusted certificates [2]. So internal services need names under a domain you own. The trade-offs: DNS-01 needs API credentials for your DNS provider, and Let's Encrypt warns that full credentials on a web server raise the damage if it is compromised; it suggests narrowly scoped credentials, a separate validation server, or delegating_acme-challengevia CNAME [9]. Issued certificates are logged publicly, and Let's Encrypt points to Certificate Transparency search tools for this [10]. In our reading, a wildcard certificate keeps individual service names out of the certificate itself. Traefik's certificate resolver documents the same DNS challenge, including CNAME delegation [8]. Be careful with rate limits: as of 18 Oct 2026, according to Let's Encrypt (page last updated 5 Aug 2026), 5 certificates per identical set of names and 50 per registered domain every 7 days, with a staging environment that has higher limits [10]. Caddy also advises testing against staging [2]. - Option two: your own CA. Caddy can generate a local CA with a root and an intermediate; leaf certificates are signed by the intermediate. This does not use ACME or DNS validation, and it is trusted only where the root is installed [2]. By Caddy's documented defaults, the root lasts 10 years, the intermediate 7 days and leaf certificates 12 hours [3]. Caddy tries to install the root into the trust store of its own machine, but that can fail in containers or under an unprivileged user; then you run
caddy trustor copy the root out by hand [3]. Putting the root on other devices is your job as administrator [2]. Every client without the root shows security errors [2]. And because a trusted root can vouch for any name on every device that trusts it, protect its key. Caddy calls trusting it safe as long as your computer is not compromised and the key does not leak [2]. - Forwarded headers and WebSockets. Behind a proxy, the backend sees the proxy as the client. RFC 7239 describes this loss of information and defines the
Forwardedheader with the parametersby,for,hostandproto; it calls the header optional and sensitive and says it should be off by default [12]. The olderX-Forwarded-For,X-Forwarded-ProtoandX-Forwarded-Hostare the de-facto, non-standard variants [12]. Caddy sets all three by default and ignores incoming values unless they come from addresses you list astrusted_proxies[1]. Traefik hasforwardedHeaders.trustedIPsper entry point and documents itsinsecuremode as for tests only [7]. In nginx, the originalHostis not passed by default; the proxied server gets$proxy_hostunless you setproxy_set_header Host $host;[6]. WebSockets need extra care:UpgradeandConnectionare hop-by-hop headers, so nginx needsproxy_set_header Upgrade $http_upgrade;and a matchingConnectionheader, and it closes idle tunnels after 60 seconds by default [5]. Caddy supports WebSockets itself but closes them on a config reload unless you setstream_close_delay[1]. - Typical failure patterns. Redirect loops often appear when the backend sees plain HTTP, believes the visitor is insecure and redirects to HTTPS again and again. Check that the proxy sends the original scheme and host and that the app is told to trust the proxy. Logs showing only the proxy address as client point to missing or untrusted forwarded headers. Certificate warnings mostly come from one of three causes: the CA root is not trusted on that client, the name in the browser is not in the certificate, or someone opened the service by IP address. RFC 6066 notes that a mismatch between requested name and certificate name shows up when the client checks the server identity [13].
- Security: one entry point means one target. Funnelling everything through one proxy simplifies logging, certificates and updates. It also means that this proxy is the first thing an attacker meets, so keep its software current. Forward only the ports the proxy needs, and keep management interfaces such as NAS or hypervisor consoles out of public reach. Bind backends so they accept traffic only from the proxy, otherwise the proxy can be bypassed – that is our recommendation, not a quote from a source. A pre-check can add login in front of an app: Caddy's
forward_authsends a copy of the request to an authentication gateway, continues on a2xxanswer and otherwise passes the gateway's response, usually a login redirect, back to the client [4]. Treat logs as sensitive data; RFC 7239 itself calls the client details in forwarded headers sensitive [12]. - The Zero Trust connection, stated carefully. In NIST SP 800-207, a policy enforcement point enables, monitors and eventually terminates connections between a subject and a resource, and it can be a gateway component in front of the resource [14]. A reverse proxy can play that part. The same document expects an organisation to have identity, credential and access management plus asset management in place, and describes access as decided per session by dynamic policy that includes the state of identity and device [14]. A reverse proxy alone brings none of that. In our reading, it is a possible enforcement point, not a replacement for identities, an inventory or written policies.
A homelab example
In our homelab at Bytearium, an internal certificate authority exists, based on "Caddy Local Authority 2026 ECC Root". The certificate is prepared on one workstation but not yet entered as system-wide trusted (status 14–18 Oct 2026). Until that step is done, clients relying on the system trust store would show warnings for certificates from that CA, because trust is exactly what the root provides [2]. We treat that as an open task, not as finished work. We do not name hosts or services here.
For a generic example with a placeholder domain: in Caddy you write a site block with the address dienst.homelab.example and two lines inside it, tls internal and reverse_proxy backend:8080 [1][3]. That serves the service over HTTPS with a certificate from the local CA and forwards requests to the backend named backend. If you prefer a public CA, replace the first line with a tls block containing dns and your provider's name [3]. This is an illustration, not recorded output.
Checklist
- Check whether every internal name resolves to the proxy and not to the individual backends.
- Decide whether you use a public CA with DNS-01 or your own CA, and write down why.
- Check whether every client device, including phones and containers, trusts your own root before you roll it out.
- Limit the DNS API credentials to the TXT records you need, or delegate
_acme-challengeto a separate zone. - Test certificate issuance against a staging endpoint first, so you do not hit the rate limits.
- Check whether the backend receives the original scheme, host and client address, and whether it trusts only the proxy as the source of those headers.
- Test WebSocket-based pages, and check idle timeouts and what happens on a config reload.
- Check that management interfaces are not reachable from the internet and that backends accept traffic only from the proxy.
- Update the proxy regularly and review its logs; write down which part of Zero Trust you cover and which you do not.