How-To: Secure Nginx Proxy Manager Admin UI with Your Own SSL Certificates
This article walks you through using Nginx Proxy Manager (NPM) to secure its own administrative interface (port 81) with a custom SSL certificate, instead of using Let’s Encrypt.
1. Prerequisites
- Nginx Proxy Manager deployed via Docker Compose.
- A valid
.crt
(certificate) and.key
(private key) for your FQDN, e.g.,innerweb.linux.matthewdavidson.us
. - Your domain’s DNS A record pointing to the server running NPM.
- Docker and Docker Compose installed on your host (tested with Docker CLI v2+).
Directory layout assumes:
~/nginx-proxy-manager/ ├── docker-compose.yml ├── data/ │ └── custom_ssl/ │ ├── innerweb.linux.matthewdavidson.us.crt │ └── innerweb.linux.matthewdavidson.us.key └── letsencrypt/ # used by NPM for LE storage (unused here)
2. Prepare Docker Compose
- Open your
docker-compose.yml
under~/nginx-proxy-manager/
. - Use host networking so the container binds directly to the host’s ports:
version: '3'
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
network_mode: host
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
- Save and recreate the container:
cd ~/nginx-proxy-manager
docker compose up -d --force-recreate app
Why:
network_mode: host
allows NPM to listen on host ports 80, 81, and 443 directly, simplifying proxying.
3. Import Your Custom Certificate
- Log into the NPM UI at
http://<server-IP>:81
. - Navigate to SSL Certificates → Add SSL Certificate → Custom.
- Fill in:
- Name:
innerweb.linux.matthewdavidson.us
- Domain Names:
innerweb.linux.matthewdavidson.us
- Certificate: either paste the contents of
innerweb.linux.matthewdavidson.us.crt
or click Select and choose/data/custom_ssl/innerweb.linux.matthewdavidson.us.crt
. - Key: paste or select
/data/custom_ssl/innerweb.linux.matthewdavidson.us.key
.
- Click Save.
Your certificate is now stored in NPM under
/data/ssl/
.
4. Create a Self‑Proxy Host for the Admin UI
- In the NPM UI go to Hosts → Proxy Hosts → Add Proxy Host.
- On the Details tab:
- Domain Names:
innerweb.linux.matthewdavidson.us
- Scheme:
http
- Forward Hostname / IP:
app
(This matches the service name in Docker Compose.) - Forward Port:
81
- Check Block Common Exploits if desired.
- Switch to the SSL tab:
- ✔ Enable SSL
- ⚪ Custom SSL → select your certificate “innerweb.linux.matthewdavidson.us”
- ✔ Force SSL
- ✔ HTTP/2
- ✔ HSTS (optional)
- Leave any Let’s Encrypt options unchecked.
- On the Advanced tab (or Advanced → Custom Nginx Configuration), add:
# Rewrite internal admin UI redirects back to external FQDN
proxy_redirect http://192.168.0.244:81/ https://innerweb.linux.matthewdavidson.us/;
- Click Save.
Tip: Adjust the internal IP
192.168.0.244
if your host’s IP differs.
5. Finalize and Test
- Ensure your DNS A record for
innerweb.linux.matthewdavidson.us
points to your server’s public or LAN IP. - In your browser, visit:
- HTTP →
http://innerweb.linux.matthewdavidson.us
(should redirect to HTTPS) - HTTPS →
https://innerweb.linux.matthewdavidson.us
- You should see the NPM login page with your custom SSL certificate properly presented.
6. Troubleshooting
- 502 Bad Gateway:
- Verify you used
expose: '81'
in Compose, notports:
for 81. - Confirm Forward Hostname is
app
(matching the Docker service name). - Tail NPM logs:
docker compose logs -f app
or check/data/logs/proxy_host-<ID>.error.log
. - Certificate Mismatch / Redirects:
- Ensure your Advanced
proxy_redirect
line matches the internal URL your UI listens on. - Double‑check that Force SSL is enabled under SSL settings.
Conclusion
You have now secured the Nginx Proxy Manager administrative interface behind your own SSL certificate. All traffic flows through port 443 on your FQDN, and the internal admin port remains hidden from direct access.
0 Comments