Nginx Reverse Proxy: Securing Your Homelab with SSL
· 4 min read
Running multiple services on your homelab? A reverse proxy is essential for clean URLs, SSL certificates, and proper security. Here's how to set up Nginx as your gateway to everything.
What Does a Reverse Proxy Do?
Instead of accessing services by port numbers:
http://192.168.1.100:8096(Jellyfin)http://192.168.1.100:8989(Sonarr)http://192.168.1.100:7878(Radarr)
You get clean URLs with SSL:
https://jellyfin.home.yourdomain.comhttps://sonarr.home.yourdomain.comhttps://radarr.home.yourdomain.com
Benefits:
- SSL everywhere - Encrypt all traffic
- Single entry point - One port to expose (443)
- Subdomain routing - Clean URLs for each service
- Load balancing - Distribute traffic (for HA setups)
- Caching - Improve performance for static content
Network Architecture
Installing Nginx on Ubuntu
# Install Nginx
sudo apt update
sudo apt install -y nginx
# Enable and start
sudo systemctl enable nginx
sudo systemctl start nginx
# Verify
sudo nginx -t
Basic Configuration Structure
Nginx configuration files:
/etc/nginx/
nginx.confMain config
sites-availableAvailable site configs
default
jellyfin.conf
sites-enabledSymlinks to active sites
jellyfin.conf→ ../sites-available/jellyfin.conf
conf.dAdditional configs
snippetsReusable config snippets
SSL with Certbot (Let's Encrypt)
First, install Certbot:
sudo apt install -y certbot python3-certbot-nginx
Get certificates for your domains:
sudo certbot --nginx -d jellyfin.yourdomain.com
Certbot will:
- Validate domain ownership
- Generate certificates
- Configure Nginx automatically
- Set up auto-renewal
Reverse Proxy Configuration
Here's a template for any service:
/etc/nginx/sites-available/service.conf
server {
listen 80;
server_name service.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name service.yourdomain.com;
# SSL certificates (managed by Certbot)
ssl_certificate /etc/letsencrypt/live/service.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/service.yourdomain.com/privkey.pem;
# SSL settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# 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;
location / {
proxy_pass http://127.0.0.1:8096; # Internal service port
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Enable the site:
sudo ln -s /etc/nginx/sites-available/service.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Jellyfin Specific Configuration
Jellyfin needs WebSocket support for live transcoding:
/etc/nginx/sites-available/jellyfin.conf
server {
listen 443 ssl http2;
server_name jellyfin.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/jellyfin.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/jellyfin.yourdomain.com/privkey.pem;
# Large file uploads for libraries
client_max_body_size 20M;
location / {
proxy_pass http://127.0.0.1:8096;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /socket {
proxy_pass http://127.0.0.1:8096;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Cloudflare Integration
If using Cloudflare for DNS:
- Set SSL mode to "Full (Strict)"
- Add Cloudflare real IP restoration:
# /etc/nginx/conf.d/cloudflare.conf
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
# ... add all Cloudflare IP ranges
real_ip_header CF-Connecting-IP;
Troubleshooting
502 Bad Gateway
- Check if backend service is running
- Verify
proxy_passURL is correct - Check firewall rules
SSL Certificate Issues
sudo certbot renew --dry-run
View Access Logs
sudo tail -f /var/log/nginx/access.log
Learn More
Running a different reverse proxy? Share your setup on Discord!
