Setting up a reverse proxy is one of the most essential skills for any backend developer or
DevOps engineer. Whether you're deploying microservices, load balancing traffic, or securing
your applications with SSL/TLS, Nginx remains the gold standard. In this
comprehensive guide, I'll walk you through configuring Nginx as a reverse proxy for
utkarsh.com—a real-world scenario that covers everything from basic setup to
production-ready optimizations.
What is a Reverse Proxy?
A reverse proxy sits between client requests and your backend servers, acting as an intermediary that forwards requests to the appropriate destination. Unlike a forward proxy (which protects clients), a reverse proxy protects your servers and provides several critical benefits:
- Load Balancing: Distribute traffic across multiple backend servers
- SSL Termination: Handle HTTPS encryption at the proxy level
- Caching: Store frequently accessed content to reduce backend load
- Security: Hide backend server details and filter malicious requests
- Compression: Reduce bandwidth by compressing responses
Prerequisites
Before we begin, ensure you have:
- A VPS or dedicated server with Ubuntu 22.04+ (or similar Linux distribution)
- Root or sudo access to the server
- Domain name (
utkarsh.com) with DNS pointing to your server's IP - Backend application running on a local port (e.g., Node.js on port 3000)
Step 1: Install Nginx
First, update your system packages and install Nginx:
# Update package lists
sudo apt update
# Install Nginx
sudo apt install nginx -y
# Start and enable Nginx
sudo systemctl start nginx
sudo systemctl enable nginx
# Verify installation
nginx -v
Step 2: Basic Reverse Proxy Configuration
Let's create a configuration file for utkarsh.com. Nginx stores site configurations
in /etc/nginx/sites-available/:
# Create configuration file
sudo nano /etc/nginx/sites-available/utkarsh.com
Add the following basic reverse proxy configuration:
server {
listen 80;
listen [::]:80;
server_name utkarsh.com www.utkarsh.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass $http_upgrade;
}
}
Understanding the Configuration
listen 80: Accept HTTP connections on port 80server_name: Match requests for utkarsh.com and www.utkarsh.comproxy_pass: Forward requests to your backend applicationproxy_set_header: Pass important client information to the backendX-Real-IP: The actual client IP addressX-Forwarded-For: Chain of proxy IPs the request passed throughX-Forwarded-Proto: Original protocol (http/https)
Step 3: Enable the Site Configuration
Create a symbolic link to enable the site and test the configuration:
# Enable the site
sudo ln -s /etc/nginx/sites-available/utkarsh.com /etc/nginx/sites-enabled/
# Test Nginx configuration for syntax errors
sudo nginx -t
# If test passes, reload Nginx
sudo systemctl reload nginx
Step 4: SSL/TLS Setup with Let's Encrypt
Security is non-negotiable in production. Let's encrypt our traffic using free SSL certificates from Let's Encrypt:
# Install Certbot
sudo apt install certbot python3-certbot-nginx -y
# Obtain SSL certificate
sudo certbot --nginx -d utkarsh.com -d www.utkarsh.com
# Follow the prompts to complete setup
Certbot automatically updates your Nginx configuration. Here's what the SSL-enabled configuration looks like:
server {
listen 80;
listen [::]:80;
server_name utkarsh.com www.utkarsh.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name utkarsh.com www.utkarsh.com;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/utkarsh.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/utkarsh.com/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# Modern SSL Configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# HSTS (ngx_http_headers_module is required)
add_header Strict-Transport-Security "max-age=63072000" always;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass $http_upgrade;
}
}
sudo certbot renew --dry-run
Step 5: Load Balancing Configuration
For high-traffic applications, distribute load across multiple backend servers:
# Define upstream servers
upstream utkarsh_backend {
least_conn; # Load balancing method
server 127.0.0.1:3000 weight=3;
server 127.0.0.1:3001 weight=2;
server 127.0.0.1:3002 weight=1;
# Health checks
keepalive 32;
}
server {
listen 443 ssl http2;
server_name utkarsh.com www.utkarsh.com;
# SSL configuration...
location / {
proxy_pass http://utkarsh_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
# Other proxy headers...
}
}
Load Balancing Methods
round_robin(default): Distributes requests evenlyleast_conn: Routes to server with fewest active connectionsip_hash: Same client IP always goes to same backend (session persistence)weight: Assign relative weights to servers based on capacity
Step 6: Enable Caching for Performance
Reduce backend load and improve response times with proxy caching:
# Define cache zone (add to http block in nginx.conf)
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=utkarsh_cache:10m
max_size=1g inactive=60m use_temp_path=off;
server {
# ... SSL and server configuration ...
location / {
proxy_pass http://localhost:3000;
# Enable caching
proxy_cache utkarsh_cache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_lock on;
# Add cache status header for debugging
add_header X-Cache-Status $upstream_cache_status;
# Other proxy headers...
}
# Bypass cache for dynamic content
location /api/ {
proxy_pass http://localhost:3000;
proxy_cache_bypass 1;
proxy_no_cache 1;
# Proxy headers...
}
}
Step 7: Security Hardening
Protect your reverse proxy with essential security headers and configurations:
server {
# ... existing configuration ...
# 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;
# Hide Nginx version
server_tokens off;
# Rate Limiting
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
# Proxy configuration...
}
# Block common exploits
location ~* \.(git|env|htaccess|htpasswd)$ {
deny all;
return 404;
}
}
Step 8: Gzip Compression
Reduce bandwidth usage by compressing responses:
http {
# Enable Gzip
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json
application/javascript application/rss+xml
application/atom+xml image/svg+xml;
gzip_min_length 1000;
}
Complete Production Configuration
Here's the final, production-ready configuration combining all the optimizations:
# /etc/nginx/sites-available/utkarsh.com
upstream utkarsh_backend {
least_conn;
server 127.0.0.1:3000;
keepalive 32;
}
# Redirect HTTP to HTTPS
server {
listen 80;
listen [::]:80;
server_name utkarsh.com www.utkarsh.com;
return 301 https://$server_name$request_uri;
}
# Main HTTPS server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name utkarsh.com www.utkarsh.com;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/utkarsh.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/utkarsh.com/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
# Security Headers
add_header Strict-Transport-Security "max-age=63072000" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
server_tokens off;
# Proxy timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Main application
location / {
proxy_pass http://utkarsh_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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_cache_bypass $http_upgrade;
}
# Static files with long cache
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
proxy_pass http://utkarsh_backend;
}
# Health check endpoint
location /health {
access_log off;
return 200 "OK";
add_header Content-Type text/plain;
}
}
Common Troubleshooting
502 Bad Gateway
This error typically means Nginx can't connect to your backend:
- Verify your backend application is running:
curl http://localhost:3000 - Check if the port is correct in your
proxy_passdirective - Review Nginx error logs:
sudo tail -f /var/log/nginx/error.log
504 Gateway Timeout
Your backend is taking too long to respond:
- Increase timeout values in Nginx configuration
- Optimize your backend application's response time
- Consider adding caching for slow endpoints
sudo nginx -t
before reloading Nginx to avoid downtime.
Conclusion
Configuring Nginx as a reverse proxy is a fundamental skill that unlocks powerful capabilities for your web applications. We've covered everything from basic setup to production-grade configurations including SSL termination, load balancing, caching, and security hardening.
Remember these key takeaways:
- Always use HTTPS in production—Let's Encrypt makes it free and easy
- Implement proper headers for security and client information forwarding
- Enable caching for static content to reduce backend load
- Monitor and log your reverse proxy for troubleshooting
- Test configurations before applying them to avoid downtime
With this knowledge, you're ready to deploy robust, scalable, and secure web applications behind Nginx. Happy deploying! 🚀