Cache Invalidation in Hybrid AEM: Keeping EDS and AMS

 

Cache Invalidation in Hybrid AEM: Keeping EDS and AMS in Sync on AWS CloudFront

Introduction

In a hybrid AEM setup where EDS and AMS serve different parts of the same website through AWS CloudFront, cache invalidation is one of the trickiest problems to solve. Both systems have completely different invalidation mechanisms — and if you don't coordinate them properly, editors end up seeing stale content, confused about why their published changes aren't showing up.

This post explains how cache invalidation works in each system, why hybrid setups make it harder, and how to build a reliable invalidation strategy across both origins.


The Core Problem

In a single-origin AEM setup, invalidation is straightforward:

  • Editor publishes in AEM
  • Dispatcher flush agent clears the Dispatcher cache
  • CloudFront invalidation clears the CDN layer
  • Done

In a hybrid setup you have two completely separate invalidation pipelines that must never interfere with each other:

EDS publish event
      ↓
Franklin Bot → Hlx Purge API → EDS edge cache cleared
      ↓
CloudFront invalidation for /blog/* paths only

AMS publish / replication event
      ↓
Dispatcher Flush Agent → Dispatcher cache cleared
      ↓
CloudFront invalidation for /products/* paths only

The danger: if an AMS invalidation accidentally triggers a CloudFront purge on EDS paths (or vice versa), you lose your entire EDS cache — destroying the performance advantage EDS was chosen for.

 

       

 


      How EDS Invalidation Works

EDS has a very elegant, automated invalidation model.

When an author updates a page in Google Docs or SharePoint and it is published through the Franklin pipeline:

  1. Franklin Bot detects the change
  2. It automatically calls the Hlx Purge API for the affected URL
  3. The EDS edge cache (Fastly/Hlx) clears that specific page
  4. CloudFront, sitting in front of EDS, needs to be told separately

The key thing to understand: EDS invalidates at the page level automatically. You rarely need to manually trigger EDS purges. The challenge is making sure CloudFront also clears its copy.

EDS invalidation scope:

  • Single page purge — when one document is updated
  • Section purge — when a shared block or fragment is updated (affects multiple pages)
  • Full site purge — rarely needed, triggered manually

How AMS Invalidation Works

AMS invalidation follows the traditional Dispatcher flush model.

When an author activates/publishes content in AEM:

  1. AEM replication agent sends the content to Publish instance
  2. Dispatcher Flush Agent on Publish sends a flush request to Dispatcher
  3. Dispatcher removes the cached .html file and marks the stat file
  4. CloudFront needs a separate invalidation call for the CDN layer

The key difference from EDS: AMS invalidation is path-based and granular — it clears specific .html files or entire subtrees depending on your Dispatcher flush configuration. It does not automatically clear CloudFront.

AMS invalidation scope:

  • Single page — /content/mysite/en/products/overview.html
  • Section tree — /content/mysite/en/products/*
  • Full Dispatcher flush — clears everything (avoid in production)

    

The CloudFront Invalidation Layer

Both EDS and AMS need to trigger CloudFront invalidations when content changes. But they must only invalidate their own paths.

The golden rule:

EDS publish event   → invalidate CloudFront /blog/* paths only
AMS publish event   → invalidate CloudFront /products/* paths only

Never let an EDS event invalidate AMS paths and never let an AMS flush invalidate EDS paths. A full /* CloudFront invalidation should be a last resort — it clears everything across both origins and destroys cache efficiency.

How to Trigger Scoped CloudFront Invalidations

From EDS side: EDS does not natively call CloudFront. You need a small webhook or Lambda function that:

Listen for Franklin publish webhook event
  Extract the URL path that was published (e.g. /blog/my-post)
  Call CloudFront CreateInvalidation API
  Scope: only the specific EDS path (/blog/my-post)
  Do NOT invalidate /products/* or AMS paths

From AMS side: AEM has a built-in mechanism for this — a custom CQ Replication Event Listener or a post-processing workflow step that:

Listen for AEM replication/activation event
  Get the page path that was activated (e.g. /content/mysite/en/products/overview)
  Map it to the external URL (/products/overview)
  Call CloudFront CreateInvalidation API
  Scope: only the specific AMS path
  Do NOT invalidate /blog/* or EDS paths

Invalidation Patterns — What Works and What Doesn't

Pattern 1 — Page-Level Invalidation (Recommended)

Invalidate only the exact URL that changed.

Page published: /content/mysite/en/products/camera.html
External URL:   /products/camera
CloudFront invalidation: /products/camera

Pros: Precise, fast, cheap (CloudFront charges per invalidation path) Cons: If a shared component (header, footer, navigation) changes, you need to invalidate all pages that use it


Pattern 2 — Section-Level Invalidation

Invalidate an entire section when a shared component or template changes.

Navigation component updated → affects all pages
CloudFront invalidation: /products/*

Pros: Covers all affected pages in one call Cons: Temporarily reduces cache hit rate for entire section


Pattern 3 — Surrogate Keys / Cache Tags (Advanced)

Tag CloudFront responses with logical groups so you can invalidate by tag rather than by path.

All /products/* pages tagged with: "products-section", "global-nav"
Navigation changes → invalidate tag "global-nav"
CloudFront clears all pages with that tag across /products/*

This is the most precise approach but requires CloudFront to pass surrogate key headers from AMS Dispatcher (Surrogate-Key or Cache-Tag headers) and a Lambda@Edge function to manage tag-based invalidation.


Shared Components — The Hardest Invalidation Problem

In hybrid setups, the most painful scenario is a shared component that appears on both EDS and AMS pages — typically the global header, footer, or navigation.

Global navigation updated in AEM
  ↓ Needs to invalidate:
  /products/* (AMS pages)     AND
  /blog/*     (EDS pages)

Recommended approach:

Treat shared components as two separate problems:

  • On AMS pages — navigation is rendered server-side by AEM. AMS invalidation handles it.
  • On EDS pages — navigation is typically a shared block fetched from a Google Sheet or SharePoint list. EDS automatically purges it when the sheet is updated.

The key is: do not try to share a single invalidation event across both origins. Keep them independent and let each system handle its own shared components natively.


Stale Content Fallback — Defence in Depth

Even with the best invalidation setup, there will be edge cases where CloudFront serves stale content. Use stale-while-revalidate and stale-if-error directives as a safety net:

For EDS paths:

Cache-Control: public, max-age=86400, stale-while-revalidate=3600

This means: serve cached content for up to 24 hours, but if the cache is between 24h and 25h old, serve stale while fetching fresh in the background. The user never sees a slow page.

For AMS paths:

Cache-Control: public, max-age=3600, stale-while-revalidate=300, stale-if-error=86400

This means: serve cached content for 1 hour, revalidate in background for 5 minutes after expiry, and if AMS Dispatcher is down, serve stale content for up to 24 hours rather than showing a 503.


Key Takeaways

  • EDS and AMS have completely separate invalidation pipelines — never let them cross-contaminate each other's CloudFront paths.
  • EDS invalidation is largely automatic via Franklin Bot — your job is to wire up a CloudFront invalidation scoped to EDS paths only.
  • AMS invalidation requires a custom replication listener or workflow step to trigger scoped CloudFront invalidations.
  • Always invalidate at page level where possible — section-level invalidation as a fallback, full /* invalidation only as a last resort.
  • Use stale-while-revalidate as a safety net on both origins — it eliminates slow cache misses and protects against origin downtime.
  • Shared components (navigation, footer) should be invalidated independently on each origin — do not try to build a single cross-origin invalidation event.

Comments

Popular Posts

Configure/Decoding AEM AuditLogs

How to Increase Apache Request Per Second ?

AdobeDispatcherHacks ".statfile"

how to clear dispatcher cache in aem ?

How Does S3 works with AEM ?

AEM Security Headers

How to Sync HMAC in AEM ?

OakAccess0000: Access denied

AEM ACL and how they are evaluated

Dispatcher flush from AEM UI