Home · Volume 4 · Chapter 06

📖 Chapter 06 — Reverse Proxies and TLS

Expose services to the internet with HTTPS, the right way.

v0.1 · draft Vol 4 · Ch 06
~12 min

Learning Objectives

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:

With a reverse proxy:

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:

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:

  1. Find your public IP. Most routers show it in the admin UI. Or visit https://ifconfig.me from the NAS.
  2. 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.
  3. 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:

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:

The mitigations:

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:

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:

  1. Run the service in a container with no public port mapping (only on the host's localhost or a private Docker network).
  2. Run the reverse proxy with both Tailscale and public access enabled.
  3. 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

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."

Ch 06 · v0.1 · drafted from the original ChatGPT conversation, July 2026