SSL/TLS Certificates: What Developers Should Know
Here’s a classic outage: a production site goes down at 2 a.m. because its SSL certificate expired. The renewal-failure emails had been going to an engineer who left months earlier, the certificate was provisioned by hand with no monitoring, and the first sign of trouble was customers seeing a red “Your connection is not private” warning.
It’s entirely preventable — but preventing it means understanding how certificates actually work at the protocol level, not just which “Enable SSL” checkbox to tick in a dashboard.
This guide covers the TLS handshake, the Certificate Authority chain of trust, debugging missing intermediate certificates with openssl, and hardening Nginx.
Stop guessing why staging throws SSL warnings. Run a diagnostic on any domain with our local SSL Certificate Checker to inspect the full chain of trust and cipher suites in the browser.
1. The TLS Handshake
When a browser connects to https://api.example.com, a multi-step negotiation — the TLS handshake — runs before any HTTP data is sent:
1. ClientHello: Browser → Server
"I support TLS 1.3 and can encrypt with these cipher suites."
2. ServerHello: Server → Browser
"We'll use TLS 1.3 with AES-256-GCM. Here's my certificate to prove my identity."
3. Validation (the browser checks):
- Is the current time within the certificate's 'Valid From' / 'Valid To' range?
- Does the domain match the certificate's Subject Alternative Name (SAN)?
- Was the certificate signed by a Root Authority the OS trusts?
- Is the intermediate chain complete and verifiable?
4. Key generation:
Both sides use Diffie-Hellman Ephemeral (DHE) to derive identical session keys without ever sending the key over the wire.
5. Encrypted:
The connection is now encrypted. HTTP traffic begins.
The certificate is what makes step 3 possible — it’s the server’s identity proof, like a passport issued by a trusted authority.
2. Anatomy of a Certificate
Decode a .pem or .crt file and you get a structured object:
Subject: CN=api.example.com
Subject Alternative Names (SANs): api.example.com, www.example.com, legacy.example.com
Issuer: Let's Encrypt Authority X3
Valid From: 2026-01-15T00:00:00Z
Valid To: 2026-04-15T23:59:59Z
Public Key: RSA 2048 bits
Signature Algorithm: SHA-256 with RSA Encryption
Serial Number: 04:a3:b2:98:c7:12:ef...
The key fields:
| Field | Purpose |
|---|---|
| Subject / SAN | The domains this certificate is valid for. Connect to dev.example.com and if it’s not on the SAN list, the browser errors. |
| Issuer (CA) | The authority that signed it (DigiCert, Cloudflare, Let’s Encrypt). |
| Validity range | The timestamps when the certificate becomes active and when it expires. |
| Public Key | The key used to encrypt the initial handshake. |
| Signature Algorithm | The Issuer’s hashing algorithm. SHA-1 is deprecated and broken — it must be SHA-256 or better. |
3. The Three Validation Levels
A. Domain Validation (DV)
The CA uses an automated protocol (like ACME) to verify you control the domain — usually by having you place a token in a DNS TXT record or an HTTP path.
- Reality: this is what Let’s Encrypt provides. It’s sufficient and recommended for almost all web apps and APIs.
B. Organization Validation (OV)
The CA verifies domain control and the organization’s legal identity via business registries and phone verification.
- Reality: $50–$200/year. It adds no cryptographic security over DV — it just puts your company name in the certificate details, which users rarely check.
C. Extended Validation (EV)
The most thorough check — the CA verifies the company’s legal existence, address, and operational status.
- Reality: EV used to trigger the green address bar, but browsers removed that UI years ago. EV is now visually identical to a free DV certificate, so it’s wasted budget unless a specific compliance audit requires it.
4. Let’s Encrypt and ACME
Let’s Encrypt issues free DV certificates through the automated ACME (Automated Certificate Management Environment) protocol.
Its certificates expire every 90 days — deliberately. The short lifespan forces you to automate renewal, which eliminates the human error of forgetting to renew a two-year manual certificate.
Certbot + Nginx
# 1. Install the certbot client and Nginx plugin
sudo apt update
sudo apt install certbot python3-certbot-nginx
# 2. Fetch the certificate and patch the Nginx config automatically
sudo certbot --nginx -d example.com -d www.example.com -d api.example.com
# 3. Test the automated renewal
sudo certbot renew --dry-run
DNS challenge (for wildcards)
For a wildcard certificate (*.example.com), you can’t use the HTTP path challenge — you need a DNS challenge, where certbot puts a token in your DNS records.
# Using the Cloudflare DNS API plugin for hands-free wildcard renewal
sudo certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d "*.example.com" -d example.com
5. The Chain of Trust and the Missing Intermediate
Browsers don’t trust your server’s leaf certificate directly. They trust a small list of Root CAs preinstalled in the OS (macOS Keychain, Windows Certificate Store).
Because Root CAs are kept offline for security, they don’t sign your certificate directly. They sign an Intermediate CA, which signs yours.
The chain:
1. Root CA (trusted by the OS)
└── 2. Intermediate CA (signed by the Root)
└── 3. Your leaf certificate (signed by the Intermediate)
The rule: your server must send the full chain (your certificate + the intermediate) in a single bundle during the handshake.
The common bug: a missing intermediate
If Nginx is configured to send only the leaf certificate (cert.pem) instead of the full chain (fullchain.pem), the setup is broken.
It can still seem to work in desktop Chrome. Why? Modern desktop browsers have a fallback called AIA Fetching (Authority Information Access): if the intermediate is missing, the browser pauses, downloads it from the internet, and resumes.
The catch: AIA fetching is not supported by many mobile browsers, older Android versions, or API clients (curl, Python requests, Node axios).
That creates a confusing bug: the site works in desktop Chrome, but the mobile app and the Python API integration fail with CERTIFICATE_VERIFY_FAILED. Always serve fullchain.pem.
6. Debugging TLS from the CLI
When an API won’t connect, bypass the browser and inspect the handshake with openssl.
# 1. Inspect the certificate details from the server
openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null | openssl x509 -text -noout
# 2. Check just the expiration dates
echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null | openssl x509 -noout -dates
# 3. Verify the server sends the FULL CHAIN (0, 1, 2)
openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts
7. Hardening Nginx TLS
Default Nginx SSL configs are weak out of the box. Harden them.
A. Drop legacy protocols
# Disable the broken TLS 1.0 and 1.1 (RFC 8996). Allow only modern protocols.
ssl_protocols TLSv1.2 TLSv1.3;
B. Use strong cipher suites
# Let the server dictate cipher order to prevent downgrade attacks.
ssl_prefer_server_ciphers on;
# Prefer modern ECDHE ciphers.
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
C. Enable OCSP stapling
Normally, the browser pauses the handshake to ask the CA’s OCSP server, “Has this certificate been revoked?” That adds latency and leaks the user’s browsing to the CA.
OCSP stapling fixes this: your server pings the CA every few hours, caches the “not revoked” signature, and staples it to the certificate during the handshake — so the browser doesn’t have to make the external request.
# Enable OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
# DNS resolvers so Nginx can reach the CA
resolver 8.8.8.8 1.1.1.1 valid=300s;
resolver_timeout 5s;
Further Reading
- HTTP Security Headers: HSTS and Content Security Policy
- DNS Records Explained: Architecture of the Internet
- CORS Errors Explained: Securing Browser Network Boundaries
- Base64 Encoding Mistakes: JWT Architecture Failures
Stop guessing at your TLS setup. Use our SSL Certificate Checker to decode SAN fields, verify the full chain, and check expiration dates. Pair it with the DNS Lookup Tool to confirm your CAA records lock down authorized issuers.