Guide

HTTPS for home services: one reverse proxy, proper certificates

A reverse proxy sits between the internet and your apps. It holds the HTTPS certificates, answers on port 443, and passes each request to the right app by name. You forward one port instead of one per app – and on a .app name, you can’t skip HTTPS anyway.

Updated · Checked against Caddy 2, Nginx Proxy Manager 2.15, acme.sh 3.1 · 6 min read

Ports to forward
443 (plus 80 for HTTP‑01)
Easiest
Caddy with automatic HTTPS
Point‑and‑click
Nginx Proxy Manager
Fewest certificates
One wildcard via DNS‑01

Why a reverse proxy

  • One open port. Port 443 on your router goes to the proxy. Home Assistant, Jellyfin and Nextcloud stay on their own internal ports and are never exposed directly.
  • Names instead of port numbers. ha.yourname.uk.app, films.yourname.uk.app and cloud.yourname.uk.app can all use port 443; the proxy tells them apart by the name the browser asks for.
  • Certificates in one place. The proxy gets and renews them. The apps behind it don’t need to know about HTTPS at all.
  • Browsers insist on it. .app is on the HSTS preload list, so browsers refuse plain HTTP for any .app name and won’t let anyone click past a certificate error.

Before you start: DNS and ports

  1. Point home.yourname.uk.app at your public IP with a dynamic DNS updater (see the start guide).
  2. For each app, add a CNAME record in your uk.app DNS: ha → home.yourname.uk.app, and so on. Or add one wildcard: host *, type CNAME, value home.yourname.uk.app.
  3. Give the proxy machine a fixed LAN address and forward TCP 443 to it on your router. Forward TCP 80 too if you use the automatic (HTTP‑01) certificate method below. If you are not sure how, see the guide for your router.

Option A: Caddy, with automatic HTTPS

Caddy gets and renews certificates by itself as soon as a site block names a public host. It is the easiest choice if you are comfortable with a text file. Install it from the official instructions for your system (Debian and Ubuntu have an apt repository), then edit /etc/caddy/Caddyfile:

/etc/caddy/Caddyfile
ha.yourname.uk.app {
	reverse_proxy 192.168.1.20:8123
}

films.yourname.uk.app {
	reverse_proxy 192.168.1.30:8096
}

cloud.yourname.uk.app {
	reverse_proxy 192.168.1.40:11000
}
sudo systemctl reload caddy
journalctl -u caddy -f      # watch the certificates being issued

Caddy passes the visitor’s address to the app in the X-Forwarded-For header and handles WebSockets without extra settings, which is what Home Assistant and Jellyfin need. Each app still has to be told to trust the proxy – that step is in every app guide on this site.

Option B: Nginx Proxy Manager

Nginx Proxy Manager (NPM) is a web interface on top of Nginx, usually run in Docker. It is a good fit if you prefer forms to config files. It is also available as a Home Assistant app.

  1. Open NPM’s admin page (port 81 by default) and change the default login.
  2. Hosts → Proxy Hosts → Add Proxy Host. Enter the domain name (ha.yourname.uk.app), scheme http, the app’s LAN address and port. Tick Websockets Support and Block Common Exploits.
  3. On the SSL tab, choose Request a new Certificate, tick Force SSL and HTTP/2 Support, and save. NPM asks Let’s Encrypt using the HTTP‑01 method, so port 80 must be forwarded to NPM too.

Watch out

Don’t forward port 81 (the NPM admin page) from your router. Reach it on your LAN or over a VPN.

Option C: one wildcard certificate via DNS‑01

Instead of a certificate per name, you can get one certificate for yourname.uk.app and *.yourname.uk.app by proving control of the DNS rather than of a web server. This has three advantages: port 80 can stay closed, new app names work immediately, and it uses fewer certificates – which matters, because certificate authorities apply rate limits per registered domain, and uk.app names share that allowance until uk.app is listed on the Public Suffix List.

uk.app provides a small hook for acme.sh (and an adapter for lego). It needs an API token with the read and write:records scopes from my.uk.app/tokens – a DDNS token won’t do.

1. Install acme.sh and the uk.app hook
# as root on the proxy machine (sudo -i)
apt install unzip python3 curl
curl https://get.acme.sh | sh -s email=you@example.com
curl -fsSLO https://uk.app/downloads/ukapp-tools.zip && unzip ukapp-tools.zip
install -m 755 ukapp.py /usr/local/bin/ukapp
cp dns_ukapp.sh ~/.acme.sh/dnsapi/

install -m 600 /dev/null /root/.ukapp-api-token
nano /root/.ukapp-api-token          # paste the API token, save
2. Issue the certificate (takes about five minutes)
export UKAPP_TOKEN="$(cat /root/.ukapp-api-token)"
~/.acme.sh/acme.sh --issue --server letsencrypt --dns dns_ukapp --dnssleep 300 \
  -d yourname.uk.app -d '*.yourname.uk.app'

