All Deployment guides Deployment

How to Install an SSL Certificate on Nginx

This guide walks you through installing an SSL certificate on Nginx — from uploading the files to optimizing TLS settings and troubleshooting common errors. It works with certificates from GetHTTPS, Certbot, or any other source.

If you don’t have a certificate yet, get one for free in 5 minutes.

Prerequisites

  • Nginx installed and serving your site on HTTP (port 80)
  • Root/sudo access to the server
  • Certificate files — you need two:
    • fullchain.pem — your certificate + intermediate chain (combined)
    • privkey.pem — your private key

Which file is which? GetHTTPS gives you four files. Nginx needs fullchain.pem (not cert.pem) and privkey.pem. Using cert.pem alone causes “incomplete chain” errors because browsers can’t verify the trust path. Certificate formats explained →

Step 1: Upload certificate files to the server

Copy the files to a secure directory:

# Create directory
sudo mkdir -p /etc/ssl/gethttps

# Copy files (from your local machine to the server)
sudo cp fullchain.pem /etc/ssl/gethttps/
sudo cp privkey.pem /etc/ssl/gethttps/

# Restrict private key permissions — only root should read it
sudo chmod 600 /etc/ssl/gethttps/privkey.pem
sudo chmod 644 /etc/ssl/gethttps/fullchain.pem
sudo chown root:root /etc/ssl/gethttps/*

If transferring via SCP:

scp fullchain.pem privkey.pem user@your-server:/tmp/
# Then on the server:
sudo mv /tmp/fullchain.pem /tmp/privkey.pem /etc/ssl/gethttps/

Step 2: Configure the HTTPS server block

Edit your site’s Nginx configuration. The file is typically at:

  • /etc/nginx/sites-available/yourdomain.conf (Debian/Ubuntu)
  • /etc/nginx/conf.d/yourdomain.conf (CentOS/RHEL)

Complete recommended configuration:

# HTTPS server block
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com www.example.com;

    # ─── Certificate files ───────────────────────
    ssl_certificate     /etc/ssl/gethttps/fullchain.pem;
    ssl_certificate_key /etc/ssl/gethttps/privkey.pem;

    # ─── TLS protocol and ciphers ────────────────
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;

    # ─── Session caching (performance) ───────────
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # ─── OCSP stapling (faster verification) ─────
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    # ─── Security headers ────────────────────────
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;

    # ─── Your site ───────────────────────────────
    root /var/www/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

# HTTP redirect block
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

What each section does:

DirectivePurpose
listen 443 ssl http2HTTPS on port 443 with HTTP/2 enabled
ssl_certificatePath to your certificate + chain
ssl_certificate_keyPath to your private key
ssl_protocolsOnly TLS 1.2 and 1.3 (1.0/1.1 deprecated since 2020)
ssl_prefer_server_ciphers offLet the client choose the best cipher (modern best practice)
ssl_session_cacheCache TLS sessions — avoids re-handshake for returning visitors
ssl_session_tickets offTickets can weaken forward secrecy
ssl_staplingServer fetches OCSP response — faster for clients
Strict-Transport-SecurityHSTS — browsers remember to always use HTTPS
return 301Permanent redirect from HTTP to HTTPS

Step 3: Test the configuration

Always test before reloading:

sudo nginx -t

Expected output:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

If the test fails, Nginx tells you the exact line number with the error. Common mistakes:

  • Typo in the file path (ssl_certifcate instead of ssl_certificate)
  • File doesn’t exist at the specified path
  • Missing semicolons

Step 4: Reload Nginx

sudo systemctl reload nginx

Use reload, not restart. Reload applies the new configuration to new connections without dropping existing ones — zero downtime.

Step 5: Verify the certificate

Browser check

Visit https://yourdomain.com. Click the padlock icon to confirm:

  • Issued by: “Let’s Encrypt” (or your CA)
  • Valid: start and end dates
  • Domain: matches your URL

Command-line check

# Full certificate details
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

# Check the full chain is present
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null \
  | grep -E 'Verify return code|depth='

Expected: Verify return code: 0 (ok) and multiple depth levels (your cert + intermediate).

Online check

Use SSL Labs Server Test for a comprehensive report. It checks:

  • Certificate chain validity
  • Protocol support (should show TLS 1.2 + 1.3 only)
  • Cipher suite strength
  • Known vulnerabilities (BEAST, POODLE, Heartbleed)
  • HSTS configuration

Target: Grade A or A+.

How to renew

When your certificate approaches expiry (day 60 of 90 for Let’s Encrypt):

  1. Get a new certificate from GetHTTPS (or your renewal tool)
  2. Replace the files:
    sudo cp new-fullchain.pem /etc/ssl/gethttps/fullchain.pem
    sudo cp new-privkey.pem /etc/ssl/gethttps/privkey.pem
  3. Reload Nginx:
    sudo systemctl reload nginx

No restart needed. Nginx picks up the new files on reload. Full renewal guide →

Want automatic renewal? Install Certbot for hands-off renewal: sudo certbot --nginx -d example.com. It handles everything including the Nginx config.

Troubleshooting

”SSL: error:0B080074:x509 certificate routines”

Cause: You’re using cert.pem instead of fullchain.pem.

Fix: Change ssl_certificate to point to fullchain.pem, which includes both your certificate and the intermediate chain of trust:

ssl_certificate /etc/ssl/gethttps/fullchain.pem;  # NOT cert.pem

“cannot load certificate key”

Cause: Private key file permissions are wrong, the file is corrupted, or the key doesn’t match the certificate.

Fix:

# Check permissions (should be 600)
ls -la /etc/ssl/gethttps/privkey.pem

# Verify the key is valid
sudo openssl ec -in /etc/ssl/gethttps/privkey.pem -check  # For ECDSA
sudo openssl rsa -in /etc/ssl/gethttps/privkey.pem -check  # For RSA

# Verify key matches certificate
sudo openssl x509 -noout -modulus -in /etc/ssl/gethttps/fullchain.pem | md5sum
sudo openssl rsa -noout -modulus -in /etc/ssl/gethttps/privkey.pem | md5sum
# Both hashes MUST match

“nginx: [emerg] bind() to 0.0.0.0:443 failed”

Cause: Port 443 is already in use by another process.

Fix:

# Find what's using port 443
sudo ss -tlnp | grep 443

# Usually: another Nginx instance or Apache
sudo systemctl stop apache2  # If Apache is running
sudo systemctl reload nginx

HTTPS works but browser shows “Not Secure”

Cause: Mixed content — your page loads images, scripts, or CSS over http://.

Fix: Open DevTools (F12) → Console → look for “Mixed Content” warnings. Update all resource URLs to use https:// or relative paths.

”ERR_SSL_VERSION_OR_CIPHER_MISMATCH”

Cause: Client doesn’t support the TLS versions or ciphers your server offers.

Fix: Make sure your config includes TLS 1.2 (not just 1.3):

ssl_protocols TLSv1.2 TLSv1.3;  # NOT just TLSv1.3

Certificate shows as “Untrusted” despite correct files

Cause: The certificate chain is incomplete — fullchain.pem is missing the intermediate certificate, or the file is in the wrong order.

Fix: Rebuild fullchain.pem by concatenating your cert + the intermediate:

cat cert.pem chain.pem > fullchain.pem

The order matters: your certificate first, then the intermediate.

Frequently asked questions

Do I need separate config files for HTTP and HTTPS?

No. Put both server blocks (port 80 and 443) in the same file. The HTTP block only exists to redirect to HTTPS.

Should I use ssl on;?

No. ssl on; is deprecated. Use listen 443 ssl; instead — the ssl parameter in the listen directive is the modern approach.

What’s the difference between reload and restart?

reload gracefully applies new config to new connections without dropping existing ones. restart stops and starts the process, briefly dropping all connections. Always use reload for certificate changes.

Can I serve multiple domains with different certificates?

Yes. Use separate server blocks with different server_name, ssl_certificate, and ssl_certificate_key directives. Nginx uses SNI (Server Name Indication) to serve the correct certificate based on the requested hostname.

Does Nginx support ECDSA/ECC certificates?

Yes. Nginx handles ECDSA certificates exactly like RSA — same directives, same file format. GetHTTPS generates ECDSA P-256 by default (smaller keys, faster handshakes).

How do I enable HTTP/2?

Add http2 to the listen directive: listen 443 ssl http2;. This is already in the recommended config above. HTTP/2 requires HTTPS — you can’t use it over plain HTTP.

Related articles

Getting Started 2026-05-08
How to Get a Free SSL Certificate (Step-by-Step Guide)
Get a free SSL certificate from Let's Encrypt in 5 minutes — no software to install, no account to create. Complete guide covering 4 methods, both challenge types, installation on 6 platforms, and troubleshooting.
Deployment 2026-05-07
How to Redirect HTTP to HTTPS
Force all traffic to HTTPS with server-side redirects. Configuration examples for Nginx, Apache, and .htaccess with 301 permanent redirects.
Deployment 2026-05-08
How to Install an SSL Certificate on Apache
Step-by-step guide to installing an SSL certificate on Apache with mod_ssl. Covers file upload, VirtualHost config, TLS best practices, HSTS, HTTP redirect, and troubleshooting 5 common errors.
Deployment 2026-05-07
How to Fix Mixed Content Warnings
Mixed content happens when an HTTPS page loads resources over HTTP. Learn how to find and fix mixed content errors to get a clean padlock icon.
Get a free SSL certificate in your browser
No installation, no account. Your private key never leaves your device.
Get your certificate