# Edge caching is configured for HTML pages

- **Pattern:** `ab-001804` (`marketing-page-speed.infrastructure.edge-caching-config`)
- **Severity:** info
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001804
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001804)

## Why it matters

Marketing page HTML served without edge caching forces every visit — including from users in the same CDN region as a previous visitor — to hit the origin server and wait for a full server-rendered response. With ISR or `s-maxage` cache headers, the CDN serves the pre-generated HTML directly from an edge node, making repeat visits within the cache window as fast as static file serving (TTFB under 50ms). ISO-25010 time-behaviour quality for HTML responses is identical to static assets when edge caching is configured. Marketing pages have no per-user dynamic content, making them ideal candidates for edge caching — yet `Cache-Control: no-store` or `force-dynamic` rendering defeats this capability entirely.

## Severity rationale

Info because edge caching for HTML is an enhancement over ISR that further reduces origin load and TTFB variance, but its absence is less severe than the underlying TTFB and CDN checks it depends on.

## Remediation

Add `export const revalidate` to marketing page route files to enable ISR — Vercel will cache the HTML at edge nodes and serve subsequent requests without hitting the origin.

```ts
// app/(marketing)/page.tsx
// Remove any: export const dynamic = 'force-dynamic'

// Cache for 1 hour; Vercel serves from edge on cache hit
export const revalidate = 3600

export default async function HomePage() {
  const content = await getCMSContent() // fetched once per revalidate window
  return <MarketingPage content={content} />
}
```

For custom hosting, set `s-maxage` to instruct CDN nodes to cache HTML:

```ts
export async function GET() {
  return new Response(html, {
    headers: {
      'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400'
    }
  })
}
```

Verify caching is active by checking the `x-vercel-cache` or `cf-cache-status` response header — `HIT` confirms the CDN served from cache.

## Detection

- **ID:** `edge-caching-config`
- **Severity:** `info`
- **What to look for:** HTML pages (as opposed to static assets) can also be cached at the CDN edge when their content is the same for all users. For ISR pages, the CDN caches the generated HTML and serves it without hitting the origin. Check: (1) Next.js `revalidate` exports on marketing page components, (2) `Cache-Control` headers with `s-maxage` (CDN cache duration), (3) Cloudflare Page Rules for HTML caching, (4) `vercel.json` for cache headers on HTML routes, (5) Fastly/Akamai VCL config files. Note: on Vercel, ISR pages are automatically cached at the edge — if `revalidate` is detected on the homepage, this is satisfied. Count all instances found and enumerate each.
- **Pass criteria:** Marketing page HTML is cached at the CDN edge via ISR, `s-maxage`, or equivalent. Repeat visits (and first visits from same CDN region) are served from cache without an origin request. At least 1 implementation must be confirmed.
- **Fail criteria:** All HTML is served from the origin with no CDN caching (`Cache-Control: no-cache`, `no-store`, or missing `s-maxage`). Every visit triggers an origin request.
- **Skip (N/A) when:** Static export (`output: 'export'`) — HTML files are already static and cached by the CDN as static assets.
- **Detail on fail:** `"Marketing pages use force-dynamic rendering with Cache-Control:no-store. Every visit hits the origin. With ISR (revalidate:3600), the CDN would serve cached HTML for 1 hour without origin load."`
- **Remediation:** Enable edge caching for marketing page HTML:

  ```ts
  // Next.js App Router — ISR caches HTML at Vercel edge
  // app/(marketing)/page.tsx
  export const revalidate = 3600 // cache for 1 hour

  // Or via response headers for custom hosting
  export async function GET() {
    return new Response(html, {
      headers: {
        'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400'
      }
    })
  }
  ```

---

## External references

- iso-25010 performance-efficiency.time-behaviour

Taxons: performance

HTML version: https://auditbuffet.com/patterns/ab-001804
