Nginx SSL and HTTPS Configuration Secure Your Node.js App with Let's Encrypt
Learn how to configure SSL/HTTPS in Nginx for your Node.js application, including obtaining certificates with Let's Encrypt, HTTP to HTTPS redirects, and security headers.
Nginx SSL and HTTPS Configuration
Securing your Node.js app with HTTPS is essential. This guide covers configuring SSL in Nginx, obtaining certificates with Let's Encrypt, and security best practices.
Step 1: Obtain SSL Certificate
sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Certbot automatically:
- Verifies domain ownership
- Obtains the SSL certificate
- Modifies Nginx config to use HTTPS
- Sets up HTTP to HTTPS redirect
- Configures auto-renewal
Step 2: Manual SSL Configuration
If you have certificates from another CA:
server { listen 80; server_name yourdomain.com; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name yourdomain.com; ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/privkey.pem; location / { proxy_pass http://localhost:3000; 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; } }
Step 3: Optimize SSL Settings
Add these to the SSL server block:
server { listen 443 ssl http2; server_name yourdomain.com; ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; # SSL optimization ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # HSTS header add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; location / { proxy_pass http://localhost:3000; # ... proxy headers ... } }
Key SSL Directives
ssl_protocols Which TLS versions to support. TLSv1.2 and TLSv1.3 are current. Disable TLSv1.0 and TLSv1.1 (insecure).
ssl_ciphers Which cipher suites to use. HIGH:!aNULL:!MD5 uses strong ciphers only.
ssl_prefer_server_ciphers Let the server choose the cipher (not the client).
ssl_session_cache Cache SSL sessions for faster repeat connections.
add_header Strict-Transport-Security HSTS tells browsers to always use HTTPS for this domain.
HTTP to HTTPS Redirect
server { listen 80; server_name yourdomain.com www.yourdomain.com; return 301 https://$host$request_uri; }
This redirects all HTTP traffic to HTTPS permanently (301).
Security Headers
server { listen 443 ssl; # ... SSL config ... add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; 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; }
Verify SSL Configuration
# Check SSL Labs grade # Visit: https://www.ssllabs.com/ssltest/ # Check certificate sudo openssl x509 -in /etc/letsencrypt/live/yourdomain.com/cert.pem -text -noout # Test auto-renewal sudo certbot renew --dry-run
Update Node.js for HTTPS Behind Nginx
Since Nginx handles SSL, Node.js receives plain HTTP. But Node.js needs to know the original request was HTTPS for generating correct URLs and setting secure cookies.
// app.js app.set("trust proxy", 1); // Trust Nginx proxy // Now req.protocol returns 'https' when behind Nginx with SSL // req.secure returns true // req.ip returns the real client IP
Update cookie settings:
res.cookie("token", token, { httpOnly: true, secure: true, // HTTPS only sameSite: "lax", maxAge: 7 * 24 * 60 * 60 * 1000 });
Auto-Renewal
Let's Encrypt certificates expire in 90 days. Certbot sets up auto-renewal:
# Check the timer sudo systemctl list-timers | grep certbot # Manual renewal sudo certbot renew # Reload Nginx after renewal sudo certbot renew --deploy-hook "systemctl reload nginx"
The Takeaway
Nginx SSL configuration involves obtaining certificates with Let's Encrypt (certbot --nginx), adding SSL directives (ssl_protocols, ssl_ciphers, ssl_session_cache), redirecting HTTP to HTTPS, adding security headers (HSTS, X-Frame-Options), and setting trust proxy in Express. Auto-renewal is handled by certbot's systemd timer. Always verify with SSL Labs test.
Install certbot, run sudo certbot --nginx -d yourdomain.com. Certbot obtains the SSL certificate and modifies Nginx config automatically. It adds listen 443 ssl, certificate paths, and HTTP to HTTPS redirect. Set up auto-renewal with certbot renew --dry-run.
Use ssl_protocols TLSv1.2 TLSv1.3 (disable TLSv1.0 and 1.1 as they're insecure). Use ssl_ciphers HIGH:!aNULL:!MD5 for strong ciphers only. Set ssl_prefer_server_ciphers on and ssl_session_cache shared:SSL:10m for performance.
Create a server block with listen 80 and return 301 https://$host$request_uri. This permanently redirects all HTTP traffic to HTTPS. Certbot adds this automatically when you run it with the --nginx flag.
Strict-Transport-Security (HSTS) tells browsers to always use HTTPS for your domain, even if the user types http://. Add add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains' always to prevent SSL stripping attacks.
Set app.set('trust proxy', 1) in Express. This makes req.protocol return 'https', req.secure return true, and req.ip return the real client IP (from X-Forwarded-For). Set secure: true on cookies so they're only sent over HTTPS.
Ready to master Node.js completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master Node.js
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

