Imagine having a single secure entrance to your home, with a guard who checks everyone coming in, directs them to the right room, and keeps your valuables safe. That’s exactly what Nginx Proxy Manager does for your home server.
Nginx Proxy Manager (NPM) is a user-friendly interface for Nginx that acts as a reverse proxy. It handles SSL certificates, access control, and routing—making it the perfect security gatekeeper for your home services.
This guide will walk you through everything from basic setup to advanced security configurations, ensuring your home server stays protected in 2026.
What is a Reverse Proxy?
Before diving into Nginx Proxy Manager, let’s understand what a reverse proxy does.
The Analogy
Without Reverse Proxy:
User → Internet → Your Server (port 80)
Your Server (port 32400) ← Direct access to each service
Your Server (port 8080)
Your Server (port 3000)
With Reverse Proxy:
User → Internet → Nginx Proxy Manager (ports 80, 443)
↓ Routes to:
├─ Service 1 (port 32400) ← Hidden
├─ Service 2 (port 8080) ← Hidden
├─ Service 3 (port 3000) ← Hidden
└─ Service 4 (port 8123) ← Hidden
All traffic goes through one secure entrance
Benefits of Using Nginx Proxy Manager
| Benefit | Description |
|---|---|
| Single Entry Point | Only expose ports 80 and 443 |
| SSL/TLS Encryption | Automatic Let’s Encrypt certificates |
| Access Control | Restrict who can access services |
| Load Balancing | Distribute traffic across servers |
| Logging | Track all access attempts |
| Easy Management | Web-based configuration |
Installing Nginx Proxy Manager
Method 1: Docker Installation (Recommended)
Step 1: Create Docker Compose File
# Create directory
mkdir nginx-proxy-manager
cd nginx-proxy-manager
# Create docker-compose.yml
nano docker-compose.yml
Add this configuration:
version: '3.8'
services:
nginx-proxy-manager:
image: 'jc21/nginx-proxy-manager:latest'
container_name: nginx-proxy-manager
restart: unless-stopped
ports:
- '80:80' # HTTP
- '443:443' # HTTPS
- '81:81' # Admin UI
environment:
# Initial admin credentials
- DB_MYSQL_HOST=db
- DB_MYSQL_PORT=3306
- DB_MYSQL_USER=npm
- DB_MYSQL_PASSWORD=your_db_password
- DB_MYSQL_NAME=npm
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
depends_on:
- db
networks:
- npm-network
db:
image: 'jc21/mariadb-aria:latest'
container_name: npm-db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=your_root_password
- MYSQL_DATABASE=npm
- MYSQL_USER=npm
- MYSQL_PASSWORD=your_db_password
volumes:
- ./mysql:/var/lib/mysql
networks:
- npm-network
networks:
npm-network:
driver: bridge
Step 2: Start Nginx Proxy Manager
# Start containers
docker-compose up -d
# Check status
docker-compose ps
# View logs
docker-compose logs -f
Method 2: Manual Installation (Non-Docker)
Step 1: Install Dependencies
# Install Node.js and NPM
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs gcc g++ make
# Install Nginx
sudo apt install nginx -y
# Install Certbot
sudo apt install certbot python3-certbot-nginx -y
Step 2: Install Nginx Proxy Manager
# Download and install
cd /opt
sudo git clone https://github.com/NginxProxyManager/nginx-proxy-manager.git
cd nginx-proxy-manager
# Install dependencies
sudo npm install
# Build
sudo npm run build
# Create systemd service
sudo nano /etc/systemd/system/npm.service
Systemd Service File:
[Unit]
Description=Nginx Proxy Manager
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/nginx-proxy-manager
ExecStart=/usr/bin/node /opt/nginx-proxy-manager/app.js
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
# Start service
sudo systemctl start npm
sudo systemctl enable npm
# Check status
sudo systemctl status npm
Initial Setup & Configuration
Step 1: Access the Admin Interface
# Open your browser and go to:
http://your-server-ip:81
# Default login:
Email: admin@example.com
Password: changeme
# You'll be prompted to change these immediately
Step 2: Change Default Admin Credentials
# 1. Log in with default credentials
# 2. Click on the user icon (top right)
# 3. Select "User Profile"
# 4. Change email and password
# 5. Click "Update"
# Use a strong password:
# - At least 12 characters
# - Mix of uppercase, lowercase, numbers, symbols
# - Avoid personal information
Step 3: Update Default Settings
# In the admin panel:
# 1. Settings → Default Site
# - Set SSL to "Force SSL" (always use HTTPS)
# - Set default location if needed
# 2. Settings → SSL Certificates
# - Configure Let's Encrypt
# - Set auto-renewal (default 90 days)
# 3. Settings → Advanced
# - Enable logging
# - Set log level to "info" (or "warn")
# - Enable access logs
Creating SSL Certificates
Using Let’s Encrypt (Recommended)
# In the NPM Admin Panel:
# 1. Go to "SSL Certificates" → "Add SSL Certificate"
# 2. Choose "Let's Encrypt"
# - Domain: your-domain.com
# - Email: your-email@domain.com
# - Check "Use a DNS Challenge" (optional)
# - Click "Save"
# 3. Wait for certificate generation
# - Usually takes 10-30 seconds
# 4. Verify certificate
# - Should show "Valid" status
# - Check expiration date
Using Custom Certificates
# If you already have certificates:
# 1. Go to "SSL Certificates" → "Add SSL Certificate"
# 2. Choose "Custom"
# - Certificate: paste your certificate
# - Private Key: paste your private key
# - Chain: paste the full chain (optional)
# 3. Click "Save"
# 4. Test the certificate
openssl s_client -connect your-domain.com:443
Wildcard Certificates
# For subdomains: *.your-domain.com
# DNS Challenge required:
# 1. Use Let's Encrypt with DNS challenge
# 2. Choose your DNS provider
# 3. Follow instructions to set TXT records
# 4. Wildcard certificate generated
Setting Up Proxy Hosts
Basic Proxy Host Configuration
# In NPM Admin Panel:
# 1. Go to "Proxy Hosts" → "Add Proxy Host"
# 2. Configure:
# Tab 1: Domain Details
# - Domain Names: service.your-domain.com
# - Forward Hostname: localhost (or internal IP)
# - Forward Port: 8080 (your service port)
# - Block Common Exploits: Yes (recommended)
# - Websockets Support: Yes (if needed)
# Tab 2: SSL
# - SSL Certificate: Select or create one
# - Force SSL: Yes
# - HTTP/2 Support: Yes
# - HSTS: Yes (recommended)
# - HSTS Subdomains: Yes
# Tab 3: Advanced
# - Custom Nginx Configuration (if needed)
# - Location: /
# 3. Click "Save"
Advanced Proxy Host Configuration
# Example: Plex Media Server
Domain: plex.your-domain.com
Forward Hostname: 192.168.1.100
Forward Port: 32400
SSL: Enabled
Force SSL: Yes
Websockets: Yes
Access List: Restricted (only you)
Custom Nginx Config:
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
# Example: Home Assistant
Domain: home.your-domain.com
Forward Hostname: localhost
Forward Port: 8123
SSL: Enabled
Force SSL: Yes
Websockets: Yes
Access List: Internal IPs Only
Security Configuration
1. Access Control Lists (ACLs)
# Create Access Lists:
# 1. Go to "Access Lists" → "Add Access List"
# 2. Create a list for each use case:
# Example: "Admin Only"
# - Name: admin-only
# - Satisfaction: Any
# - Add Entry:
# - Type: Allow
# - Value: 192.168.1.0/24 # Local network
# - Type: Allow
# - Value: 10.0.0.2 # Specific IP
# - Type: Deny
# - Value: 0.0.0.0/0 # Everyone else
# Example: "Restricted"
# - Name: restricted
# - Satisfaction: All (must match all conditions)
# - Add Entry:
# - Type: Allow
# - Value: 192.168.1.100 # Your device
# - Type: Allow
# - Value: 192.168.1.101 # Another trusted device
# Example: "Internal Only"
# - Name: internal-only
# - Type: Allow
# - Value: 192.168.1.0/24 # Local network only
# - Type: Deny
# - Value: 0.0.0.0/0 # All external IPs
2. HTTP Security Headers
# In Proxy Host → Advanced → Custom Nginx Configuration:
# Add security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self';" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
# Remove server version
server_tokens off;
# Prevent clickjacking
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
3. Rate Limiting
# In Nginx Configuration (Advanced):
# Rate limiting prevents brute-force attacks
# Example: Limit requests to 5 per minute per IP
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;
limit_req zone=login_limit burst=10 nodelay;
# Apply to specific locations
location /login {
limit_req zone=login_limit;
proxy_pass http://backend:8080;
}
# Global rate limiting (in advanced config)
limit_req_zone $binary_remote_addr zone=global_limit:10m rate=10r/s;
4. Block Malicious IPs
# Option 1: Using Access Lists
# Create "Block List" access list
# - Type: Deny
# - Value: 203.0.113.5 # Known malicious IP
# Apply to all proxy hosts
# Option 2: Using fail2ban with NPM
# Create fail2ban filter for NPM
sudo nano /etc/fail2ban/filter.d/nginx-proxy-manager.conf
[Definition]
failregex = ^.*client: <HOST>.*"POST /api/tokens.*HTTP/.*" 401
ignoreregex =
# Restart fail2ban
sudo systemctl restart fail2ban
5. Rate Limiting Against DDoS
# In Nginx Advanced Configuration:
# Limit connections per IP
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_conn addr 10;
# Limit download speed
limit_rate 100k;
# Apply globally
location / {
limit_conn addr;
limit_rate 100k;
proxy_pass http://backend;
}
6. Enable ModSecurity (WAF)
# Install ModSecurity (Docker version)
# Add to docker-compose.yml:
nginx-proxy-manager:
image: 'jc21/nginx-proxy-manager:latest'
# ... existing config ...
environment:
- MODSECURITY=1
- MODSECURITY_CONFIG=/etc/nginx/modsecurity.conf
# Create modsecurity configuration
# /nginx-proxy-manager/data/modsecurity/modsecurity.conf
Advanced Security Features
1. Two-Factor Authentication
Enable 2FA for Admin Access:
# In NPM Admin Panel:
# 1. Click user icon → "User Profile"
# 2. Enable "Two-Factor Authentication"
# 3. Scan QR code with authenticator app
# 4. Enter verification code
# 5. Click "Enable"
# Download backup codes (store them safely!)
2. IP Geolocation Blocking
# Block countries with high attack rates
# Install geoip module
sudo apt install nginx-module-geoip
# In Nginx configuration:
geoip_country /usr/share/GeoIP/GeoIP.dat;
map $geoip_country_code $blocked_country {
default 0;
CN 1; # China
RU 1; # Russia
KP 1; # North Korea
}
# Apply in location block:
location / {
if ($blocked_country) {
return 403;
}
proxy_pass http://backend;
}
3. SSH Access Through NPM
# Protect SSH access using NPM
# Create proxy host for SSH
Domain: ssh.your-domain.com
Forward Hostname: localhost
Forward Port: 22
Websockets: No
# SSH over HTTPS (WebSSH)
# This is advanced - consider using alternative like Cloudflare Tunnel instead
4. Web Application firewall (WAF)
# Using Cloudflare WAF with NPM
# 1. Set up Cloudflare DNS
# 2. Configure SSL (Full or Full Strict)
# 3. Enable WAF in Cloudflare
# 4. Set security level to "High"
# Alternative: Use ModSecurity (OWASP Core Rule Set)
# Install OWASP rules
git clone https://github.com/coreruleset/coreruleset /etc/nginx/modsec/crs
# Enable in modsecurity.conf:
Include /etc/nginx/modsec/crs/crs-setup.conf
Include /etc/nginx/modsec/crs/rules/*.conf
5. Regular Security Audits
# Create audit script
nano /usr/local/bin/npm-audit.sh
#!/bin/bash
# NPM Security Audit
echo "=== Nginx Proxy Manager Security Audit ==="
# 1. Check SSL certificates
echo "SSL Certificates:"
/usr/bin/certbot certificates
# 2. Check admin access logs
echo "Admin Access Logs:"
tail -n 50 /data/logs/nginx-proxy-manager.log | grep "Admin"
# 3. Check for failed logins
echo "Failed Login Attempts:"
grep "401" /data/logs/nginx-proxy-manager.log | tail -10
# 4. Check for blocked IPs
echo "Blocked IPs:"
grep "403" /data/logs/nginx-proxy-manager.log | tail -10
# 5. Check proxy hosts status
echo "Proxy Hosts Status:"
docker exec nginx-proxy-manager curl -s http://localhost:81/api/proxy-hosts
# 6. Check for security updates
echo "Check for updates:"
docker pull jc21/nginx-proxy-manager:latest --quiet
echo "Audit complete."
# Make executable
chmod +x /usr/local/bin/npm-audit.sh
# Schedule weekly audit
sudo crontab -e
# Add:
0 1 * * 0 /usr/local/bin/npm-audit.sh | mail -s "NPM Security Audit" your-email@domain.com
Security Best Practices
1. Docker Security Hardening
# In docker-compose.yml, add security options:
services:
nginx-proxy-manager:
# ... existing config ...
security_opt:
- no-new-privileges:true
- seccomp=unconfined # Or use custom profile
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
- CHOWN
read_only: true
tmpfs:
- /tmp
- /var/run
2. Network Isolation
# Create isolated network
docker network create --subnet=172.20.0.0/16 npm-network
# In docker-compose.yml:
networks:
npm-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
# Services only accessible through NPM:
# 1. Place internal services on npm-network
# 2. Don't expose their ports to host
# 3. Only NPM can reach them
# Example internal service:
jellyfin:
image: jellyfin/jellyfin
networks:
- npm-network
# No ports published
3. Resource Limits
# In docker-compose.yml:
services:
nginx-proxy-manager:
# ... existing config ...
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
4. Log Monitoring
# Set up log monitoring
nano /usr/local/bin/monitor-npm-logs.sh
#!/bin/bash
# Monitor NPM logs for security events
LOG_FILE="/path/to/data/logs/nginx-proxy-manager.log"
ALERT_EMAIL="your-email@domain.com"
# Check for suspicious patterns
tail -n 1000 "$LOG_FILE" | \
grep -E "Failed login|401|403|malicious|hack|brute" | \
while read line; do
echo "ALERT: $line" | mail -s "NPM Security Alert" "$ALERT_EMAIL"
done
# Schedule every 5 minutes
*/5 * * * * /usr/local/bin/monitor-npm-logs.sh
5. Database Security
# For Docker setup, secure the database:
# In docker-compose.yml:
db:
# ... existing config ...
security_opt:
- no-new-privileges:true
environment:
- MYSQL_ROOT_PASSWORD=strong_root_password
- MYSQL_DATABASE=npm
- MYSQL_USER=npm
- MYSQL_PASSWORD=strong_db_password
volumes:
- ./mysql:/var/lib/mysql
# Don't expose database ports
# Use internal network only
6. backup Configuration
# Create backup script
nano /usr/local/bin/backup-npm.sh
#!/bin/bash
# Backup NPM configuration
BACKUP_DIR="/backup/npm"
DATE=$(date +%Y%m%d_%H%M%S)
# Stop containers to ensure clean backup
docker-compose -f /path/to/docker-compose.yml stop
# Backup data
tar -czf "$BACKUP_DIR/npm-data-$DATE.tar.gz" /path/to/data/
# Backup MySQL
docker exec npm-db mysqldump -u root -p $MYSQL_ROOT_PASSWORD npm > \
"$BACKUP_DIR/npm-db-$DATE.sql"
# Restart containers
docker-compose -f /path/to/docker-compose.yml start
# Keep only last 7 days of backups
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -delete
find "$BACKUP_DIR" -name "*.sql" -mtime +7 -delete
# Schedule daily backup
sudo crontab -e
# Add:
0 2 * * * /usr/local/bin/backup-npm.sh
Troubleshooting Common Issues
Issue 1: SSL Certificate Not Working
# Check certificate details
openssl s_client -connect your-domain.com:443
# View NPM logs
docker-compose logs nginx-proxy-manager | grep SSL
# Manually renew Let's Encrypt
docker exec nginx-proxy-manager certbot renew --force-renewal
# Check DNS resolution
dig your-domain.com
Issue 2: Cannot Access Admin Panel
# Check port 81 is open
sudo netstat -tulpn | grep 81
# Check NPM service
docker ps | grep nginx-proxy-manager
# View logs
docker-compose logs -f
# Restart NPM
docker-compose restart nginx-proxy-manager
Issue 3: High Memory Usage
# Check memory usage
docker stats
# Adjust limits in docker-compose
deploy:
resources:
limits:
memory: 1G
# Check for memory leaks
docker logs nginx-proxy-manager --tail 100
Issue 4: Failed Login Attempts
# Check admin logs
docker exec nginx-proxy-manager tail -f /data/logs/nginx-proxy-manager.log | grep "Admin"
# Review failed logins
docker exec nginx-proxy-manager cat /data/logs/nginx-proxy-manager.log | grep "401"
# Create access list to block attackers
# Use the Access Lists feature in NPM UI
Quick Reference
Essential NPM Commands
# Docker commands
docker-compose up -d # Start NPM
docker-compose down # Stop NPM
docker-compose restart # Restart NPM
docker-compose logs -f # View logs
# NPM Admin Interface
http://your-server-ip:81 # Admin URL
# Default credentials
admin@example.com / changeme
# Backup commands
docker exec npm-db mysqldump --all-databases > backup.sql
tar -czf npm-backup.tar.gz data/ letsencrypt/
Security Checklist
- Change default admin password
- Enable 2FA for admin account
- Use strong SSL certificates (Let’s Encrypt)
- Force HTTPS for all services
- Create access control lists
- Enable security headers
- Set up rate limiting
- Monitor logs regularly
- Keep NPM updated
- Backup configuration
- Use separate network for services
- Limit resource usage
- Enable WAF if possible
Conclusion
Nginx Proxy Manager is an essential tool for securing your home server in 2026. It provides a single, secure entry point for all your services, handling SSL certificates, access control, and routing.
Key Takeaways:
- Always change default credentials immediately
- Use Let’s Encrypt for automatic SSL certificates
- Implement access control lists for sensitive services
- Enable security headers for protection
- Monitor logs for suspicious activity
- Regular updates and backups are crucial
- Use the principle of least privilege
Ready to secure your services? Explore our Complete Home Server Security Guide for more protection strategies.
Frequently Asked Questions (FAQs)
Q: Is Nginx Proxy Manager secure? A: Yes, when properly configured. It uses industry-standard Nginx with regular security updates.
Q: Can I use NPM with Cloudflare? A: Yes, enable “Full (Strict)” SSL in Cloudflare and use origin certificates in NPM.
Q: How do I update NPM?
A: For Docker: docker-compose pull && docker-compose up -d. For manual: git pull && npm install.
Q: What happens if Let’s Encrypt expires? A: NPM automatically renews certificates. Check logs if renewal fails.
Q: Can I use NPM on a Raspberry Pi? A: Yes, NPM works on ARM architecture like Raspberry Pi.
Q: Is NPM better than Caddy or Traefik? A: NPM is more user-friendly, while Caddy/Traefik offer more advanced features. Choose based on your needs.
Q: How many proxy hosts can NPM handle? A: Virtually unlimited, performance depends on your server hardware.
Q: Can I use NPM for non-web services? A: Yes, NPM can proxy TCP/UDP traffic using stream module.
Discussion
Loading comments...