How to Install SSL Certificate on Nginx Server
Picture this: you’ve spent weeks building your new site, you finally push to production, and within minutes your phone lights up — “Your connection is not private.” A missing padlock is more than an aesthetic annoyance; modern browsers actively warn users away from unencrypted sites, and search engines quietly penalize them in rankings. Getting SSL right on Nginx isn’t complicated, but the details matter. This guide covers everything from grabbing a free Let’s Encrypt certificate with Certbot to tuning your config for an SSL Labs A+ score — so you can ship with confidence and sleep without alerts.
Prerequisites
Before you begin, ensure you have:
- Root or sudo access to your server
- Nginx installed and running
- A domain name pointing to your server
- Basic command line knowledge
Check your Nginx version:
nginx -v
Method 1: Installing Let’s Encrypt Certificate with Certbot
Let’s Encrypt provides free SSL certificates with automated renewal. This is the recommended approach for most websites.
Step 1: Install Certbot
Ubuntu/Debian:
sudo apt update
sudo apt install certbot python3-certbot-nginx
CentOS/RHEL:
sudo yum install certbot python3-certbot-nginx
Fedora:
sudo dnf install certbot python3-certbot-nginx
Step 2: Obtain Certificate
Certbot can automatically configure Nginx for you:
sudo certbot --nginx -d example.com -d www.example.com
Follow the prompts:
- Enter your email address (for renewal notifications)
- Agree to Terms of Service
- Choose whether to redirect HTTP to HTTPS (recommended)
Certbot will:
- Validate domain ownership
- Obtain the certificate
- Update Nginx configuration
- Reload Nginx
Step 3: Test Automatic Renewal
Let’s Encrypt certificates expire after 90 days. Certbot sets up automatic renewal:
# Test renewal process
sudo certbot renew --dry-run
# View renewal timer (systemd)
sudo systemctl status certbot.timer
Step 4: Verify Installation
Visit your website using https://:
# Test SSL configuration
curl -I https://example.com
# Check certificate details
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
Method 2: Installing Commercial SSL Certificate
If you purchased an SSL certificate from a commercial CA (DigiCert, GlobalSign, Sectigo, etc.), follow these steps.
Step 1: Generate Certificate Signing Request (CSR)
Create a private key and CSR:
# Generate private key (RSA 2048-bit)
sudo openssl genrsa -out /etc/ssl/private/example.com.key 2048
# Secure the private key
sudo chmod 600 /etc/ssl/private/example.com.key
# Generate CSR
sudo openssl req -new -key /etc/ssl/private/example.com.key -out /etc/ssl/certs/example.com.csr
During CSR generation, provide:
- Country Name: Two-letter country code (e.g., US, GB, PL)
- State/Province: Full name (e.g., California, Mazowieckie)
- City: Full city name
- Organization Name: Your company name
- Organizational Unit: Department (optional)
- Common Name: Your domain (e.g., example.com)
- Email Address: Contact email
Step 2: Submit CSR to Certificate Authority
- Copy the CSR content:
cat /etc/ssl/certs/example.com.csr
- Paste it into your CA’s certificate request form
- Complete domain validation (email, DNS, or file-based)
- Download the issued certificate and intermediate certificates
Step 3: Install Certificate Files
Upload the certificate files to your server:
# Primary certificate
sudo nano /etc/ssl/certs/example.com.crt
# Intermediate certificates (chain)
sudo nano /etc/ssl/certs/example.com-chain.crt
# Or combine them into one file
cat example.com.crt example.com-chain.crt > example.com-fullchain.crt
sudo mv example.com-fullchain.crt /etc/ssl/certs/
Set proper permissions:
sudo chmod 644 /etc/ssl/certs/example.com*.crt
sudo chmod 600 /etc/ssl/private/example.com.key
Configuring Nginx for SSL
Basic SSL Configuration
Edit your Nginx site configuration:
sudo nano /etc/nginx/sites-available/example.com
Add SSL configuration:
server {
listen 80;
server_name example.com www.example.com;
# Redirect all HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL Certificate paths
ssl_certificate /etc/ssl/certs/example.com-fullchain.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
# SSL Configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
# SSL Session
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/example.com-chain.crt;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Security Headers
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;
# Your application configuration
root /var/www/example.com;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}
Test Configuration
Before restarting Nginx, test the configuration:
sudo nginx -t
If successful, reload Nginx:
sudo systemctl reload nginx
Optimal Nginx SSL Configuration
Strong SSL Settings
For maximum security (A+ rating on SSL Labs):
# Modern configuration (supports TLS 1.2 and 1.3 only)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
# Diffie-Hellman parameter for DHE ciphersuites
ssl_dhparam /etc/ssl/certs/dhparam.pem;
# HSTS (force HTTPS for 1 year, including subdomains)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
Generate DH parameters (this takes time):
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
Performance Optimization
# SSL Session Resumption
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# Enable HTTP/2
listen 443 ssl http2;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/chain.pem;
Multiple Domains
For hosting multiple SSL sites:
# Site 1
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/ssl/certs/example.com-fullchain.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
# ... rest of configuration
}
# Site 2
server {
listen 443 ssl http2;
server_name another.com www.another.com;
ssl_certificate /etc/ssl/certs/another.com-fullchain.crt;
ssl_certificate_key /etc/ssl/private/another.com.key;
# ... rest of configuration
}
Troubleshooting Common Issues
The issues below cover the most common snags. If you’re staring at something more puzzling — like intermittent handshake failures or OCSP errors — our comprehensive SSL/TLS troubleshooting guide has deeper diagnostics and step-by-step fixes for ten of the most common certificate problems.
Certificate Chain Issues
Problem: Browser shows “Incomplete certificate chain”
Solution: Include intermediate certificates:
# Combine certificate and chain
cat certificate.crt intermediate.crt > fullchain.crt
# Or use the CA's bundle file
ssl_certificate /etc/ssl/certs/fullchain.crt;
Mixed Content Warnings
Problem: Page loaded over HTTPS contains HTTP resources
Solution: Update all URLs to use HTTPS or protocol-relative URLs:
<!-- Bad -->
<script src="http://example.com/script.js"></script>
<!-- Good -->
<script src="https://example.com/script.js"></script>
<!-- Also good (protocol-relative) -->
<script src="//example.com/script.js"></script>
ERR_SSL_PROTOCOL_ERROR
Problem: Browser cannot establish SSL connection
Solutions:
- Check if Nginx is listening on port 443
- Verify firewall allows port 443
- Test SSL configuration:
nginx -t - Check certificate paths are correct
# Check if Nginx listens on 443
sudo netstat -tlnp | grep :443
# Test firewall
sudo ufw status
sudo ufw allow 443/tcp
# Check certificate files exist
ls -la /etc/ssl/certs/example.com*
ls -la /etc/ssl/private/example.com.key
Certificate Expiration
Monitor your certificates using CrtMgr or set up manual checks. For production infrastructure, a dedicated monitoring stack is worth the investment — check out how to set up SSL certificate monitoring with Prometheus and Grafana for full observability with alerting:
# Check expiration date
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
# Create monitoring script
#!/bin/bash
DOMAIN="example.com"
DAYS_WARN=30
EXPIRY=$(echo | openssl s_client -connect $DOMAIN:443 -servername $DOMAIN 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
if [ $DAYS_LEFT -lt $DAYS_WARN ]; then
echo "Warning: Certificate for $DOMAIN expires in $DAYS_LEFT days"
fi
Security Best Practices
Keep Private Keys Secure
- Use restrictive permissions (600)
- Never commit to version control
- Consider hardware security modules (HSM) for sensitive applications
Regular Updates
- Keep Nginx updated
- Update SSL/TLS configuration as standards evolve
- Monitor security advisories
Use Strong Protocols
- Disable SSLv3, TLS 1.0, TLS 1.1
- Enable TLS 1.2 and TLS 1.3 only
- Use strong cipher suites
Enable HSTS
- Force HTTPS connections
- Prevent protocol downgrade attacks
- Consider HSTS preload list
Monitor Certificate Expiration
- Use monitoring tools like CrtMgr
- Set up renewal alerts
- Test renewal processes regularly
Testing Your SSL Configuration
Online Tools
Command Line Testing
# Test SSL connection
openssl s_client -connect example.com:443 -servername example.com
# Check certificate chain
openssl s_client -connect example.com:443 -servername example.com -showcerts
# Test specific TLS version
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3
# Check OCSP stapling
openssl s_client -connect example.com:443 -status
Installing SSL on Nginx is genuinely straightforward once you know the path. Certbot handles the Let’s Encrypt workflow almost entirely for you, and a well-tuned cipher block will satisfy even the most opinionated security auditors. Enable HTTP/2 while you’re in there — it’s free performance that requires exactly one word change in your config.
Whatever you do, don’t skip monitoring. A certificate that expires silently at 2 AM on a Saturday is the most avoidable outage in infrastructure management. Run your final config through SSL Labs to confirm that A+ rating, then get your expiration monitoring sorted. CrtMgr makes it easy to track all your domains in one dashboard — so you’re never scrambling because you forgot a renewal.