CrtMgr Blog - SSL/TLS Certificate Management Insights

Expert insights on SSL/TLS certificate management, types, deployment, and best practices

How to Install SSL Certificate on Nginx Server

Nginx SSL TLS Web Server Installation Guide

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

  1. Copy the CSR content:
cat /etc/ssl/certs/example.com.csr
  1. Paste it into your CA’s certificate request form
  2. Complete domain validation (email, DNS, or file-based)
  3. 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:

  1. Check if Nginx is listening on port 443
  2. Verify firewall allows port 443
  3. Test SSL configuration: nginx -t
  4. 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

  1. Keep Private Keys Secure

    • Use restrictive permissions (600)
    • Never commit to version control
    • Consider hardware security modules (HSM) for sensitive applications
  2. Regular Updates

    • Keep Nginx updated
    • Update SSL/TLS configuration as standards evolve
    • Monitor security advisories
  3. Use Strong Protocols

    • Disable SSLv3, TLS 1.0, TLS 1.1
    • Enable TLS 1.2 and TLS 1.3 only
    • Use strong cipher suites
  4. Enable HSTS

    • Force HTTPS connections
    • Prevent protocol downgrade attacks
    • Consider HSTS preload list
  5. 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.

Related Articles