Skip to content
Beta Multi-model support (OpenAI, Gemini, and more) launching soon.

perf-caching-review

Installable Ships in performance-auditor — or copy the raw skill below.
Open Claude.ai

Paste into Claude Code, Claude.ai, the desktop app, or the browser extension — ⌘/Ctrl+V.

ExperimentalNo security review

Caching Review

Audit every layer of the caching stack — CDN, origin, API, browser — and produce a prioritised list of cache configuration fixes. Poor caching is the single most common cause of TTFB > 600 ms on RBA client sites.

Usage

/perf-caching-review <URL or base-URL> [--platform sitecore|optimizely|umbraco|headless] [--env staging|production]

Examples:

  • /perf-caching-review https://www.client.com --platform sitecore --env production
  • /perf-caching-review https://localhost:3000 --platform headless
  • /perf-caching-review https://api.client.com/v1 --platform headless — API-layer audit

Phase 1 — Response Header Inspection

For each key URL type (homepage, content page, API endpoint, media/CDN asset), fetch response headers and record:

# Inspect caching headers for a page
curl -sI "https://www.client.com/" | grep -iE "cache-control|etag|last-modified|age|x-cache|cf-cache|cdn-cache|surrogate-control|vary|expires|pragma"

# Full headers for audit
curl -sI "https://www.client.com/products/" 2>&1

Key headers to extract:

  • Cache-Control — the primary directive
  • ETag and Last-Modified — conditional request support
  • Vary — which request headers the cache keys on (watch for Vary: Cookie)
  • Age — seconds the response has been in the CDN cache (0 = cache miss)
  • X-Cache or CF-Cache-Status — CDN cache hit/miss indicator
  • CDN-Cache-Control — CDN-specific override (Azure Front Door supports this)
  • Surrogate-Control — CDN TTL separate from browser TTL (Varnish/Fastly/Akamai)
  • Surrogate-Key / Cache-Tag — used for targeted cache purge

Phase 2 — Cache Strategy Classification

Classify each resource type and check against the recommended strategy:

Resource type Recommended strategy Rationale
Static assets (versioned) public, max-age=31536000, immutable Never changes — filename includes hash
Static assets (unversioned) public, max-age=86400, must-revalidate Long TTL but allow revalidation
CMS content pages public, max-age=300, stale-while-revalidate=60 Fresh enough; SWR keeps it fast
API — stable reference data public, max-age=3600 Rarely changes; safe to cache
API — user-specific private, no-store Must not be shared
API — authenticated private, max-age=0, must-revalidate Revalidate on every use
Media / images (CDN) public, max-age=2592000, immutable 30 days; CMS media URL includes version
HTML with personalisation private, no-store OR split caching Varies per user

Flag immediately:

  • no-store or no-cache on public content pages that could be cached → HIGH
  • Missing Cache-Control header entirely → MEDIUM (browser defaults vary)
  • Vary: Cookie on public pages → HIGH (effectively disables CDN caching for cookie-bearing requests)
  • Vary: User-Agent → HIGH (creates enormous cache fragmentation)

Phase 3 — CDN Configuration Audit

Azure Front Door

