All questions

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

Asked by Bastien Girard on

BGBastien Girard

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?

Was this helpful?

2 answers

Ffelix_oAccepted answer

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.

Was this helpful?
BGBastien Girard

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

Ddarren_mc

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.

Was this helpful?
Still have questions?