TLS is mandatory for any production site. The minimum directives to enable it on a server block are listen 443 ssl http2;, ssl_certificate pointing at the PEM file, ssl_certificate_key at the private key, and ssl_protocols TLSv1.2 TLSv1.3;. The http2 keyword (or, in nginx versions before 1.25.1, the separate http2 on; directive) activates HTTP/2 over TLS, which is required for header compression and multiplexing. Older protocols — SSLv2, SSLv3, TLSv1.0, and TLSv1.1 — are all deprecated and should never appear in ssl_protocols; only TLSv1.2 and TLSv1.3 belong in a modern configuration.
Cipher selection and session reuse shape the actual handshake. A reasonable hardened cipher list is ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;, paired with ssl_prefer_server_ciphers on; so the server's preference wins over the client's. ssl_dhparam is no longer required when you only allow ECDHE suites, but if you do use it, generate at least 2048 bits with openssl dhparam -out dhparam.pem 4096. To avoid the cost of repeated full handshakes, set ssl_session_cache shared:SSL:10m; — about 40 000 sessions per megabyte — and tune ssl_session_timeout (default 5m) to values like 1d for browsers or 4h for APIs.
OCSP stapling lets the server fetch and sign the certificate's revocation status itself, so clients do not need to contact the CA mid-handshake. Enable it with ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 1.1.1.1 valid=300s;. HSTS (HTTP Strict Transport Security) tells browsers to use HTTPS only for a given period: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;. includeSubDomains extends the policy to every subdomain, so use it only after you have HTTPS on all of them; preload marks the site for inclusion in browsers' built-in HSTS preload list, which is hard to undo and requires a submission to hstspreload.org. The cleanest way to enforce HTTPS is a dedicated port-80 server that redirects every request: server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; }.
Certificates need to be renewed. Let's Encrypt issues 90-day certificates, and the standard tool is certbot: certbot --nginx -d example.com -d www.example.com edits the nginx configuration to serve the HTTP-01 challenge at /.well-known/acme-challenge/, obtains the certificate, and rewrites the HTTPS block. A daily cron with certbot renew --quiet --deploy-hook "systemctl reload nginx" keeps certificates fresh; after each renewal nginx is reloaded (or sent SIGHUP), which rotates workers and picks up the new certificate files without dropping connections — the same graceful pattern as any other configuration change.