# Our whole site sits behind a CDN, where does personalization fit?

Asked by Bastien Girard on 2025-04-08. Tags: performance, cdn, caching, ssr.

I do platform engineering at a travel site. Our request path today is simple:

```
browser -> CDN (full HTML cache, ~97% hit ratio) -> origin
```

Every page is the same for everyone, which is exactly why the hit ratio is so
good. Marketing now wants personalized modules on the home page and destination
pages.

I do not want to flip those routes to fully dynamic and watch origin traffic
multiply. What is the sane architecture here? Can I keep the shell cached and
only resolve the personalized parts per request, or does personalization force
the whole page onto teh dynamic path?

## 2 answers

### Accepted answer from felix_o (2025-04-08)

You have named the tension correctly: cached HTML is by definition identical
for everyone, and request-level personalization is by definition not. There is
no trick that makes both true for the same bytes. So the answer is to split
the page.

Two workable shapes:

1. **Dynamic route for personalized pages.** Move only the routes that carry
   personalized slots onto the dynamic path and resolve content server-side
   with `fetchContent`. No flicker, personalized HTML arrives in one response,
   but those routes stop hitting the CDN cache. Fine if it is a handful of
   pages.

2. **Cached shell + client-side slots.** Keep the full-page cache exactly as
   is and fetch only the personalized slots from the browser with the React
   hooks, each with a fallback. The shell paints immediately from the CDN and
   the slot content is per-user. The slot fetch goes to Croct's API, which is
   under 90ms P95 end-to-end, so the swap window is small.

With a 97% hit ratio I would start with option 2 for the home page and only
promote a route to option 1 if the personalized module is above the fold and
the swap bothers you.

#### Reply from Bastien Girard (2025-04-09)

Went with the cached shell approach for destinations and dynamic rendering
for the home page only. Hit ratio barely moved. Merci.

### Answer from darren_mc (2025-04-09)

Adding a small operational note from running the hybrid setup: the response
metadata on content fetches tells you what you got, `contentSource` is
`"slot"`, `"experience"`, or `"experiment"` with the matching IDs. We log it
per route, which makes it easy to audit later which routes actually serve
personalized content and which could move back to the fully cached path. At
travel-site scale that audit saved us a couple of routes we had assumed
needed to be dynamic.
