📖 Chapter 06 — Reverse Proxies and TLS
Expose services to the internet with HTTPS, the right way.
Learning Objectives
- Explain what a reverse proxy does and when you need one
- Set up Caddy or Traefik as a reverse proxy
- Obtain and automatically renew TLS certificates with Let's Encrypt
- Expose multiple services on a single public IP
Introduction
You have a service running on a port (e.g., Immich on port 3001). You want to access it from the internet (or from a phone on cellular) as https://photos.yourdomain.com. You want the connection encrypted. You want the certificate to renew itself. You want all of this with minimal ongoing work.
That's what a reverse proxy does. This chapter covers the setup.
Why a reverse proxy
Without a reverse proxy:
- Each service would need its own public IP (you don't have many).
- Each service would need its own certificate (each renewed separately).
- Each service would need to handle TLS termination (extra config, extra CPU).
With a reverse proxy:
- One public IP serves all services (the proxy listens on 80 and 443).
- One certificate (or one per service, managed by the proxy).
- The proxy handles TLS; the backend services serve plain HTTP.
The reverse proxy is the front door. Each service is a room in the house. The proxy decides which requests go to which room based on the hostname (photos.yourdomain.com → Immich, docs.yourdomain.com → Paperless, etc.).
Tools: Caddy, Traefik, Nginx
Three popular choices for home labs:
- Caddy: the easiest. Automatic HTTPS, automatic certificate renewal, simple config. The right default for most home labs.
- Traefik: more powerful, designed for container environments. Integrates with Docker labels. Slightly more complex.
- Nginx: the classic. The most popular web server in the world. More configuration, but extremely flexible.
The conversation's recommendation: Caddy for most home labs. The automatic HTTPS is a killer feature. The config is dead simple.
Setting up Caddy
Install Caddy in a container:
version: "3.9"
services:
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /mnt/tank/Lab/caddy/Caddyfile:/etc/caddy/Caddyfile
- /mnt/tank/Lab/caddy/data:/data
- /mnt/tank/Lab/caddy/config:/config
The Caddyfile is the config. Simple example:
photos.yourdomain.com {
reverse_proxy localhost:3001
}
docs.yourdomain.com {
reverse_proxy localhost:8000
}
That's it. Caddy reads the file, automatically obtains a Let's Encrypt certificate for each hostname, and routes requests to the right backend. Certificates renew automatically. No further config needed.
The DNS side
For the reverse proxy to work, the hostnames need to resolve to your public IP. The setup:
- Find your public IP. Most routers show it in the admin UI. Or visit
https://ifconfig.mefrom the NAS. - Configure DNS A records at your domain registrar (or in Cloudflare if you use it). For each hostname:
photos,docs, etc., point to your public IP. - Configure port forwarding on your router: external 80 and 443 → the NAS's local IP (192.168.1.10) on ports 80 and 443.
Once DNS resolves and ports are forwarded, the reverse proxy is reachable from the internet. Let's Encrypt verifies the hostname and issues the certificate.
Cloudflare proxy mode
If you use Cloudflare for DNS (and you probably should, for the DDoS protection and the free TLS), you can enable "Proxy mode" (the orange cloud icon in the Cloudflare dashboard). When proxied:
- Cloudflare acts as a reverse proxy in front of your reverse proxy. (Yes, a proxy in front of a proxy.)
- Cloudflare hides your real IP. The internet sees Cloudflare's IPs.
- Cloudflare handles TLS to the client; your reverse proxy handles TLS to Cloudflare.
For Caddy behind Cloudflare, use the cloudflare DNS challenge plugin for certificate issuance (instead of the default HTTP challenge, which Cloudflare's proxy can break). Or use Cloudflare's "Full" or "Full (Strict)" SSL mode and let Cloudflare handle the public-facing certificate.
The security tradeoffs
Exposing services to the internet is a security decision. The risks:
- Any service on the public internet is a target. Automated scanners find and attack exposed services within hours.
- A vulnerability in a service can be exploited from anywhere in the world.
- Misconfiguration (e.g., a service with default credentials) can be exploited.
The mitigations:
- Only expose what you need. Don't expose admin interfaces (Jellyfin's admin, Immich's admin, Vaultwarden's admin) to the public internet.
- Use Cloudflare Access (or similar) for any service that needs authentication. Cloudflare Access adds an auth layer in front of the service, with email-based or SSO-based login.
- Use strong passwords. Don't reuse passwords. Use a password manager.
- Keep services updated. The update is the fix for the vulnerability that the scanner just found.
- Monitor the logs. If you see suspicious activity, investigate.
When to use Tailscale instead
From Volume 1, Chapter 8: Tailscale is the recommended way to reach the NAS remotely. The conversation was explicit: prefer Tailscale over exposing services to the public internet for most use cases.
The exceptions where a reverse proxy makes sense:
- You want to share a service with someone who doesn't have Tailscale (e.g., a contractor, a non-technical family member).
- You want to use a mobile app that doesn't support Tailscale.
- You want to expose a service to a public audience (a personal website, a public API).
For everything else, Tailscale is the right answer. Tailscale services are not reachable from the public internet, which is a security feature, not a limitation.
The "what if I need both" answer
For services that need both Tailscale (for you) and public access (for sharing with others), the right pattern is:
- Run the service in a container with no public port mapping (only on the host's localhost or a private Docker network).
- Run the reverse proxy with both Tailscale and public access enabled.
- Configure the proxy to allow Tailscale IPs without authentication, but require authentication for public IPs (via Cloudflare Access).
You get the security of Tailscale for yourself and the convenience of public access for others, with strong authentication on the public path.
The Cloudflare Tunnel alternative
Cloudflare Tunnel is a different way to expose services. Instead of opening ports on your router, you run a Cloudflare Tunnel client (cloudflared) on the NAS. The client makes an outbound connection to Cloudflare's network. Cloudflare proxies incoming requests back through that outbound connection.
The benefit: no port forwarding, no exposed public IP, no incoming firewall rules. The traffic looks like an outbound HTTPS connection from your perspective.
The cost: you depend on Cloudflare for the public path. If Cloudflare has an outage, your services are unreachable.
For a home lab, Cloudflare Tunnel is a clean alternative to a traditional reverse proxy. It's especially useful when the ISP blocks inbound ports (CGNAT) or when you don't want to expose your public IP.
Engineering Note
Exposing a service is a decision, not a default. Every public port is a target. Every public service is in the scanner logs. The default should be: the service is not public. Reach it via Tailscale. If you need public access, do it with Cloudflare Tunnel or Cloudflare Access, with strong auth, with monitoring. The convenience of "just open a port" is not worth the risk in 2026.
Summary
A reverse proxy is the front door for multiple services on a single public IP. Caddy is the right default for home labs (automatic HTTPS, simple config). Set up DNS records, port forwarding, and the Caddyfile. Use Cloudflare for DNS and DDoS protection. Use Cloudflare Access or Cloudflare Tunnel for additional security. Prefer Tailscale for personal access; use the reverse proxy for sharing with others. The default is "not public." Public is a decision.
Checklist
- ⬜ Decide which services need to be public (probably few or none)
- ⬜ For public services, install Caddy (or Traefik) as the reverse proxy
- ⬜ Configure DNS A records for each public hostname
- ⬜ Configure port forwarding for 80 and 443 to the NAS
- ⬜ Use Cloudflare for DNS, DDoS protection, and Access (for auth)
- ⬜ Verify TLS certificates renew automatically (Caddy does this by default)
Looking Ahead
Chapter 07 is monitoring. Once you have services running, you want to know when they're broken. Uptime Kuma, Prometheus, Grafana. The chapter that turns "I think it's working" into "I know it's working, and I know when it stops."