Skip to main content
Back to blog

What is a reverse proxy and why do you need one?

·3 min readNetworking

When you start self-hosting multiple services, you quickly hit a problem: you have one public IP address but multiple services that all want to be accessible on port 443 (HTTPS). A reverse proxy solves this by sitting in front of all your services and routing requests to the right one based on the domain name.

How it works

Without a reverse proxy, you would access services by port number: your-ip:8080 for Nextcloud, your-ip:3000 for Gitea, your-ip:9090 for monitoring. This is ugly, hard to remember, and does not support HTTPS easily.

A reverse proxy takes incoming requests on port 80/443, looks at the domain name in the request (the Host header), and forwards the request to the correct internal service:

nextcloud.example.com  ->  reverse proxy  ->  localhost:8080 (Nextcloud)
gitea.example.com      ->  reverse proxy  ->  localhost:3000 (Gitea)
monitor.example.com    ->  reverse proxy  ->  localhost:9090 (Grafana)
graph LR
    A["nextcloud.example.com"] --> RP["Reverse Proxy<br/>:443"]
    B["gitea.example.com"] --> RP
    C["monitor.example.com"] --> RP

    RP --> NC["Nextcloud<br/>:8080"]
    RP --> GT["Gitea<br/>:3000"]
    RP --> GF["Grafana<br/>:9090"]

Each service runs on its own internal port but is accessible through a clean subdomain with HTTPS.

What a reverse proxy handles

SSL/TLS termination. The reverse proxy handles HTTPS certificates for all your services. Individual services do not need to know about SSL at all. They just serve HTTP on their internal ports.

Domain routing. Route different domains or subdomains to different backend services. One public IP, unlimited services.

Security headers. Add security headers (X-Frame-Options, Content-Security-Policy, etc.) in one place instead of configuring each service individually.

Load balancing. If you run multiple instances of a service, the reverse proxy can distribute traffic between them. Less relevant for homelab use but important for production deployments.

Common options

Caddy is what I use now. Automatic HTTPS with zero configuration, simple config file format, and great for self-hosting. I wrote a separate post about it.

Nginx is the most widely used reverse proxy. Extremely flexible but the configuration syntax is verbose. I used it for a couple years before switching to Caddy.

Traefik integrates with Docker and automatically discovers services. Good if you want zero-touch configuration as you add containers, but the learning curve is steeper than Caddy.

Nginx Proxy Manager is Nginx with a web UI for people who do not want to edit config files. It works but adds another layer of abstraction.

A minimal Caddy example

nextcloud.example.com {
    reverse_proxy localhost:8080
}

gitea.example.com {
    reverse_proxy localhost:3000
}

That is the entire config. Caddy handles HTTPS certificates automatically via Let's Encrypt.

DNS setup

For a reverse proxy to work, you need DNS records pointing your domains to your server's IP address:

nextcloud.example.com  ->  A record  ->  your-public-ip
gitea.example.com      ->  A record  ->  your-public-ip

If you are self-hosting at home, you also need to forward ports 80 and 443 on your router to the machine running the reverse proxy.

For local-only services (not exposed to the internet), you can use DNS rewrites in AdGuard Home or your router's DNS settings to point domains to internal IPs.

When you need one

As soon as you are running more than one web service, a reverse proxy makes your life easier. Even with a single service, the automatic HTTPS alone is worth it. I set up a reverse proxy early in my self-hosting journey and it has been one of the most useful pieces of infrastructure I run.

Sources

Enjoying the blog? Subscribe via RSS to get new posts in your reader.

Subscribe via RSS