How to Install SSL Certificate on Nginx Server
Nginx is one of the most popular web servers, powering millions of websites worldwide. This guide walks you through installing SSL/TLS certificates on Nginx, from obtaining certificates to optimal configuration.
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
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:
# 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
- SSL Labs: https://www.ssllabs.com/ssltest/
- Security Headers: https://securityheaders.com/
- Mozilla Observatory: https://observatory.mozilla.org/
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
Conclusion
Installing SSL certificates on Nginx is straightforward, whether using free Let’s Encrypt certificates or commercial ones. Key points to remember:
- Use Certbot for Let’s Encrypt (easiest method)
- Follow security best practices for configuration
- Enable HTTP/2 for better performance
- Set up proper monitoring for certificate expiration
- Test your configuration with SSL Labs
With proper SSL configuration, your Nginx server will provide secure, fast HTTPS connections to your users. Use tools like CrtMgr to monitor your certificates and ensure they never expire unexpectedly.