nginx -t && systemctl reload nginx

Reverse Proxy Config Builder

Point a domain at a service running on a port. Pick nginx or Caddy and get a config with the headers that self-hosted apps actually need.

host:port of the service.
Nextcloud and media apps need this raised.

The headers self-hosted apps depend on

Behind a proxy an application sees the proxy as its client, which breaks anything that logs IPs, rate limits, or builds absolute URLs. X-Real-IP and X-Forwarded-For carry the original address; X-Forwarded-Proto tells the app it was reached over HTTPS so it stops generating http:// links and redirect loops. Omit that last one and Nextcloud, Jellyfin, and most others will misbehave in ways that look like application bugs. WebSocket apps additionally need the Upgrade and Connection headers or live updates silently fail.

nginx and Caddy reverse proxy config for self-hosted services

This reverse proxy generator writes a working server block for putting a domain in front of a service on a local port. It handles the TLS listener and HTTP redirect, the forwarded headers self-hosted applications need, WebSocket upgrade for live updates, client_max_body_size for large uploads, and buffering options for streaming endpoints. Caddy output is included for anyone who would rather not manage certificates by hand.

Reverse proxy FAQ

nginx or Caddy for a home server?

Caddy obtains and renews TLS certificates automatically with no extra tooling, and its config is much shorter, which makes it the easier choice for a homelab. nginx has broader documentation, more third-party examples, and is what you will meet professionally. Neither is wrong.

Why does my app redirect endlessly or generate http:// links?

The application does not know it was reached over HTTPS, so it builds URLs with the wrong scheme. Send X-Forwarded-Proto $scheme, and check the application also has its own setting for trusting proxy headers. Many require both.

My uploads fail at a certain size. Where is the limit?

nginx defaults client_max_body_size to 1MB and returns 413 above it. Raise it in the server block, and remember the application usually has its own limit too, plus PHP's upload_max_filesize and post_max_size if it is a PHP app.

Do I need to open the upstream port in my firewall?

No, and you should not. Bind the service to 127.0.0.1 so only the proxy can reach it, and open only 80 and 443. If the service listens on 0.0.0.0 people can bypass your proxy, and with it your TLS and any authentication you added there.