Every guide on this site points here for the HTTPS step. Once NPM is running, adding secure HTTPS to a new app takes about 60 seconds: fill in a domain, a forwarding address, and click “Request Certificate.” That’s it. No certbot commands, no cron jobs, no manual Nginx config reloads.
What a reverse proxy does (and why you want one)
Without a reverse proxy, each self-hosted app listens on its own port: Nextcloud on 8080, Immich on 2283, Vaultwarden on 8181. You’d have to share ugly URLs like http://your-ip:8080 and maintain a separate TLS certificate per app — or skip HTTPS entirely and run everything insecurely.
A reverse proxy sits at the front door of your server. It listens on the standard web ports (80 for HTTP, 443 for HTTPS) and decides which app gets each request based on the domain name. You can run a dozen apps behind a single IP address, each on its own subdomain, and the proxy handles TLS termination for all of them from one place.
Nginx Proxy Manager wraps this in a GUI so you don’t need to write a single line of Nginx config. It also auto-renews Let’s Encrypt certificates before they expire, so you never wake up to a broken padlock.
Prerequisites
- A VPS with Docker installed — see Best VPS for Self-Hosting if you’re still picking one.
- A domain or subdomain pointed at your server’s IP — the full walkthrough is at Point a Domain to a VPS.
- Ports 80 and 443 open in your firewall or cloud security group (port 81 for the admin UI, though you can close that after setup or restrict it by IP).
Let’s Encrypt requires an HTTP challenge on port 80 to verify you own the domain. If port 80 is blocked, certificate requests will fail.
Step 1 — Compose file
Create a directory for NPM and add a docker-compose.yml:
services:
npm:
image: jc21/nginx-proxy-manager:latest
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "81:81"
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
Then start it:
docker compose up -d
The two volumes (./data and ./letsencrypt) store your proxy config and certificates on the host so they survive container restarts and upgrades.
Step 2 — First login
Open http://your-server-ip:81 in a browser. Log in with the default credentials:
- Email:
[email protected] - Password:
changeme
NPM will immediately prompt you to set a real email and password. Do it now — the default credentials are public knowledge, so anyone who can reach port 81 can take over your proxy if you skip this step.
Step 3 — Add a proxy host with HTTPS
This is the step you’ll repeat for every app.
- In the dashboard, go to Hosts then Proxy Hosts, then click Add Proxy Host.
- Enter the Domain Name — for example,
cloud.yourdomain.com. - Set Forward Hostname / IP to the container name (if it’s on the same Docker network) or the host IP, and Forward Port to the app’s internal port.
- Click the SSL tab, choose Request a new SSL Certificate, check Force SSL, and optionally HTTP/2 Support.
- Agree to the Let’s Encrypt Terms of Service and click Save.
NPM will verify domain ownership via an HTTP challenge on port 80, issue the certificate, and reload Nginx — usually in under 30 seconds. Your app is now live at https://cloud.yourdomain.com.
Step 4 — WebSocket support and large uploads
Two common gotchas when adding real apps:
WebSocket support. Apps like n8n, Home Assistant, and many chat tools use WebSockets for live updates. In the proxy host editor, toggle on Websockets Support on the Details tab. Without it, those real-time connections will silently fail.
Large file uploads. Nginx’s default upload limit is 1 MB, which will break Nextcloud, Immich, and anything else that handles file uploads. In the proxy host editor, click the Advanced tab and add:
client_max_body_size 0;
Setting it to 0 removes the limit entirely. You can also set a specific cap like client_max_body_size 10g; if you prefer.
Troubleshooting
Certificate request fails. The most common cause is that DNS hasn’t propagated yet — wait a few minutes and try again. The second most common cause is that port 80 is blocked on your firewall or by your cloud provider’s security group rules. Let’s Encrypt must be able to reach your server on port 80 to complete the HTTP challenge.
502 Bad Gateway. NPM can’t reach the upstream app. Check that the container name or IP is correct, the port matches the app’s actual listening port, and that both containers share the same Docker network. If NPM and the app are in separate Compose stacks, create a shared external network and add both containers to it.
“Too many redirects” loop. This happens when the app itself is also set to redirect HTTP to HTTPS, creating a loop with NPM doing the same. In the proxy host settings, set the forward scheme to https and forward directly to the app’s HTTPS port, or disable the app’s own redirect if it’s behind NPM.
Admin UI locked out. If you’ve lost access to the NPM admin panel, you can reset the database by stopping the container, removing ./data/database.sqlite, and restarting. This wipes all proxy host configuration, so back up that file regularly if you have many hosts configured.
Use it everywhere
Nginx Proxy Manager is the shared HTTPS layer for every app guide on this site. Once it’s running, connecting a new app is a two-minute task. Here’s where you’ll use it next:
- Self-Host Nextcloud — file sync and collaboration
- Self-Host Immich — photo library with AI search
- Self-Host Vaultwarden — password manager
- Self-Host n8n — workflow automation
All of these run comfortably on a small Hetzner VPS alongside NPM itself. See Best VPS for Self-Hosting for a full comparison of what to run them on.