perf-caching-review
Paste into Claude Code, Claude.ai, the desktop app, or the browser extension — ⌘/Ctrl+V. ✓ Copied. Switch to Claude and paste — ⌘/Ctrl+V.
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 directiveETagandLast-Modified— conditional request supportVary— which request headers the cache keys on (watch forVary: Cookie)Age— seconds the response has been in the CDN cache (0= cache miss)X-CacheorCF-Cache-Status— CDN cache hit/miss indicatorCDN-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-storeorno-cacheon public content pages that could be cached → HIGH- Missing
Cache-Controlheader entirely → MEDIUM (browser defaults vary) Vary: Cookieon 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:
- Are caching rules configured per route? (
/media/*,/static/*vs./api/*) - Is compression enabled at the Front Door tier (Brotli + gzip)?
- Is the origin shield (origin group + health probes) configured correctly to protect origin from traffic spikes?
- Is
CDN-Cache-Controlbeing honoured? Azure Front Door respectsCDN-Cache-ControloverCache-Control. - 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.jsonor the Next.jsrevalidate/cacheconfig on data-fetching functions. - XM Cloud Edge CDN: is
stale-while-revalidateenabled 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=86400or 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.Hybridor 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: CookieandVary: User-Agentissues 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-Controlvalue, 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
-
ReadWriteGlobGrepBash(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.