Security

TLS termination at the CDN: the origin certificate you forgot you had

July 15, 20269 min readCertPulse Engineering

The connection you think is one connection is actually two

When you put a CDN in front of your origin, the padlock in the browser stops telling you what you think it tells you. It confirms that the browser negotiated a valid TLS session with the edge. That's it. The edge (Cloudflare, Fastly, CloudFront, Akamai, whatever) terminated that session, decrypted the request, and then opened a second TLS connection to your origin to fetch the actual response. Two handshakes. Two certificates. One of them you watch obsessively. The other one you probably haven't looked at since you set it up.

That's the whole problem, in one sentence. And that second certificate, the one you forgot, is where the outages and the quiet security holes live.

The public-facing certificate, the one whose Subject matches your domain and that browsers validate, is issued and renewed by the CDN. On Cloudflare it's the Universal SSL cert, auto-provisioned and rotated on their schedule. On CloudFront you point it at ACM. Fastly manages it through their TLS subscriptions. You don't hold the private key, you don't run the renewal, and honestly you don't need to think about it much. The CDN discovers it, renews it, staples OCSP, and keeps the padlock green. Good. That's the value you're paying for.

The origin certificate is the one nobody adopted. It sits on your load balancer or your web server, it terminated the CDN-to-origin leg of the connection, and the moment the public endpoint went green in your monitoring, the entire org stopped thinking about it. It has no browser looking at it. It has no CDN renewing it. It has an expiry date that is coming whether you remember it or not.

The SSL-mode trap

Here's where it gets actually dangerous, and it's dangerous precisely because it looks fine.

Every CDN gives you a setting for how it connects to your origin. Cloudflare calls the levels Flexible, Full, and Full (Strict). The names sound like a difficulty slider. They're actually a description of how much of your backend security you've turned off.

Flexible means the CDN talks to your origin over plain HTTP. The browser sees HTTPS, feels safe, and the CDN-to-origin hop is cleartext across whatever network sits between the edge and your box. If that path crosses the public internet, and for a lot of origins it does, you've built a padlock that protects the first half of the trip and abandons the second. Don't use Flexible. I shouldn't have to say it, but I've inherited environments running it, so I'm saying it.

Full means the CDN connects to your origin over TLS, but here's the part that bites people: it does not validate the origin certificate. Expired? Accepted. Self-signed? Accepted. Wrong hostname? Accepted. A certificate presented by someone who isn't your origin at all? Accepted. Full encrypts the origin leg but authenticates nothing, which means the CDN-to-origin connection is trivial to machine-in-the-middle for anyone who can get on the path. The public endpoint looks immaculate. The backend is wide open. This is the trap: "Full" sounds like the complete option, and teams pick it, and it silently tolerates a rotting or fraudulent origin cert forever.

