Org Skills
Referenceaem-security

Edge protection and security headers

Supporting material for aem-security. Agents load it on demand; it ships inside the skill folder.

RawSource

Verified 2026-09 against https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/implementing/dispatcher/cdn-configuring-traffic and https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/security/home.

Traffic filter rules

Traffic filter rules live in cdn.yaml in the config pipeline and are evaluated at the CDN, before the request reaches dispatcher or publish. They cover rate limiting, geography, IP lists and request properties. WAF rules (separately licensed) add signature-based detection on top.

Working order:

  1. Write the rule with the log-only action.
  2. Deploy, then read what it would have blocked over a representative period — including a weekday peak and any batch integration window.
  3. Fix the false positives. The first things a naive rate limit catches are your own integrations, a partner's crawler and the monitoring probe.
  4. Switch to block, and keep watching.

Things worth rules: a request rate ceiling per client for expensive paths, geo restrictions where the business genuinely has none of that market, an allow-list for admin-ish paths, and blocking known-bad paths that only scanners request.

Things not worth rules: anything the dispatcher filter already denies by default, and anything you cannot describe the false-positive case for.

Purge keys

Cache invalidation is authorised by a purge key.

  • Treat it as a secret: Cloud Manager secret variable, never in Git, never in client-side code.
  • Rotate it, and know who owns the rotation. An unrotatable key shared across teams becomes a standing risk that nobody can remediate quickly.
  • A leaked purge key is a denial-of-service: repeated purges push all traffic to origin.

Security header set

Set these once at the edge for every response rather than per page:

Header Value to start from Why
Strict-Transport-Security max-age=31536000; includeSubDomains Stops protocol downgrade; add preload only when you are sure about every subdomain
X-Content-Type-Options nosniff Prevents MIME sniffing turning an upload into script
Content-Security-Policy see below The only header that meaningfully limits XSS impact
Referrer-Policy strict-origin-when-cross-origin Stops paths and query strings leaking to third parties
X-Frame-Options / CSP frame-ancestors Deny, or an explicit allow-list Clickjacking. Note the author environment needs framing for the editor
Permissions-Policy Deny the features the site does not use Reduces what injected script can reach

CSP with client libraries

CSP is where AEM projects stall, because client libraries and the page editor both produce inline script and style.

A rollout that works:

  1. Deploy in report-only mode with a reporting endpoint, and leave it long enough to see real traffic, including the author environment and any third-party tag manager.
  2. Read the reports. Expect inline handlers in older components, inline style attributes, and analytics or personalisation vendors loading further scripts.
  3. Remove what you can: move inline script into a clientlib file, replace inline onclick with listeners, move inline style into CSS.
  4. For what genuinely must stay inline, use a nonce, regenerated per response — not unsafe-inline, which removes most of the protection.
  5. Keep a separate, looser policy for the author environment; the editor needs framing and inline execution that publish does not.
  6. Only then enforce on publish, and keep the reporting endpoint.

Third-party scripts are the recurring cost: each vendor added to the allow-list can load anything it likes from its own origin, so script-src becomes a list of parties you have transitively trusted. Review it on the same cadence as dependency updates.