static//edge

Reference

Cache configuration

Worked examples for the four things most zones need: correct Cache-Control headers at the origin, TTL rules per path, a stable cache key, and invalidation that is precise enough to be used on every deploy.

01Cache-Control directives

The edge reads the directives below from origin responses. Anything not listed is passed through to the client untouched.

DirectiveEffect at the edge
max-age=NFreshness for N seconds at the edge and in the browser.
s-maxage=NFreshness at the edge only; overrides max-age there.
stale-while-revalidate=NServe the expired object for up to N seconds while refreshing in the background.
stale-if-error=NServe the expired object for up to N seconds if the origin errors or times out.
immutableSuppresses revalidation requests for content-addressed assets.
no-cacheStore the object, but revalidate with the origin before every reuse.
private, no-storeNever stored at the edge.
max-age is not a promise. It is an upper bound on staleness, not a guarantee of retention. Objects may be evicted early under memory pressure or dropped by a purge. Never treat the edge as durable storage.

02Header recipes by content type

Fingerprinted assets — /assets/app.9f2c1d.js

Cache-Control: public, max-age=31536000, immutable
ETag: "9f2c1d"

The filename changes when the content changes, so the object can live at the edge for a year and never be revalidated.

HTML pages — short TTL, background refresh

Cache-Control: public, max-age=0, s-maxage=300,
               stale-while-revalidate=600, stale-if-error=86400
Vary: Accept-Encoding

Browsers always revalidate; the edge serves for five minutes, refreshes quietly for ten more, and rides out a day-long origin outage rather than serving an error page.

Read-only JSON — small TTL, tagged for purge

Cache-Control: public, s-maxage=60, stale-while-revalidate=120
Surrogate-Key: catalog product-4417
Vary: Accept-Encoding, Accept-Language

Anything user-specific

Cache-Control: private, no-store

Responses that depend on a session must be marked private, no-store at the origin. Do not rely on a path rule to protect them: a rule can be edited, a header travels with the response.

03Zone rules

Rules are evaluated top to bottom; the first pattern that matches wins. They set a default for responses whose headers are absent or wrong, and they can override the origin when override: true is set.

static-edge.yaml

zone: docs-static
default_ttl: 60s
compress: [text/html, text/css, application/javascript, application/json]

rules:
  - match: "/assets/*"
    ttl: 365d
    browser_ttl: 365d
    override: true
    ignore_query: true

  - match: "/api/v1/catalog*"
    ttl: 60s
    stale_while_revalidate: 120s
    cache_key:
      query_allow: [page, per_page, sort]
      headers: [accept-language]

  - match: "/api/v1/account*"
    cache: false

  - match: "/*"
    ttl: 300s
    stale_if_error: 24h

Validate a file before applying it:

se-cli config validate static-edge.yaml
se-cli config apply static-edge.yaml --zone docs-static

04Cache keys and normalisation

By default the key is method + host + path + sorted query string, plus the values of every header named in Vary. Two habits keep the hit ratio high:

Requests that share a single cache key

/catalog?page=2&utm_source=newsletter
/catalog?utm_source=ads&page=2
/catalog?page=2

  -> key: GET|shop.example|/catalog|page=2

05Surrogate keys

A surrogate key is a label attached to a response so that a group of objects can be dropped together. Emit them at the origin as a space-separated list:

Surrogate-Key: product-4417 catalog homepage

One product page, the catalog listing and the homepage can then all be invalidated by a single call when product 4417 changes, without knowing which URLs contain it. The header is consumed by the edge and stripped before the response reaches the client.

Limits: up to 64 keys per response, 128 bytes per key, 500 keys per purge request.

06Purge API

All calls are authenticated with a zone token in the Authorization header and return 202 Accepted with a job identifier. Propagation to every node normally completes in under five seconds.

Purge a single URL

curl -X POST https://static-edge-cdn.xyz/api/v1/zones/docs-static/purge \
  -H "Authorization: Bearer $SE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["/index.html", "/catalog?page=2"]}'

Purge by prefix

curl -X POST https://static-edge-cdn.xyz/api/v1/zones/docs-static/purge \
  -H "Authorization: Bearer $SE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"prefix": "/api/v1/catalog"}'

Purge by surrogate key

curl -X POST https://static-edge-cdn.xyz/api/v1/zones/docs-static/purge \
  -H "Authorization: Bearer $SE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keys": ["product-4417", "catalog"]}'

Response

HTTP/2 202
content-type: application/json

{
  "job": "pj_01J8ZQ4M2X",
  "zone": "docs-static",
  "objects": 3,
  "queued_at": "2026-08-12T09:41:07Z"
}

Check a job

curl https://static-edge-cdn.xyz/api/v1/jobs/pj_01J8ZQ4M2X \
  -H "Authorization: Bearer $SE_TOKEN"
Purging everything is rarely the right answer. A zone-wide purge sends every subsequent request to the origin at once. Prefer surrogate keys, and reserve {"everything": true} for incidents. It is rate limited to two calls per hour per zone.

07Verifying behaviour

Request the same URL twice and read the headers. The first response should be MISS with age: 0, the second HIT with a growing age:

curl -sSI https://static-edge-cdn.xyz/docs | grep -Ei 'x-se-|age|cache-control'

If a response you expect to be cached reports BYPASS, check in order:

  1. A Set-Cookie header on the response.
  2. private or no-store in Cache-Control.
  3. A zone rule with cache: false matching earlier than you expected.
  4. A request method other than GET or HEAD.

A low hit ratio with plenty of MISS responses usually means key fragmentation instead — revisit section 04.

Last reviewed: 12 August 2026 · API version v1