Full (Strict) is the one you actually want. The CDN validates the origin certificate: it must be unexpired, it must chain to a trusted CA (or to the CDN's own origin CA), and the hostname must match. This is the mode that turns your origin certificate back into something that matters. It's also, not coincidentally, the mode that will take your site down the instant that forgotten origin cert expires.

The equivalents exist everywhere. Fastly lets you set the TLS configuration on the backend and choose whether to check the cert and pin against a CA; skip that and you've rebuilt Full. CloudFront has an Origin Protocol Policy plus origin SSL settings, and while it validates public-CA origin certs by default for custom origins, misconfigured or self-signed origins are a well-worn footgun. Akamai's Origin Server settings and the SureRoute/verification options give you the same fork: verify the origin cert, or don't. Different vocabularies, identical decision. Somewhere in every one of these products is a checkbox that says "actually check the certificate on the other end," and it is not always on by default.

Blind on both sides

The reason origin certs rot is structural, not careless. You're blind from two directions at once.

You can't fully manage the edge cert because you don't own it. You can inspect what the CDN presents publicly, but you can't drive its renewal, you can't see its private key handling, and you're trusting the provider's automation. That's usually a fine trust to extend, since CDNs are good at this, but it means the edge cert falls outside your normal certificate lifecycle tooling. It's not in your ACM inventory. It's not in your internal PKI. It's theirs.

And nobody probes the origin, because from the outside the origin is invisible. Your uptime checks hit the CDN. Your synthetic monitors hit the CDN. Your certificate scanner, if you have one, resolves your domain, gets the CDN's anycast IP, and reports on the edge cert, the healthy one. The origin's certificate never enters the picture because no default monitoring path ever reaches it.

Then there's the detail that makes this a slow-motion trap: origin certs are built to be forgotten. Cloudflare's Origin CA will hand you a certificate valid for fifteen years. That's a deliberate design choice, not an oversight. The origin leg is meant to be set-and-forget while the public cert churns on the industry's shrinking schedule. Which is lovely until you consider what fifteen years of not-thinking-about-it does to institutional memory. The person who installed it has left. The runbook doesn't mention it. The Terraform that provisioned it got refactored twice. And the cert sits there, valid, unwatched, on Full mode, doing nothing to authenticate anything.

The failure finally surfaces one of two ways. Either that fifteen-year cert actually expires and someone on Full (Strict) gets a hard 526 outage at an inconvenient hour. Or, worse, the cert already expired years ago, nobody noticed because you were on Full, and the day someone finally does the responsible thing and flips to Strict to close the MITM gap, the site goes down instantly. The security hardening trips over the neglect it was supposed to fix. I've watched that exact sequence: a good engineer improving origin validation, taking prod down, and getting blamed for the outage that a long-dead certificate actually caused.

ACME gets weird behind a proxy

If your instinct is "fine, I'll just put Let's Encrypt on the origin and stop worrying," the CDN complicates that too, and it's worth understanding why before certbot leaves you confused.

HTTP-01 validation breaks because the challenge is served over HTTP on port 80 at your domain, and your domain resolves to the CDN. The ACME server's request for the challenge token hits the edge, not your origin. Depending on your CDN config it gets cached, redirected, rewritten, or served from an edge that has no idea what token you provisioned on the backend. You'll watch the challenge fail and swear the file is right there on the server, because it is. The validator just never reached the server.

Proxied DNS records break naive assumptions the same way. When a record is proxied (orange-cloud in Cloudflare terms), the DNS answer is the CDN's IP, so anything that reasons about your origin from a public DNS lookup gets the wrong answer.

DNS-01 is the path that works. It validates by placing a TXT record under _acme-challenge, which has nothing to do with where your A record points or what the CDN does to HTTP traffic. The proxy is irrelevant to a TXT lookup. If your DNS provider has an API (and if you're already on a CDN, it does), you automate DNS-01 and issue real, short-lived, publicly-trusted origin certs behind the proxy without fighting the edge. That also lets you drop the fifteen-year Origin CA cert in favor of something on a normal renewal cadence, so it fails loudly and often instead of silently and once, and loud-and-often is a property you want in a certificate. It's the difference between a cron job you'll notice breaking and a landmine with a fifteen-year fuse.

What to actually watch

Two probes. That's the shape of the fix.

Probe the public endpoint the normal way: resolve the domain, connect, and check the chain, the expiry, the negotiated TLS version and cipher. This watches the edge cert. It'll usually be healthy because the CDN is renewing it, but you want to know immediately if the provider's automation ever hiccups, and you want a record of what the public world actually sees.

Probe the origin directly, and this is the leg your CDN dashboard will never show you. Connect straight to the origin's IP and set the Host header to your real hostname. Bypass the CDN entirely and force the origin to present its certificate to you the same way it presents it to the edge. Now you can read the origin cert's real expiry, its chain, whether it's self-signed, whether the hostname even matches. This is the check that catches the fifteen-year cert with three weeks left on it. This is the check that tells you you're on Full instead of Full (Strict) before an attacker or an audit does.

Then alert on the origin leg silently downgrading: an origin cert that flips to self-signed, an expiry marching toward zero, a chain that stopped validating, a TLS version that quietly dropped. Silent is the word that matters here. Every one of these degrades without touching the public padlock, so if you're only watching the front door you find out from an outage or a pentest, and neither is a good way to learn.

This is the exact gap CertPulse sits in. Point it at the public hostname for the edge cert and at the origin IP with a Host override for the leg your CDN hides, and both certificates land in one inventory with expiry thresholds and downgrade alerts on each. The origin cert stops being the one nobody owns and becomes a row you actually watch.

Every CDN is two certificates. Put both on your radar, run Full (Strict) and not Full, issue origin certs with DNS-01 on a short cadence so they fail loud, and probe the origin directly. Then the leg behind the padlock is something you monitor, not something you rediscover at 2am.

This is why we built CertPulse

CertPulse connects to your AWS, Azure, and GCP accounts, enumerates every certificate, monitors your external endpoints, and watches Certificate Transparency logs. One dashboard for every cert. Alerts when auto-renewal fails. Alerts when certs approach expiry. Alerts when someone issues a cert for your domain that you didn't request.

If you're looking for complete certificate visibility without maintaining scripts, we can get you there in about 5 minutes.

TLS termination at the CDN: the origin certificate you forgot you had | CertPulse