NGINX
NGINX is a high-performance web server and reverse proxy that serves as the front door to your self-hosted services. It handles incoming requests and routes them to the appropriate backend services.
Why Use NGINX?
Single Entry Point: One IP/port serves multiple services through different domains or paths.
SSL/TLS Termination: Handle HTTPS encryption in one place instead of configuring it for each service.
Load Balancing: Distribute traffic across multiple backend instances.
Caching: Improve performance by caching static content.
Security: Add authentication, rate limiting, and access controls.
Core Concepts
Reverse Proxy
A reverse proxy sits between clients and your backend services:
Client → NGINX → Backend Service
NGINX receives requests and forwards them to the appropriate service based on the domain name or URL path.
Server Blocks
Server blocks (similar to Apache's virtual hosts) define how NGINX handles requests for different domains.
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:8080;
}
}
Basic Configuration
Installation
# Ubuntu/Debian
sudo apt update
sudo apt install nginx
# Start and enable
sudo systemctl start nginx
sudo systemctl enable nginx
Directory Structure
/etc/nginx/
├── nginx.conf # Main configuration
├── sites-available/ # Available site configs
├── sites-enabled/ # Active site configs (symlinks)
└── conf.d/ # Additional configs
Simple Reverse Proxy
Create /etc/nginx/sites-available/myservice:
server {
listen 80;
server_name myservice.local;
location / {
proxy_pass http://localhost:8080;
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;
}
}
Enable the site:
sudo ln -s /etc/nginx/sites-available/myservice /etc/nginx/sites-enabled/
sudo nginx -t # Test configuration
sudo systemctl reload nginx
Common Patterns
WebSocket Support
Required for services like Home Assistant:
location / {
proxy_pass http://localhost:8123;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
Large File Uploads
For services like Immich (photo uploads):
server {
client_max_body_size 50000M; # 50GB max upload
location / {
proxy_pass http://localhost:2283;
}
}
Increased Timeouts
For long-running requests (LLM responses):
location / {
proxy_pass http://localhost:3000;
proxy_read_timeout 600s;
proxy_connect_timeout 150s;
}
Path-Based Routing
Serve multiple services from one domain:
server {
listen 80;
server_name myserver.local;
location /jellyfin/ {
proxy_pass http://localhost:8096/;
}
location /homeassistant/ {
proxy_pass http://localhost:8123/;
}
}
Static File Serving
Serve files directly without proxying:
server {
listen 80;
server_name files.local;
root /var/www/files;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
SSL/TLS Configuration
Using Let's Encrypt
# Install Certbot
sudo apt install certbot python3-certbot-nginx
# Obtain certificate
sudo certbot --nginx -d example.com
# Auto-renewal is configured automatically
Manual SSL Configuration
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
# Modern SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://localhost:8080;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
Security Headers
Add security headers to all responses:
server {
# ... other config ...
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;
# HSTS (only after testing HTTPS works)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}
Authentication
Basic Authentication
# Create password file
sudo apt install apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd username
server {
location / {
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://localhost:8080;
}
}
IP Whitelist
location / {
allow 192.168.1.0/24; # Local network
deny all;
proxy_pass http://localhost:8080;
}
Performance Tuning
Caching
# Define cache path
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m;
server {
location / {
proxy_cache my_cache;
proxy_cache_valid 200 60m;
proxy_cache_use_stale error timeout http_500 http_502 http_503;
proxy_pass http://localhost:8080;
}
}
Compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss;
Connection Limits
# Limit connections per IP
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
limit_conn addr 10; # Max 10 connections per IP
}
Useful Commands
# Test configuration
sudo nginx -t
# Reload configuration (no downtime)
sudo systemctl reload nginx
# Restart NGINX
sudo systemctl restart nginx
# View error log
sudo tail -f /var/log/nginx/error.log
# View access log
sudo tail -f /var/log/nginx/access.log
# Check NGINX status
sudo systemctl status nginx
Troubleshooting
502 Bad Gateway
Cause: Backend service is down or unreachable
Solutions:
# Check if backend is running
curl http://localhost:8080
# Check NGINX error log
sudo tail /var/log/nginx/error.log
# Verify proxy_pass URL is correct
413 Request Entity Too Large
Cause: Upload size exceeds limit
Solution:
client_max_body_size 100M;
Permission Denied
Cause: NGINX can't access files or sockets
Solutions:
# Check file permissions
ls -la /path/to/file
# Check SELinux (if enabled)
sudo setsebool -P httpd_can_network_connect 1
Best Practices
Separate Config Files: One file per service in sites-available/
Test Before Reload: Always run nginx -t before reloading
Use Includes: Break complex configs into reusable snippets
Log Rotation: Configure logrotate to prevent disk fill
Monitor Logs: Regularly check error logs for issues
Keep Updated: Apply security updates promptly
Example: Complete Service Configuration
# /etc/nginx/sites-available/jellyfin
server {
listen 80;
server_name jellyfin.example.com;
# Redirect to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name jellyfin.example.com;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/jellyfin.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/jellyfin.example.com/privkey.pem;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=63072000" always;
# Large file support for media
client_max_body_size 0;
location / {
proxy_pass http://localhost: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;
# Buffering off for streaming
proxy_buffering off;
}
# Logging
access_log /var/log/nginx/jellyfin.access.log;
error_log /var/log/nginx/jellyfin.error.log;
}
Related Topics:
- Self-Hosting a Home Server - Complete homelab guide
- Docker - Container platform
- Tailscale - Secure remote access