env.dev

HTTPS and TLS Explained: Certificates, Handshake & HSTS

Understand HTTPS, TLS certificates, the TLS handshake, cipher suites, Let's Encrypt, and HSTS for secure web communication.

Last updated:

HTTPS is HTTP over TLS — every byte between client and server is encrypted, authenticated, and integrity-checked. TLS (Transport Layer Security) replaced SSL 3.0 in 1999, yet the term "SSL" persists colloquially. As of 2024, over 95% of web traffic is encrypted via HTTPS according to Google's Transparency Report. TLS 1.3 (RFC 8446), finalized in 2018, reduced the handshake from 2 round-trips to 1 and removed all legacy cipher suites, making connections both faster and more secure.

What Is the Difference Between TLS and SSL?

SSL (Secure Sockets Layer) was developed by Netscape in the 1990s. SSL 3.0 was the last SSL version before it was superseded by TLS 1.0 in 1999. SSL 2.0 and 3.0 have known vulnerabilities (POODLE, DROWN) and are prohibited by RFC 7568. In practice, when someone says "SSL certificate" they mean a TLS certificate. Modern servers should support only TLS 1.2 and TLS 1.3.

ProtocolYearStatus
SSL 2.01995Prohibited (insecure)
SSL 3.01996Prohibited (POODLE attack)
TLS 1.01999Deprecated (2020)
TLS 1.12006Deprecated (2020)
TLS 1.22008Supported — widely used
TLS 1.32018Recommended — current standard

How Does the TLS Handshake Work?

The TLS handshake establishes a secure connection before any application data is exchanged. TLS 1.3 simplified this to a single round-trip (1-RTT), and supports 0-RTT resumption for repeat connections.

TLS 1.3 handshake (1-RTT)
Client                                    Server
  │                                          │
  │  ClientHello                             │
  │  + supported cipher suites               │
  │  + key_share (ECDHE public key)          │
  │  + supported_versions: TLS 1.3           │
  │ ──────────────────────────────────────►   │
  │                                          │
  │  ServerHello                             │
  │  + selected cipher suite                 │
  │  + key_share (server ECDHE public key)   │
  │  {EncryptedExtensions}                   │
  │  {Certificate}                           │
  │  {CertificateVerify}                     │
  │  {Finished}                              │
  │  ◄──────────────────────────────────────  │
  │                                          │
  │  {Finished}                              │
  │ ──────────────────────────────────────►   │
  │                                          │
  │  ═══ Application Data (encrypted) ═══   │

After key exchange, both sides derive symmetric session keys using HKDF. All application data is encrypted with AEAD ciphers (AES-128-GCM, AES-256-GCM, or ChaCha20-Poly1305 in TLS 1.3).

What Are TLS Certificate Types?

Certificates are issued by Certificate Authorities (CAs) and come in three validation levels. The encryption strength is identical across all types — the difference is how thoroughly the CA verifies the applicant's identity.

TypeValidationIssuance timeUse case
DV (Domain Validation)Domain ownership onlyMinutesBlogs, APIs, most websites
OV (Organization Validation)Domain + organization checkDaysBusiness websites
EV (Extended Validation)Domain + legal entity auditWeeksBanks, government portals
Wildcard (*.example.com)Domain (any subdomain)Minutes–DaysMulti-subdomain setups
SAN / Multi-domainMultiple specific domainsMinutes–DaysShared hosting, CDNs

How Does Let's Encrypt Work?

Let's Encrypt is a free, automated CA that issues DV certificates using the ACME (Automatic Certificate Management Environment) protocol. It has issued over 4 billion certificates since launching in 2015. Certificates are valid for 90 days, encouraging automation.

Certbot (Let's Encrypt client)
# Install certbot
sudo apt install certbot python3-certbot-nginx

# Obtain and auto-configure for Nginx
sudo certbot --nginx -d example.com -d www.example.com

# Obtain certificate only (manual installation)
sudo certbot certonly --standalone -d example.com

# Auto-renewal (usually set up by default)
sudo certbot renew --dry-run

# Certificate files location
# /etc/letsencrypt/live/example.com/fullchain.pem  (cert + intermediates)
# /etc/letsencrypt/live/example.com/privkey.pem    (private key)

What Is HSTS and Why Does It Matter?

HTTP Strict Transport Security (HSTS) tells browsers to only connect via HTTPS, even if the user types http://. This prevents SSL-stripping attacks where a man-in-the-middle downgrades the connection to plain HTTP.

HSTS header
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

The preload directive lets you submit your domain to browser HSTS preload lists (hstspreload.org), hardcoding HTTPS enforcement before the first visit. Once preloaded, removal takes months — only enable this when you are certain all subdomains support HTTPS.

What Is Certificate Pinning?

Certificate pinning binds a host to a specific certificate or public key, rejecting connections even if a valid CA-signed certificate is presented by a different key. This defends against compromised CAs. However, HTTP Public Key Pinning (HPKP) was deprecated in 2018 because misconfiguration caused permanent site lockouts. Modern alternatives include Certificate Transparency (CT) logs and CAA DNS records.

CAA is a regular DNS record type — see DNS explained for how to publish it alongside your A/AAAA and MX records.

CAA DNS record (limits which CAs can issue)
# Only Let's Encrypt and Cloudflare can issue certificates for this domain
example.com.  CAA  0 issue "letsencrypt.org"
example.com.  CAA  0 issue "digicert.com"
example.com.  CAA  0 iodef "mailto:security@example.com"

What Are Cipher Suites?

A cipher suite is a combination of algorithms used for key exchange, authentication, bulk encryption, and message authentication. TLS 1.3 drastically simplified the options to five secure suites.

TLS 1.3 cipher suites
TLS_AES_128_GCM_SHA256         # Most common, excellent performance
TLS_AES_256_GCM_SHA384         # 256-bit key, higher security margin
TLS_CHACHA20_POLY1305_SHA256   # Faster on devices without AES-NI
TLS_AES_128_CCM_SHA256         # Constrained IoT environments
TLS_AES_128_CCM_8_SHA256       # Minimal overhead IoT variant

For a full walkthrough of the surrounding directives (server blocks, reverse-proxy headers, gzip/brotli, rate limiting), see the Nginx configuration guide.

Recommended Nginx TLS configuration
server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    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;
    ssl_prefer_server_ciphers off;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

What Is mTLS (Mutual TLS)?

Mutual TLS (mTLS) extends the standard handshake so the client also presents a certificate, letting the server cryptographically verify caller identity instead of relying on bearer tokens or shared secrets. It is the default trust model for service meshes (Istio, Linkerd, Consul) and zero-trust platforms (Cloudflare Access, Tailscale) — every internal request is authenticated end-to-end. For public APIs, mTLS is a strong way to lock down high-value endpoints (banking, B2B webhooks) where you control the client.

Nginx mTLS — require a client certificate signed by your CA
server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate     /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    # Trust anchor for client certs (your internal CA bundle)
    ssl_client_certificate /etc/nginx/ca/clients-ca.pem;
    ssl_verify_client      on;          # reject the connection if no valid client cert
    ssl_verify_depth       2;
}

What Are Common TLS Pitfalls?

  • Mixed content. An HTTPS page that loads a script, image, or iframe over http:// is blocked by every modern browser. Always use protocol-relative or absolute https:// URLs, and set a Content-Security-Policy: upgrade-insecure-requests header to silently rewrite legacy assets.
  • Expired certificates with no monitoring. Let's Encrypt certs are 90 days; many outages are simply "cron didn't run." Monitor expiry externally (uptime checks, openssl s_client -servername host -connect host:443 in CI) and alert at T-14 days, not T-0.
  • Missing intermediate chain. Serving only the leaf certificate works in Chrome (which fetches intermediates) but breaks curl, Java clients, and older mobile devices. Always serve fullchain.pem, never cert.pem alone.
  • Weak DH params. If you still use static DH key exchange, generate a 2048-bit (or larger) parameter file with openssl dhparam -out dhparam.pem 2048 and reference it via ssl_dhparam. Better: drop static DH entirely and rely on ECDHE-only suites.
  • ssl_prefer_server_ciphers on on TLS 1.3. The directive is meaningful for TLS 1.2 only; in TLS 1.3 cipher selection is fixed by the spec, so leaving it on does nothing useful and can mask a misconfigured 1.2 cipher list. Set it to off on modern stacks.

References

What's the Minimum TLS Setup for Production?

  • • TLS replaced SSL decades ago — disable SSL 3.0 and TLS 1.0/1.1 on all servers
  • • TLS 1.3 is the current standard: 1-RTT handshake, no legacy ciphers, forward secrecy by default
  • • Use Let's Encrypt with automated renewal for DV certificates
  • • Enable HSTS to prevent downgrade attacks; consider preloading for critical domains
  • • Use CAA records instead of certificate pinning to restrict which CAs can issue for your domain
  • • Test your TLS configuration at ssllabs.com/ssltest — aim for an A+ rating
Was this helpful?

Read next

Env Variables Security: Secrets, Leaks & Best Practices

Why environment variables are not truly secure and what to do about it: secret rotation, leak detection, client-side risk, and secrets managers.

Continue →

Frequently Asked Questions

What is the difference between SSL and TLS?

TLS (Transport Layer Security) is the modern successor to SSL (Secure Sockets Layer). SSL is deprecated and insecure. When people say "SSL", they usually mean TLS. Use TLS 1.2 or 1.3 — disable everything older.

How does the TLS handshake work?

The client sends supported cipher suites, the server responds with a certificate and chosen cipher, the client verifies the certificate, both sides derive session keys using asymmetric crypto, then switch to fast symmetric encryption.

What is HSTS?

HTTP Strict Transport Security tells browsers to always use HTTPS for your domain. Set the Strict-Transport-Security header with a max-age value. This prevents SSL stripping attacks and accidental HTTP connections.

How do I check my TLS configuration?

Run your hostname through Qualys SSL Labs at ssllabs.com/ssltest for an external A+ scorecard covering protocols, ciphers, certificate chain, and HSTS. For deeper local checks (or pre-deploy in CI), use the open-source testssl.sh script — it probes every supported cipher, runs known-attack tests (Heartbleed, ROBOT, LOGJAM), and exits non-zero on findings.

Stay up to date

Get notified about new guides, tools, and cheatsheets.