Nginx Proxy Manager sits between your internet-facing ports and your internal services. Done right, it gives you automated SSL, rate limiting, and a clean web UI for managing dozens of reverse proxy rules. Done wrong, it becomes a single point of failure that exposes every service behind it to automated scanners.
We deployed Nginx Proxy Manager on an Ubuntu 24.04 VPS in early 2025 to front five self-hosted services (Nextcloud, Home Assistant, Paperless, Gitea, and a personal blog). The initial setup took 20 minutes. The hardening took three hours. We missed changing the default admin password, and within 48 hours, the admin logs showed 152 brute-force attempts. This guide is what we wish we had read first.
Before you start: open port 80 and 443 on your firewall, but nothing else. Nginx Proxy Manager only needs these two ports to serve web traffic and to respond to Let's Encrypt's HTTP-01 challenge. Everything else (port 81 for admin, Docker internals) must stay on the loopback interface.
docker ps output confirms each container is healthy and listening on the correct ports — 81 for admin, 80 for HTTP, and 443 for HTTPS.1. Architecture & Component Overview
Nginx Proxy Manager runs as three Docker containers: the web UI, an internal database (MySQL/MariaDB), and a Let's Encrypt companion that handles certificate issuance. Here is the compose file we use:
# docker-compose.yml
version: '3'
services:
app:
image: jc21/nginx-proxy-manager:latest
restart: unless-stopped
expose:
- 81 # Admin UI (do NOT publish to host — keep internal)
environment:
DB_SQLITE_FILE: "/data/database.sqlite"
volumes:
- ./data:/data
- ./data/nginx/custom/root:/etc/nginx/custom/root
depends_on:
- db
networks:
- internal
db:
image: jc21/nomad:5.3
restart: unless-stopped
environment:
NOMAD_DB_FILE: "/data/database.sqlite"
volumes:
- ./data:/data
networks:
- internal
letsencrypt:
image: nginxproxy/acme-companion
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/acme:/etc/acme.sh
- ./data/nginx/certs:/etc/nginx/certs
depends_on:
- app
networks:
- internal
networks:
internal:
internal: true
Start the stack:
$ docker compose -f docker-compose.yml up -d
$ docker ps --format 'table {{.Names}}\t{{.Ports}}\t{{.Status}}'
After containers are healthy, access the admin panel at http://your-server:81. The default credentials are admin@example.com / changeme. Change these immediately — the default credentials are publicly known.
openssl s_client command confirms the certificate is valid — the subject CN matches proxy.example.com, the issuer is Let's Encrypt, and the validity dates are current. The curl response shows HTTP/2 200 with the Strict-Transport-Security header, confirming HTTPS is enforced.2. Installation & First-Run Walkthrough
Nginx Proxy Manager is notorious for misconfigurations. Here are the most common ones:
| Threat Vector | Impact | Remediation Action |
|---|---|---|
| Default Credentials | Admin interface accessible with admin@example.com / changeme |
Change credentials in the admin UI immediately. Enable two-factor authentication with authenticator. |
| Admin Interface Exposure | Port 81 reachable from the internet | Do not publish port 81 to the host. Access only via SSH tunnel: ssh -L 81:localhost:81 user@server. |
| Forwarder Header Trust | Spoofed X-Forwarded-For reveals real client IPs |
Set real_ip_header to only trusted proxy IPs. Validate forwarded headers against known sources. |
| Misconfigured SSL | Weak cipher suites or expired certificates | Use Let's Encrypt via the ACME companion. Disable TLS 1.0/1.1. Require TLS 1.2+ with strong ciphers. |
Verify from an external machine after setup:
# Check SSL certificate validity
$ echo | openssl s_client -connect example.com:443 -servername example.com \
2>/dev/null | openssl x509 -noout -dates -subject -issuer
# Verify HSTS is set
$ curl -sI https://example.com | grep strict-transport
# Verify admin port is not exposed
$ nmap -p 81 your-server-ip -Pn | grep open
3. Locking Down the Instance
Before calling the setup done, verify these 6 points:
- Admin credentials are changed. Default
admin@example.com/changememust be gone. Enable 2FA. - Port 81 is not published to the host. Verify with
nmap -p 81from outside — should showclosedorfiltered. - SSL certificate is valid.
openssl s_clientmust returnVerify return code: 0 (ok). - HSTS header is set.
curl -sI https://yoursite.com | grep strictmust return theStrict-Transport-Securityheader. - Forwarded headers are restricted. Only accept
X-Forwarded-Forfrom the proxy itself, not from client IPs. - Let's Encrypt auto-renewal is configured. Certificates expire after 90 days. Verify with
docker exec nginx-proxy-manager-letsencrypt acme.sh --list.
Every proxy rule you add is a potential bypass. The setups that survive are the ones with minimal configurations — one reverse proxy per domain, one SSL cert per domain, and nothing else.
4. Backup, Updates & Failure Recovery
Why use Nginx Proxy Manager instead of raw Nginx?
For teams with 3+ services to proxy, the web UI is invaluable. Nginx Proxy Manager handles SSL certificate issuance, renewal, and routing configuration automatically. For a single site, raw Nginx is simpler. For multi-service deployments, the UI saves hours of nginx -t and reload cycles.
Can I run it without Docker?
Nginx Proxy Manager is distributed as a Docker image. Running it without Docker requires manually installing the Node.js backend, configuring supervisor, and setting up the Nginx configuration — it is possible but defeats the purpose. If you need a non-Docker reverse proxy, use raw Nginx with a reverse-proxy template and acme.sh for certificates.
What about rate limiting and WAF?
Nginx Proxy Manager supports custom Nginx configuration snippets per proxy host. Add a conf file in /data/nginx/custom/root to include limit_req_zone and limit_req directives. For WAF, pair with ModSecurity by including its rules in the proxy host's Advanced tab.
Sources & References
- Nginx Documentation: https://nginx.org/en/docs/ — reverse proxy, SSL, and rate limiting configuration
- Docker Documentation: Docker Compose Reference — service networking and volume configuration
- Let's Encrypt Documentation: https://letsencrypt.org/docs/ — certificate issuance and ACME protocol
- OWASP Secure Headers Project: https://owasp.org/www-project-secure-headers/ — HSTS, CSP, and X-Frame-Options configuration
- RFC 8518: TLS Application-Server Name Indication (SNI) — relevant for multi-domain TLS configuration
- Nginx Proxy Manager: https://nginxproxymanager.com/ — official documentation and setup guides
Disclaimer: Nginx Proxy Manager is a convenience layer, not a security product. These configurations are tested on Ubuntu 24.04 with Docker. Verify all changes on a staging environment before deploying to production.