TLS & HTTPS With Nginx
Every public website and API should be served over HTTPS, and Nginx is one of the most common places to terminate TLS. It holds the certificate and private key, negotiates encryption with clients, and forwards plain or re-encrypted traffic to backends. A correct configuration protects users' data, earns browser trust, enables HTTP/2 and HTTP/3, and passes security scanners. A careless one leaves weak protocols enabled, redirect loops, or expired certificates that take the site down.
Modern best practice is well established: automated certificates from Let's Encrypt (or your cloud CA), TLS 1.2 and 1.3 only, strong cipher suites, HSTS, and OCSP stapling. See HTTPS & TLS for the protocol fundamentals.
TL;DR
- Configure
listen 443 ssl,ssl_certificate(full chain) andssl_certificate_key. - Automate certificates with Let's Encrypt (Certbot, acme.sh, or lego) and auto-renewal.
- Allow TLS 1.2 and 1.3 only; use Mozilla's "intermediate" configuration for ciphers.
- Redirect HTTP to HTTPS with a 301, and add HSTS once HTTPS is solid everywhere.
- Enable OCSP stapling and session resumption, and turn on HTTP/2 (and optionally HTTP/3/QUIC).
- Use TLS to upstreams (
proxy_ssl_*) or mTLS when traffic crosses untrusted networks.
Quick Example
Core Concepts
Certificates
ssl_certificatemust contain your certificate and the intermediate chain (fullchain.pem). Missing intermediates work in some browsers and fail in others and in API clients.ssl_certificate_keyis the private key: protect it (600, owned by root) and never commit it. See Linux permissions.- Certificate types: single-domain, multi-domain (SAN), and wildcard (
*.example.com, which requires DNS-01 validation with Let's Encrypt). - Key types: ECDSA (P-256) certificates are smaller and faster; serving both ECDSA and RSA certificates gives maximum compatibility.
Automating With ACME
Let's Encrypt issues free 90-day certificates via the ACME protocol, and shorter lifetimes are coming. Automation is mandatory:
- HTTP-01: the CA fetches a token from
/.well-known/acme-challenge/on port 80. - DNS-01: create a TXT record via your DNS provider's API. It's required for wildcards and works for internal hosts.
- Tools: Certbot (with
--nginxor--webroot), acme.sh, lego, and, in Kubernetes, cert-manager. - Reload after renewal: use a deploy hook (
--deploy-hook "systemctl reload nginx") so Nginx picks up new certificates.
Protocols and Ciphers
- Disable SSLv3, TLS 1.0, and 1.1 (deprecated and insecure). Enable TLS 1.2 and 1.3.
- TLS 1.3 cipher suites are fixed and secure. For TLS 1.2, allow only ECDHE key exchange with AEAD ciphers (AES-GCM, ChaCha20-Poly1305), which provides forward secrecy.
- Mozilla's SSL Configuration Generator provides maintained "modern", "intermediate", and "old" profiles. Intermediate suits most public sites.
HSTS
Strict-Transport-Security tells browsers to use HTTPS for your domain for max-age seconds, preventing downgrade and SSL-stripping attacks. Roll out gradually (a short max-age first, then a long one), add includeSubDomains only when every subdomain supports HTTPS, and consider preload only when you're sure it's permanent. See security headers.
OCSP Stapling and Session Resumption
- OCSP stapling has Nginx fetch and attach the certificate's revocation status, sparing clients a separate request to the CA. It needs a
resolver. (Let's Encrypt is phasing out OCSP in favor of CRLs, so check your CA's current guidance.) - Session resumption (
ssl_session_cache, and TLS 1.3 PSKs) lets returning clients skip full handshakes, reducing latency and CPU.
HTTP/2 and HTTP/3
http2 on; enables HTTP/2 multiplexing over TLS, which is standard for all modern browsers. HTTP/3 runs over QUIC (UDP): enable it with listen 443 quic, advertise it with Alt-Svc, and open UDP 443 in firewalls. See HTTP/3 and QUIC.
TLS to Upstreams and mTLS
If traffic between Nginx and backends crosses untrusted networks, encrypt it:
Mutual TLS (ssl_verify_client on with ssl_client_certificate) requires clients to present certificates, which is common for service-to-service APIs and zero trust networks.
Best Practices
Automate Renewal and Monitor Expiry
Expired certificates are one of the most common, and most avoidable, outages. Automate renewal, and independently alert on certificate expiry (blackbox exporter probes, uptime monitors) at least 14 days out.
Use Proven Configuration Templates
Start from Mozilla's generator for your Nginx and OpenSSL versions, rather than copying old snippets with outdated ciphers or directives.
Test Your Configuration
Use SSL Labs' server test, testssl.sh, or nmap --script ssl-enum-ciphers to verify protocols, ciphers, chain completeness, and HSTS. Aim for an A or A+.
Terminate Once, Deliberately
Decide where TLS terminates (CDN, cloud load balancer, Nginx, or the app) and configure forwarded headers correctly downstream, so apps know the original scheme. See Nginx reverse proxy.
Common Mistakes
Serving Only the Leaf Certificate
Incomplete chains cause "unable to verify the first certificate" errors in curl, mobile apps, and API clients.
Enabling HSTS Before HTTPS Works Everywhere
A long-lived includeSubDomains HSTS header makes browsers refuse HTTP for every subdomain, breaking any that don't yet have valid HTTPS, and it can't be revoked quickly for returning visitors.
Redirect Loops Behind Another Proxy
If a load balancer terminates TLS and talks HTTP to Nginx, a blanket "redirect to HTTPS" in Nginx loops forever. Redirect based on X-Forwarded-Proto from the trusted load balancer, or perform redirects at the edge.
FAQ
How do I get a free TLS certificate for Nginx?
Use Let's Encrypt via an ACME client such as Certbot (certbot --nginx or certonly --webroot), acme.sh, or cert-manager in Kubernetes. Certificates are valid for 90 days, so enable automatic renewal and reload Nginx after each renewal.
Which TLS versions should I support?
TLS 1.2 and TLS 1.3. Older versions (SSL 3.0, TLS 1.0, 1.1) are insecure and deprecated by browsers and standards. Only keep TLS 1.2 ciphers with forward secrecy and authenticated encryption.
Does HTTPS slow down my site?
Negligibly on modern hardware, and HTTPS unlocks HTTP/2 and HTTP/3, which usually make sites faster. Session resumption, OCSP stapling, ECDSA certificates, and TLS 1.3 (one round-trip handshake) keep the overhead minimal.
Should TLS terminate at Nginx or at the load balancer?
Either works. Terminating at a managed cloud load balancer or CDN simplifies certificate management at the edge. Terminating (or re-encrypting) at Nginx keeps traffic encrypted closer to the application. Security policies may require encryption all the way to the backend.
Related Topics
- Nginx — The web server overview
- HTTPS & TLS — Protocol fundamentals
- Security Headers — HSTS and related headers
- HTTP/3 & QUIC — The UDP-based next generation of HTTP
- Nginx Reverse Proxy — Forwarded protocol headers
- Encryption — Cryptography behind TLS