Every time your CDN edge opens a brand-new connection to your origin to fetch an uncached object, it pays for a TCP handshake and a full TLS handshake before a single byte of your content moves. On a cache miss under load, that tax is paid thousands of times a second. Origin keepalive and connection pooling let the edge reuse warm connections instead — and it is one of the cheapest latency wins available. This guide is how to turn it on and size it without starving the origin.
What a cold origin connection actually costs
When a request misses the edge cache, the edge has to reach your origin. If it opens a fresh connection to do so, three things happen before your content moves: a TCP handshake (one round trip), a TLS handshake (one to two more round trips depending on version and resumption), and only then the actual HTTP request. On a long edge-to-origin path — which is common, because your origin is in one region and the edge is everywhere — those round trips dominate the time-to-first-byte for that object.
Multiply by scale and it stops being academic. A busy origin serving misses, dynamic responses, or personalised content fields a continuous stream of these fetches. If each one pays a full handshake, you are burning round trips and origin CPU (TLS handshakes are computationally expensive) on setup work that a warm, reused connection would skip entirely. The fix is to stop tearing the connections down.
Keepalive, pooling, and who controls them
Two related mechanisms do the work. Keepalive keeps a connection open after a response instead of closing it, so the next request can reuse it. Connection pooling is the edge maintaining a set of these warm, open connections to your origin and handing incoming misses to an idle one. Together they turn “handshake on every fetch” into “handshake occasionally, reuse constantly.”
On a managed CDN, the edge side of this is usually the provider's job — most keep a warm pool to your origin automatically — but the origin side is yours, and it has to agree. If your origin server closes connections aggressively (a low keepalive timeout, a low max-requests-per-connection, or Connection: close in responses), it overrides the edge's intent and forces new handshakes anyway. So the practical work is making sure your origin is configured to allow long-lived, high-reuse connections, and that any load balancer or reverse proxy in front of it does too.
Sizing the pool without starving the origin
There is a tension to balance. Too few keepalive connections and the edge falls back to opening fresh ones under load, losing the benefit exactly when you need it. Too many and you can exhaust origin resources — every held-open connection consumes a file descriptor and memory, and a very large pool of idle connections can crowd out capacity for real work. The origin's keepalive settings should be sized to your actual concurrency: enough headroom that steady-state misses reuse connections, without holding open far more than your peak genuinely uses.
Raise the per-connection request ceiling too. Many origin defaults cap how many requests a single connection will serve before closing — a conservative default that defeats the whole point. If a connection is healthy, let it serve many requests before recycling. The goal is that under normal load the edge almost never has to open a cold connection, because there is always a warm one free.
The idle-timeout handshake between edge and origin
The subtle trap is a timeout mismatch. Every keepalive connection has an idle timeout — how long it stays open with no traffic before being closed. If the origin's idle timeout is shorter than the interval at which the edge reuses the connection, the origin will close a connection the edge still thinks is warm. The edge then sends a request into a dead connection, has to retry on a fresh one, and you get errors or latency spikes that are maddening to diagnose because they only appear under specific traffic gaps.
The rule: the origin's keepalive idle timeout should be equal to or longer than the edge's, never shorter. If your provider documents its origin-side idle timeout, set yours above it. If it does not, set a generous origin timeout and watch for connection-reset errors, which are the signature of this mismatch. This one detail causes more “random” origin errors than almost anything else in this area.
Measuring the reuse rate
You cannot manage what you do not measure, and the metric here is the connection reuse rate: what fraction of origin fetches ride an existing connection versus opening a new one. Your origin's access logs or metrics can usually expose new-connection counts against request counts; a healthy reused setup shows many requests per connection, a broken one shows roughly one. Watch it under peak load, not at rest, because the failure mode is the pool running dry exactly when traffic is highest.
Pair the before/after with your time-to-first-byte on cache misses specifically, since that is where this shows up — a cache hit never touches the origin path at all. If you are tuning this alongside protocol work, keep the changes separate so you can attribute the win, and revisit cache hit ratio first, because the cheapest origin connection is the one you never make. This also pairs naturally with securing the origin with mutual TLS, since both are about how the edge and origin talk to each other.
