Deep dive

How to Install an SSL Certificate on Nginx From Scratch

This walks through the complete process of getting HTTPS working on Nginx from a plain HTTP-only starting point — certificate acquisition, server block configuration, the redirect, and a basic hardening pass, in the order that avoids common pitfalls.

A real Nginx SSL server block, tested and reloaded, followed by a live TLS 1.3 handshake against it — captured from an actual test server.
A real Nginx SSL server block, tested and reloaded, followed by a live TLS 1.3 handshake against it — captured from an actual test server.
1 Visitor requests http://yourdomain.com Arrives at Nginx on port 80 2 Port-80 block issues a 301 redirect Sends the browser to the https:// version of the same URL 3 Browser reconnects on port 443 Nginx completes the TLS handshake using the configured certificate 4 Request served over HTTPS The port-443 server block handles it normally from here on
How a Request Flows After This Setup

1. Obtain a certificate

For a straightforward setup, Certbot's Nginx plugin handles both issuance and configuration in one step (see our Certbot automation guide for full detail):

sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

If you're installing a certificate obtained separately (a paid certificate from another CA, for example), you'll configure the server block manually instead, as shown below.

2. The HTTPS server block

server {
    listen 443 ssl;
    server_name yourdomain.com www.yourdomain.com;

    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;

    root /var/www/yourdomain;
    index index.html;
}

ssl_certificate should point to the full chain (your certificate plus intermediates concatenated), not just your certificate alone — a common first-time mistake that produces a certificate that works in some browsers (ones that cache the intermediate separately) but fails trust checks in others.

3. The HTTP-to-HTTPS redirect block

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$host$request_uri;
}

This is a separate server block, not a rule inside the HTTPS block — Nginx needs distinct blocks per listening port, with the port-80 block's only job being this redirect.

4. Reload, don't restart

sudo nginx -t && sudo systemctl reload nginx

Always run nginx -t first to validate configuration syntax — catching a typo before reloading avoids taking the server down with a broken configuration, which reload (unlike some other services' restart behavior) will refuse to do if the test fails first.

5. A basic hardening pass

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
add_header Strict-Transport-Security "max-age=31536000" always;

Session caching improves performance for repeat visitors; the HSTS header (start with a shorter max-age while confirming everything is stable — see our HSTS deep dive) adds the redirect-loop protection covered elsewhere in this site.

6. Confirm it's in practice working

Run an SSL scan tool against your domain, and separately check that the plain http:// version correctly redirects with a 301 — both checks catch different classes of misconfiguration that a quick visual browser check alone might miss.

Common first-time mistakes to check for

What server block priority and default_server means for a multi-site Nginx setup

On a server hosting multiple sites, Nginx selects which server block handles a request based on server_name matching, with a designated default_server block catching anything that doesn't match explicitly — misconfiguring this can cause one site's certificate to be served for an unrelated domain's requests.

Confirm the full chain with OpenSSL, not just a green padlock

Run openssl s_client -connect yourdomain.com:443 -showcerts after installing — it shows every certificate Nginx is actually sending, in order. A missing intermediate here is often invisible in a browser (which frequently patches the gap itself via AIA fetching) but breaks curl, most programming-language HTTP clients, and older devices outright.

Working versus production-ready, on Nginx specifically

A working Nginx config successfully serves HTTPS. A production-ready one also has OCSP stapling actually functioning — not just ssl_stapling on; present, but a working resolver directive behind it, checked with the same -status flag on the command above — plus a current cipher suite list and HSTS. An SSL scan tool's overall grade is the fastest way to see the gap between the two.

Testing before DNS has fully propagated

For a brand-new domain, point curl at your server's IP directly with curl --resolve yourdomain.com:443:1.2.3.4 https://yourdomain.com — this exercises your actual server_name match and certificate, including which server { } block Nginx selects, without waiting on DNS at all.

One annotated server block as your reference

Keep a single, commented server { } block — redirect, HTTPS block, certificate paths, cipher configuration, OCSP stapling, HSTS — as the template every future Nginx site starts from, rather than reassembling the pieces from memory each time.

Validate before every reload, not just the first one

nginx -t takes a few seconds and catches a syntax error before it can take down an otherwise-working site — worth running before every reload for the life of the server, not just while you're actively debugging something.

A quick closing checklist for a from-scratch Nginx install

Before calling it done: the certificate and full chain check out under openssl s_client -showcerts, HTTP redirects to HTTPS with a 301, ssl_stapling shows a successful OCSP response under the same status check, and an SSL scan tool reports a strong grade.

Adding HTTP/2 alongside your new setup

On Nginx 1.25.1 and later, HTTP/2 is a standalone http2 on; directive rather than a flag on the listen line — a meaningful free performance upgrade for multi-resource pages, available the moment HTTPS itself is working, since every major browser requires HTTPS for HTTP/2.

Supporting a lingering TLS 1.0 client, temporarily

If you have specific, confirmed evidence some portion of your audience still needs TLS 1.0 or 1.1, Mozilla's SSL Configuration Generator has an "old" preset for exactly this — treat it as a deliberate, temporary ssl_protocols override with a plan to remove it, not a permanent default.

Test on a subdomain before touching your primary domain

Apply the complete configuration to a lower-stakes subdomain's server { } block first. A mistake there costs you nothing important; the same mistake applied directly to your primary domain costs you your highest-traffic property's uptime.

The short version: a working Nginx HTTPS setup is a certificate block referencing the full chain, a separate port-80 block handling the redirect, and a reload — everything past that is refinement, not a requirement.

Comments

Loading comments…