Check the Azure Front Door origin response rules and caching rules configuration. Key questions:

  1. Are caching rules configured per route? (/media/*, /static/* vs. /api/*)
  2. Is compression enabled at the Front Door tier (Brotli + gzip)?
  3. Is the origin shield (origin group + health probes) configured correctly to protect origin from traffic spikes?
  4. Is CDN-Cache-Control being honoured? Azure Front Door respects CDN-Cache-Control over Cache-Control.
  5. Is cache purge automation configured for content deploys?

Look for these in the response headers from Front Door:

x-cache: TCP_HIT        ← cache hit
x-cache: TCP_MISS       ← cache miss (went to origin)
x-cache: TCP_EXPIRED    ← cached but expired, refreshed
x-msedge-ref: ...       ← Front Door edge node identifier

Azure CDN (Classic)

X-Cache: HIT            ← served from CDN
X-Cache: MISS           ← fell through to origin
Age: 3542               ← seconds in cache

Check that the CDN profile has caching rules for:

  • /media/* — long TTL (images, documents)
  • /dist/* or /static/* — immutable (versioned JS/CSS)
  • /api/* — no cache or short TTL
  • / and content page paths — appropriate TTL with SWR

Cloudflare (if used)

CF-Cache-Status: HIT / MISS / EXPIRED / BYPASS

BYPASS on public pages means Cloudflare is detecting a session cookie and bypassing cache. Check if the cookie is actually needed on public pages or if it can be excluded from cache-keying via a Cloudflare Cache Rule.


Phase 4 — Platform-Specific Caching

Sitecore XM Cloud

Output caching:

  • Is the Sitecore rendering host caching configured? Check rendering-host-config.json or the Next.js revalidate / cache config on data-fetching functions.
  • XM Cloud Edge CDN: is stale-while-revalidate enabled on content page responses?
  • For JSS + ISR: is export const revalidate = 300 (or similar) set on page components?
// Next.js App Router — ISR for Sitecore content pages
export const revalidate = 300; // regenerate every 5 minutes

export default async function ContentPage({ params }) {
  const data = await fetchSitecoreLayout(params.path);
  return <Layout data={data} />;
}

Media Library CDN:

  • Are /~/media/ URLs routed through the CDN?
  • Is the media handler returning Cache-Control: public, max-age=86400 or longer?

Optimizely Content Cloud / DXP

DXP CDN caching:

// OutputCacheAttribute on Optimizely page controller
[OutputCache(Duration = 600, VaryByParam = "none")]
public ActionResult Index(StartPage currentPage) { ... }

Verify the CDN cache headers by checking the Surrogate-Control header — Optimizely DXP uses this to control the CDN TTL separately from the browser Cache-Control.

Check for no-store responses on public content pages — this is the most common misconfiguration on Optimizely projects.

Umbraco

Output caching:

// Umbraco controller with output cache
[UmbracoOutputCache(Duration = 600)]
public class ContentController : RenderController { ... }

Redis distributed cache:

  • Is Umbraco.Cms.Caching.Hybrid or a Redis backplane configured for multi-node deployments?
  • Is the Redis connection string in the correct config section?
// appsettings.json
{
  "Umbraco": {
    "CMS": {
      "ExamineLuceneDirectoryFactory": "SyncedTempFileSystemDirectoryFactory"
    }
  },
  "ConnectionStrings": {
    "umbracoDbDSN": "...",
    "umbracoDistributedCache": "redis-connection-string"
  }
}

Next.js / ASP.NET Core API

Next.js fetch cache:

// App Router: aggressive caching for static content
const data = await fetch('https://api.example.com/navigation', {
  next: { revalidate: 3600 } // revalidate every hour
});

// No caching for user-specific data
const userData = await fetch('https://api.example.com/me', {
  cache: 'no-store'
});

.NET API response caching:

// Startup / Program.cs
builder.Services.AddResponseCaching();
app.UseResponseCaching();

// Controller action
[ResponseCache(Duration = 3600, VaryByHeader = "Accept-Encoding")]
[HttpGet("/api/navigation")]
public IActionResult GetNavigation() { ... }

Phase 5 — ETag and Conditional Request Audit

Check if the origin supports conditional GET requests (reduces bandwidth for cache revalidation):

# Get the ETag from an initial response
curl -sI "https://www.client.com/styles.css" | grep -i etag
# → ETag: "abc123"

# Conditional request — returns 304 Not Modified if unchanged
curl -sI "https://www.client.com/styles.css" -H 'If-None-Match: "abc123"'
# Expected: HTTP/2 304

If static assets are missing ETags or Last-Modified, the CDN and browser must re-download unchanged assets on every request after expiry. Flag as MEDIUM.


Phase 6 — Report

## Caching Review Report

**Base URL:** [target]
**Platform:** [detected/specified]
**Environment:** [staging | production]

### Cache Layer Summary

| Layer | Coverage | Effectiveness | Status |
|---|---|---|---|
| CDN (Front Door / Azure CDN) | Content pages: Y/N; Media: Y/N; API: Y/N | Cache hit rate: X% (if measurable) | PASS/FAIL |
| CMS output cache | Y/N | TTL: Xs | PASS/FAIL |
| API response cache | Y/N | TTL: Xs | PASS/FAIL |
| Browser cache | Y/N | TTL: Xs | PASS/FAIL |

### Resource Cache Header Audit

| URL pattern | Cache-Control received | Recommended | Gap |
|---|---|---|---|
| / (homepage) | no-cache | public, max-age=300, swr=60 | HIGH |
| /media/*.webp | (none) | public, max-age=2592000, immutable | HIGH |
| /dist/app.a1b2c3.js | (none) | public, max-age=31536000, immutable | HIGH |
| /api/navigation | no-store | public, max-age=3600 | MEDIUM |

### Findings
[findings]

Quality Checklist

  • Every URL pattern type checked: pages, media, versioned assets, API endpoints
  • Vary: Cookie and Vary: User-Agent issues identified
  • CDN cache hit/miss headers inspected and noted
  • Platform-specific output cache configuration checked
  • ETags checked for static assets
  • Every finding recommends a specific Cache-Control value, not "add caching"
Technical details
Plugin
performance-auditor
Author
Dakota Smith
Skill name
perf-caching-review
Maturity
experimental
Security review
No security review
Owners
  • engineering
Argument hint
<URL or base-URL> [--platform sitecore|optimizely|umbraco|headless] [--env staging|production]
Allowed tools
  • Read
  • Write
  • Glob
  • Grep
  • Bash(curl *)
  • Bash(node *)
  • Bash(cat *)
  • Bash(ls *)
  • WebFetch(domain:localhost)
  • WebFetch(domain:web.dev)
  • WebFetch(domain:learn.microsoft.com)
Source
plugins/performance-auditor/skills/perf-caching-review/SKILL.md

Edit this skill on GitHub — opens GitHub's web editor. No git knowledge needed.