The wait (--dnssleep 300) gives resolvers time to see the temporary TXT record. Try --server letsencrypt_test first if you are experimenting, so failed attempts don’t count against the real limits.

3. Install it where your proxy expects it
mkdir -p /etc/caddy/certs
~/.acme.sh/acme.sh --install-cert -d yourname.uk.app --ecc \
  --fullchain-file /etc/caddy/certs/fullchain.pem \
  --key-file /etc/caddy/certs/key.pem \
  --reloadcmd "chown -R caddy:caddy /etc/caddy/certs && systemctl reload caddy"

In the Caddyfile, tell each site to use those files instead of fetching its own:

/etc/caddy/Caddyfile
(wildcard) {
	tls /etc/caddy/certs/fullchain.pem /etc/caddy/certs/key.pem
}

ha.yourname.uk.app {
	import wildcard
	reverse_proxy 192.168.1.20:8123
}

In Nginx Proxy Manager, upload the two files under SSL Certificates → Add SSL Certificate → Custom instead. For plain Nginx, use them as ssl_certificate and ssl_certificate_key.

Make renewals work

acme.sh adds a daily cron job, but that job won’t have UKAPP_TOKEN in its environment. Replace it with a small wrapper:

cat > /usr/local/sbin/acme-renew <<'EOF'
#!/bin/sh
UKAPP_TOKEN="$(cat /root/.ukapp-api-token)"
export UKAPP_TOKEN
exec /root/.acme.sh/acme.sh --cron --home /root/.acme.sh
EOF
chmod 700 /usr/local/sbin/acme-renew
~/.acme.sh/acme.sh --uninstall-cronjob
echo '17 3 * * * root /usr/local/sbin/acme-renew >/var/log/acme-renew.log 2>&1' > /etc/cron.d/acme-renew

Note

The hook keeps a note of the TXT records it created under ~/.cache/ukapp-acme so it can remove exactly those afterwards. Leave that folder in place. Full details are in uk.app’s ACME DNS‑01 guide.

Which option should you pick?

Caddy (automatic)Nginx Proxy ManagerWildcard via DNS‑01
Ports forwarded80 and 44380 and 443443 only
Set‑upOne text fileWeb forms, DockerCommand line, API token
CertificatesOne per nameOne per name (or custom)One for everything
Works for names only reachable on a VPNNoNoYes

The last row matters for the Proxmox and Pi‑hole guides: a service you only reach over WireGuard can still have a trusted certificate if you use DNS‑01.

Tell each app it is behind a proxy

Most apps need to know the proxy’s LAN address before they trust the forwarded headers. Without this you get odd errors: Home Assistant answers 400 Bad Request, Nextcloud shows “untrusted domain”, Jellyfin thinks every visitor is on your LAN.

  • Home Assistant: Settings → System → Network → HTTP server, turn on Trust X‑Forwarded‑For and add the proxy to Trusted proxies. Details.
  • Jellyfin: Dashboard → Networking → Known proxies. Details.
  • Nextcloud: trusted_proxies, trusted_domains and overwriteprotocol in config.php. Details.

Check it

  1. Does the name point home? Look up ha.yourname.uk.app with the DNS lookup and compare the A record with the address on ip.uk.app. After a change, the propagation checker shows which resolvers still have the old one.
  2. Is the port open from the internet? Run the open port checker against port 443, port 80. “Open” means your router forwards it and something answers. “Closed” or “timed out” means the forward, the device’s own firewall or CGNAT is in the way.
  3. Is the certificate right? The SSL checker shows whether the certificate covers the exact name, who issued it and when it expires.
  4. Test from outside for real. Turn Wi‑Fi off on your phone and open the address over mobile data. Testing from inside your own network can give misleading results.

Tip

Run the SSL checker on two different names. With a wildcard, both should show the same certificate covering *.yourname.uk.app.

A name for your next idea

Give your website, home server or next project a memorable address: yourname.uk.app. Choose your name and check availability before registering.

Find your name

Questions people ask

Do I need port 80 open?

Only for the HTTP‑01 method that Caddy and Nginx Proxy Manager use by default. With a DNS‑01 wildcard certificate you can close it. Caddy can also use the TLS‑ALPN method on port 443 alone, but opening 80 makes troubleshooting easier.

Why not let each app do its own HTTPS?

You would need to forward a port per app, renew several certificates in different ways, and some apps (Home Assistant OS, Plex) make custom certificates awkward. One proxy is simpler to keep right.

What does “too many certificates already issued” mean?

The certificate authority’s weekly limit for the registered domain has been reached. Wait and try again later, use a single wildcard instead of many individual certificates, or use another ACME authority such as ZeroSSL, which acme.sh and Caddy both support.

Can the proxy run on the same machine as the app?

Yes. Point it at 127.0.0.1 and the app’s port, and add 127.0.0.1 (or the container network) as the trusted proxy in the app.

Sources and further reading

We check each guide against the vendor’s own documentation and support forums. If something has changed on your firmware, tell us.

Partners