Illustration for Nginx Proxy Manager and Let’s Encrypt: HTTPS for every self-hosted app

Nginx Proxy Manager and Let’s Encrypt: HTTPS for every self-hosted app

Written by

in

,

Every service I expose to the internet, including this blog, my photos and my password manager, sits behind Nginx Proxy Manager. It gives each one its own web address and a free, automatically renewed HTTPS certificate, all from a simple web interface.

What a reverse proxy does

At home you have one public IP address, but lots of services, each on its own port. A reverse proxy sits in front of them all. Visitors connect to it on the standard web ports (80 and 443), and it looks at the domain name they asked for, photos.example.com or vault.example.com, and forwards the request to the right app on your network. It also handles the HTTPS certificates, so the apps themselves don’t have to.

Diagram: visitors on the internet connect over HTTPS to the home router, which forwards ports 80 and 443 to Nginx Proxy Manager. NPM handles SSL with Let's Encrypt, picks the app by domain name, and forwards to Immich, Vaultwarden, Nextcloud or WordPress over plain HTTP on the home network.
One public IP, many apps, each with its own HTTPS certificate.

Why Nginx Proxy Manager

Nginx is a superb proxy, but its config files can be fiddly. Nginx Proxy Manager (NPM) wraps it in a web interface: add a proxy host, tick a few boxes, and request a Let’s Encrypt certificate with one click. I run mine on a separate small machine from my main server.

Setting it up

Here’s a basic compose file:

services:
  npm:
    image: jc21/nginx-proxy-manager:latest
    restart: unless-stopped
    ports:
      - "80:80"     # HTTP
      - "443:443"   # HTTPS
      - "81:81"     # admin interface (keep this on your LAN)
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt

Do this first Start it with docker compose up -d, browse to http://<server-ip>:81, and log in. Follow the setup prompts and change the default admin details straight away.

Then, outside NPM:

  1. Forward ports 80 and 443 on your router to the NPM machine. Never forward port 81.
  2. Point your domain at home. Create a DNS A record for each subdomain, or a wildcard, pointing at your public IP.

Adding a service

  1. Hosts → Proxy Hosts → Add Proxy Host.
  2. Domain name: e.g. photos.example.com.
  3. Forward to: the app’s local IP and port, usually over plain http.
  4. Turn on Block Common Exploits, and Websockets Support for apps that need live updates, such as Immich, Vaultwarden and Homarr.
  5. On the SSL tab, choose Request a new certificate, and turn on Force SSL and HTTP/2.

Save, and within a few seconds the service is live on HTTPS. NPM renews the certificates automatically before they expire.

Features I use

  • Access lists. I have a lan-only list that only allows local addresses. Attaching it to a proxy host gives me a nice name on my home network, while the service stays invisible from the internet. It’s great for dashboards like Homarr.
  • Redirection hosts. My bare domain permanently redirects (301) to the www version of this site.
  • Custom upload limits. Under a host’s Advanced tab, client_max_body_size raises the upload size. You need this for big video uploads to Immich or Nextcloud.

Security tips

  • Only expose what you need. If a service is just for you, keep it LAN-only, or use a VPN.
  • Keep the admin port (81) off the internet.
  • Be careful with HSTS. It tells browsers to insist on HTTPS for a long time, which is hard to undo if you make a mistake. Turn it on only once everything works.
  • Keep NPM updated. It’s the front door to everything behind it.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *