Menu

Topic 2 of 8

Transport Security TLS

Learn Transport Security TLS for free with explanations, exercises, and a quick test (for API Engineer).

Published: January 21, 2026 | Updated: January 21, 2026

Why this matters

Every API request traverses networks you don't control. Transport Layer Security (TLS) protects those requests so attackers can’t read or alter them, and clients can verify they’re talking to your real service.

  • Protect user data in transit (confidentiality and integrity).
  • Prove your API’s identity (authenticity via certificates).
  • Meet compliance baselines (PCI, SOC 2, HIPAA often require strong TLS).
  • Enable secure internal traffic and service-to-service auth (mTLS).
  • Reduce downgrade risks, mixed content, and cookie theft with HSTS.

Real tasks you’ll face: harden TLS versions/ciphers, set HSTS correctly, rotate certificates without downtime, enable OCSP stapling, and configure mTLS for internal APIs.

Concept explained simply

TLS wraps your HTTP (or gRPC) traffic in encryption. During a handshake, the client and server agree on a protocol version and cipher suite, exchange keys, and verify the server’s identity with a certificate signed by a trusted Certificate Authority (CA).

  • Confidentiality: encryption prevents eavesdropping.
  • Integrity: message authentication detects tampering.
  • Authenticity: certificates prove the server (and in mTLS, the client) identity.
  • TLS 1.2/1.3: modern, secure versions. Disable 1.0/1.1.
  • Forward secrecy: ephemeral keys (ECDHE) ensure past traffic stays safe even if the key leaks later.
  • OCSP stapling: server provides recent revocation proof to clients.
  • HSTS: header that tells browsers to only use HTTPS for a period.
  • mTLS: both sides present certificates to mutually authenticate.
  • ALPN: negotiates HTTP/2 or HTTP/1.1 securely.
Mental model

Imagine handing a sealed envelope (your request) to a courier (the network). TLS is a tamper-evident, identity-verified lockbox: only the intended recipient can open it, and they prove who they are before you entrust the message. With mTLS, both sender and receiver show badges before the lockbox is exchanged.

Worked examples

1) Nginx: Strong TLS + HSTS

# /etc/nginx/conf.d/api.conf
server {
  listen 443 ssl http2;
  server_name api.example.com;

  ssl_certificate /etc/ssl/certs/api_chain.pem;      # full chain
  ssl_certificate_key /etc/ssl/private/api_key.pem;  # private key

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
  ssl_prefer_server_ciphers on;
  ssl_ecdh_curve X25519:secp256r1;

  # OCSP stapling
  ssl_stapling on;
  ssl_stapling_verify on;
  resolver 1.1.1.1 8.8.8.8 valid=300s; resolver_timeout 5s;

  # HSTS (enable only after confirming HTTPS works everywhere)
  add_header Strict-Transport-Security "max-age=15552000; includeSubDomains; preload" always;

  location / {
    proxy_pass http://upstream_api;
  }
}

server {  # Redirect HTTP to HTTPS
  listen 80;
  server_name api.example.com;
  return 301 https://$host$request_uri;
}

Quick checks:

  • curl -I https://api.example.com shows Strict-Transport-Security header.
  • curl --tlsv1.2 -I https://api.example.com returns 200/301; curl --tlsv1.1 should fail.
  • openssl s_client -connect api.example.com:443 -tlsextdebug -status shows OCSP response.

2) mTLS for an internal API (Nginx)

  1. Create a small internal CA and issue a client cert (see Exercise 2 for full commands).
  2. Configure server to trust the CA and require client certs.
  3. Test with curl using --cert/--key (success) and without (denied).
ssl_client_certificate /etc/ssl/ca/internal_ca.pem;
ssl_verify_client on;           # or 'optional' if you route based on cert presence
ssl_verify_depth 2;

location /internal/ {
  proxy_set_header X-Client-DN $ssl_client_s_dn;
  proxy_pass http://internal_upstream;
}

Result: only callers with a certificate issued by your CA can access /internal.

3) Zero-downtime certificate rotation

  1. Obtain new cert+key before expiry (automate renewal).
  2. Place files alongside the current ones with safe permissions.
  3. Point config to the new files or swap symlink atomically.
  4. Gracefully reload (e.g., nginx -s reload). No connection drop.
Command snippets
# Check expiry
openssl x509 -in /etc/ssl/certs/api_chain.pem -noout -enddate

# Dry-run config
nginx -t && nginx -s reload

# Verify live cert
openssl s_client -connect api.example.com:443 -servername api.example.com 

Who this is for

  • API engineers and backend developers deploying public or internal APIs.
  • DevOps/SREs responsible for ingress, proxies, and certificates.
  • Mobile/web engineers integrating with secure API endpoints.

Prerequisites

  • Basic HTTP knowledge (methods, headers, status codes).
  • Comfort with a web server or proxy (e.g., Nginx, Apache, Envoy, or Caddy).
  • Command line basics to run curl and openssl.

Learning path

  1. HTTPS basics and TLS handshake.
  2. Configure TLS 1.2/1.3 and secure cipher suites.
  3. Enable HSTS and OCSP stapling.
  4. Automate certificate issuance and rotation.
  5. mTLS for service-to-service auth (internal APIs).
  6. HTTP/2 and ALPN; plan for HTTP/3 later.
  7. Monitoring, alerting on expiry, and incident response (revocation).

Exercises

Complete these hands-on tasks. See solutions inside the collapsible panels. Tip: The Quick Test is available to everyone; only logged-in users get saved progress.

Exercise 1 — Harden a TLS endpoint

ID: ex1

Goal: Configure a web server (Nginx or Apache) to only allow TLS 1.2/1.3, enable OCSP stapling, and set HSTS. Verify with curl and openssl.

  • Set ssl_protocols to TLSv1.2 TLSv1.3 (or Apache SSLCipherSuite and SSLProtocol equivalents).
  • Enable OCSP stapling and a working DNS resolver.
  • Add Strict-Transport-Security with a safe max-age.
  • Test: curl --tls-max 1.3 -I https://your-host and curl --tls-max 1.1 should fail.
Show solution
# Nginx core bits
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128_GCM_SHA256';
ssl_ecdh_curve X25519:secp256r1;
ssl_stapling on; ssl_stapling_verify on; resolver 1.1.1.1 8.8.8.8;
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains; preload" always;

# Tests
curl -I https://host
curl --tls-max 1.1 -I https://host  # should fail
openssl s_client -connect host:443 -status -servername host | grep -E "OCSP Response Status|Verify return code"

Exercise 2 — mTLS for an internal path

ID: ex2

Goal: Require client certificates for /internal and pass client DN upstream. Verify access works with a valid client cert and fails without one.

  1. Create a CA and a client certificate.
  2. Configure the server with ssl_client_certificate and ssl_verify_client on.
  3. Test with curl using --cert and --key. Repeat without flags to see denial.
Show solution
# 1) Minimal CA and client cert (demo only)
openssl genrsa -out internal_ca.key 4096
openssl req -x509 -new -nodes -key internal_ca.key -sha256 -days 3650 -subj "/CN=Internal CA" -out internal_ca.pem

openssl genrsa -out client.key 2048
openssl req -new -key client.key -subj "/CN=svc-a" -out client.csr
openssl x509 -req -in client.csr -CA internal_ca.pem -CAkey internal_ca.key -CAcreateserial -out client.crt -days 365 -sha256

# 2) Nginx bits
ssl_client_certificate /etc/ssl/ca/internal_ca.pem;
ssl_verify_client on;
ssl_verify_depth 2;
location /internal/ {
  proxy_set_header X-Client-DN $ssl_client_s_dn;
  proxy_pass http://internal_upstream;
}

# 3) Tests
curl --cert client.crt --key client.key https://host/internal/ -k -I
curl https://host/internal/ -k -I   # should be 400/401/403 depending on config

Self-check checklist

  • [ ] TLS 1.0/1.1 disabled.
  • [ ] HSTS header present on HTTPS responses only.
  • [ ] OCSP stapling returns good status.
  • [ ] mTLS route denies requests without valid client cert.
  • [ ] Expiry monitoring in place; rotation can happen without downtime.

Common mistakes and how to self-check

  • Leaving TLS 1.0/1.1 enabled. Self-check: curl --tls-max 1.1 should fail.
  • Weak ciphers (no forward secrecy). Self-check: openssl s_client -connect host:443 -cipher EXPORT or RC4 should fail.
  • HSTS misconfig (sent on HTTP or before HTTPS readiness). Self-check: only on HTTPS, confirm redirects and subdomains support before preload.
  • Forgetting OCSP stapling. Self-check: openssl s_client -status shows stapled response.
  • Poor key management (keys in repos/logs). Self-check: verify permissions and secret storage; rotate on suspicion.
  • mTLS CA trust not scoped. Self-check: only trust your internal CA, not public roots.
  • No expiry alerts. Self-check: automated check under 30 days triggers alerts.
  • Missing SNI handling (multi-domain). Self-check: s_client -servername host shows correct cert.
  • Forgetting ALPN for HTTP/2. Self-check: curl -I --http2 https://host returns HTTP/2.

Practical projects

  • Build a hardened HTTPS ingress with auto-renewal and OCSP stapling; add alerts for certificate expiry under 15 days.
  • Protect an internal admin API with mTLS and propagate client identity via headers.
  • Stage a certificate rotation playbook and prove zero downtime with load testing.
  • Add HSTS safely: rollout plan, verify subdomain coverage, then consider preload.

Next steps

  • Integrate TLS checks into CI (lint configs, run curl/openssl probes).
  • Extend to HTTP/3 (QUIC) in staging once TLS 1.3 is stable.
  • Combine transport security with auth: OAuth2/JWT, key management, and rate limiting.

Mini challenge

Your public API must enforce strong TLS, but a small legacy partner needs TLS 1.0 for 60 days. Design a plan that keeps your main security posture strong while accommodating them temporarily.

One possible approach
  • Keep primary domain on TLS 1.2/1.3 only with HSTS.
  • Create a segregated legacy endpoint on a distinct subdomain and dedicated ingress with strict network ACLs, rate limits, and traffic monitoring.
  • Sign a short-term agreement, set firm deprecation date, and send weekly reminders.
  • Disable weak ciphers even on legacy if possible; prefer the least-bad TLS 1.0 suite; monitor for abuse.
  • Decommission the legacy endpoint on schedule.

Quick Test

Take the quick test to check your understanding. Everyone can take it; only logged-in users will have their progress saved.

Practice Exercises

2 exercises to complete

Instructions

Configure your web server to allow only TLS 1.2/1.3, enable OCSP stapling, and add an HSTS header. Verify with curl and openssl.

  • Set TLS versions and strong ciphers.
  • Enable OCSP stapling and a DNS resolver.
  • Add Strict-Transport-Security with a safe max-age.
  • Test: curl -I https://your-host shows HSTS; curl --tls-max 1.1 fails; openssl s_client -status shows stapled OCSP.
Expected Output
curl --tlsv1.2 -I https://host returns 200/301; curl --tls-max 1.1 fails; response includes Strict-Transport-Security; openssl s_client shows 'OCSP Response Status: successful' and 'Verify return code: 0 (ok)'.

Transport Security TLS — Quick Test

Test your knowledge with 10 questions. Pass with 70% or higher.

10 questions70% to pass

Have questions about Transport Security TLS?

AI Assistant

Ask questions about this tool