Shared versus personalised
Draw the useful dividing line between shared and personalised responses, not between static and dynamic file types.
An image is the obvious edge citizen, and so is a rendered product page, a public listing of categories, or yesterday's sports scores.
Apply the test. If ten thousand people would receive identical bytes, those bytes belong at the edge with a suitable lifetime, even though a server generated them two minutes ago.
Do the arithmetic on a short lifetime. Even five seconds on a hot endpoint collapses thousands of origin requests into one per location per five seconds. CDNs also serve the expiring copy while one background fetch refreshes it.
What cannot be cached whole
Keep personalised responses out entirely, your cart, your feed, your account page, because the classic failure is trying. Cache a page containing a signed-in name and your second visitor sees the first one's name.
Mark anything carrying a session or setting a cookie as private, and rely on the fact that CDNs default to not caching cookied responses for exactly this reason.
Split your responses along that shared and personal line, which is the actual game. Cache the page shell at the edge and fetch the personal fragment, the cart count or the name, with a small call after load.
Or render anonymously for logged-out people, who are often most of your traffic, and bypass the cache for the logged-in minority based on their session cookie.
Keep the CDN even for the entirely uncacheable, because it still pays rent. Your requests ride a private backbone from the edge to your origin over warm connections, skipping congested public paths. Encryption terminates ten milliseconds from your user instead of two hundred.
Expect that alone to take 30 percent or more off cross-continent latency, without caching a single byte.
Worked example
Sanjana runs engineering for a recipe site: 4 million pages, 92 percent of traffic logged out, origin in Virginia struggling at peak. Recipe pages were marked uncacheable because the header shows a username for logged-in users. The rebuild splits the page: recipe HTML is rendered without any personal data and cached at the edge with s-maxage=300 plus stale-while-revalidate, while a 2 KB /api/me call fills in the header client-side, cache bypassed only when a session cookie exists on that call. Origin traffic falls 89 percent, and p50 page load in India goes from 2.1 seconds to 600 ms. The one bug in rollout is instructive: a cached page briefly included a CSRF token, letting one user submit with another's token, fixed by moving token issuance into the same uncached /api/me response.