Configuring Origin Shield on Cloudflare and Fastly

Without a shield tier, every edge point of presence (PoP) that misses on a cacheable object fetches it from origin independently. A single popular asset served from fifty PoPs worldwide can generate fifty separate origin requests for the same bytes, defeating the purpose of origin shielding and request collapsing. This guide walks through the concrete dashboard and API steps to enable a shield tier on Cloudflare and Fastly, then verify and measure the result.

Prerequisite Concepts

Before changing shield configuration, make sure the underlying mechanisms are clear:

How the two vendors expose the same ideaCloudflare and Fastly both offer a shield tier, but they differ in naming, placement control, and whether it is on by default.CloudflareFastlyProduct nameArgo Tiered CacheShieldingEnabled by defaultTier selectionautomatic (smart) or customyou choose the PoPConfigured indashboard or APIservice configuration, per backendBilling modelper GB of tiered trafficincluded in the serviceCollapsing includedyes, per backend
The same concept, two different control surfaces

Step-by-Step Resolution

Choosing the shield locationThe shield should sit close to the origin, not close to the users, because its job is to make the origin round trip rare rather than fast.Is the origin in one region?yesShield in that regionnoAre there two active origin regions?yesOne shield per regionnoIs the origin behind an anycast load balancer?yesLet the vendor choose automaticallynoReview after measuring shield hit ratio
Shield placement is an origin-proximity decision

Step 1 — Enable Cloudflare Tiered Cache

Cloudflare’s shield mechanism is called Tiered Cache (the successor to the older “Argo Tiered Cache” product name — Tiered Cache is now a free zone-level feature, with Argo Smart Routing as a separate paid add-on for the origin-to-edge leg). In the dashboard: Caching → Tiered Cache, then choose a topology:

  • Smart Tiered Cache — Cloudflare automatically selects the upper-tier data center with the lowest latency to your origin. This is the correct default for most zones.
  • Custom Tiered Cache Topology — you explicitly assign a specific upper-tier data center, useful when your origin is in a region Cloudflare’s automatic selection doesn’t optimize for, or when you have data-residency constraints.

To enable it via the API instead of the dashboard:

curl -X PATCH "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/argo/tiered_caching" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" \
  -H "Content-Type: application/json" \
  --data '{"value": "on"}'

Expected response:

HTTP/2 200
Content-Type: application/json

{"success":true,"errors":[],"messages":[],"result":{"id":"tiered_caching","value":"on","editable":true}}

Once enabled, every edge PoP that misses on a cacheable object routes the miss through its assigned upper-tier PoP instead of dialing origin directly. If the upper tier already holds the object, it serves the edge PoP without any origin traffic at all.

Step 2 — Designate a shield POP in Fastly

Fastly calls the same concept shielding, and it is configured per-backend rather than per-zone. In the Fastly UI: Origins → (your backend) → Edit, then set the Shielding dropdown to the POP nearest your origin’s physical location.

Via the API, set the shield property when creating or updating a backend:

curl -X PUT "https://api.fastly.com/service/${SERVICE_ID}/version/${VERSION}/backend/origin_backend" \
  -H "Fastly-Key: ${FASTLY_API_TOKEN}" \
  -H "Accept: application/json" \
  --data-urlencode "shield=dca-dc17" \
  --data-urlencode "address=origin.example.com"

dca-dc17 is a Fastly POP code (Washington DC). Pick the code closest to your origin’s actual network location — Fastly publishes the current POP list and codes in its documentation. Activate the version afterward for the change to take effect:

curl -X PUT "https://api.fastly.com/service/${SERVICE_ID}/version/${VERSION}/activate" \
  -H "Fastly-Key: ${FASTLY_API_TOKEN}"

With shielding set, every non-shield POP that misses forwards the request to the shield POP first. Only the shield POP is permitted to reach origin directly.

Step 3 — Verify with curl

On Cloudflare, request the asset from cold and inspect CF-Cache-Status and Age:

curl -sI https://example.com/asset.js \
  | grep -iE '(cf-cache-status|cf-ray|age|cache-control)'
CF-Cache-Status: MISS
CF-RAY: 7c1e2f9a1b2c3d4e-IAD
Age: 0
Cache-Control: public, max-age=3600, s-maxage=86400

Repeat the request against a different edge PoP (use --resolve or a different Accept-Language/geographic vantage point if you have one) and look for CF-Cache-Status: HIT with a non-zero Age — confirming the second PoP received the object from the shield tier rather than triggering a second origin fetch.

On Fastly, the equivalent check uses X-Served-By and Age:

curl -sI https://example.com/asset.js \
  | grep -iE '(x-served-by|x-cache|age|cache-control)'
X-Served-By: cache-dca-dc17-DCA, cache-lhr-EGLL
X-Cache: HIT, MISS
Age: 12

X-Served-By lists every POP the request traversed, shield first. A pattern of MISS at the shield followed by HIT at subsequent edge POPs on later requests confirms the shield is absorbing repeat misses.

Step 4 — Measure origin offload

The real payoff of shielding is quantifiable at the origin. Compare origin request counts before and after enabling shielding using each vendor’s analytics API.

Cloudflare GraphQL Analytics (origin response counts by zone, grouped by hour):

curl -X POST "https://api.cloudflare.com/client/v4/graphql" \
  -H "Authorization: Bearer ${CF_API_TOKEN}" \
  -H "Content-Type: application/json" \
  --data '{"query":"query { viewer { zones(filter: {zoneTag: \"'"${ZONE_ID}"'\"}) { httpRequestsAdaptiveGroups(limit: 24, filter: {datetime_geq: \"2026-07-05T00:00:00Z\"}) { sum { edgeResponseBytes } dimensions { datetimeHour } } } } }"}'

Fastly real-time stats (origin fetches per second):

curl -s "https://rt.fastly.com/v1/channel/${SERVICE_ID}/ts/0" \
  -H "Fastly-Key: ${FASTLY_API_TOKEN}" \
  | jq '.Data[].aggregated.origin_fetches'

A working shield configuration shows origin request volume drop sharply relative to total edge requests — the ratio of edge requests to origin fetches is the offload factor. If that ratio stays close to 1:1 after enabling shielding, the response is likely not cacheable (check for private, no-store, or a missing freshness directive) rather than a shielding misconfiguration.

Expected Output / Verification

  • Cloudflare: the shield PoP shows CF-Cache-Status: MISS on the first request for an object; all other edge PoPs subsequently show HIT without their own origin fetch.
  • Fastly: X-Served-By lists the shield POP first, and X-Cache shows MISS there but HIT at edge POPs on later requests for the same object.
  • Age increases monotonically across repeated requests to the same edge PoP, confirming the object is being served from cache rather than re-fetched.
  • Origin access logs (or the CDN’s analytics API) show a request-to-origin-fetch ratio well above 1:1, proportional to the number of active edge PoPs.
Confirming the shield is in the pathA cold request should show a miss at both tiers; the second request from a different PoP should hit the shield without reaching the origin.PoP AShieldOriginMISS, forwardMISS, fetch200, stored at shield200, stored at PoP Arequest from PoP BHIT at shield, origin untouched
The two-PoP test that proves the shield is working

Edge Cases

  • Shield POP chosen far from origin — if the designated shield POP has high latency to origin, every miss (rare after warm-up, but still on the critical path for cold objects) suffers extra round-trip time. Pick the POP with the lowest latency to your origin’s actual network path, not just geographic proximity.
  • Cache-Control: private bypasses the shield entirely — RFC 9111 requires shared caches, including shield tiers, to refuse to store private responses. A private response passes straight through to origin from every edge PoP, exactly as if shielding were disabled.
  • Purge scope must include the shield tier — a purge that only targets edge PoPs and not the shield tier leaves a stale copy at the shield, which then repopulates edge PoPs with stale data on their next miss. Confirm your purge API call invalidates all tiers.
  • Tiered Cache and Argo Smart Routing are separate features — Tiered Cache handles edge-to-shield-to-origin caching topology; Argo Smart Routing optimizes the network path for cache misses and dynamic content. Enabling one does not enable the other.

Frequently Asked Questions

Does Tiered Cache replace the need for s-maxage?

No. Tiered Cache changes the topology edge nodes use to reach origin on a miss; it does not change the freshness lifetime calculation. The response still needs s-maxage or Cache-Control directives that make it cacheable in the first place — shielding only matters once the response is already storable.

Why is CF-Cache-Status showing MISS at every edge PoP even with Tiered Cache enabled?

Check that the response is actually cacheable — private, no-store, or a Set-Cookie response without explicit caching directives bypasses Tiered Cache entirely. Also confirm the zone’s Tiered Cache topology isn’t set to off, and that no Cache Rule is forcing a bypass.

Can I pin a different shield POP than the one Cloudflare or Fastly picks automatically?

Yes. Fastly lets you explicitly assign a shield POP per backend. Cloudflare’s Smart Tiered Cache selects the upper-tier data center automatically based on latency to origin, but Custom Tiered Cache Topology lets you pin it explicitly when you need control over data residency or peering paths.

How much origin offload should I expect?

It depends on how many edge PoPs serve your traffic and how concentrated your cache keys are. Sites with global traffic and widely dispersed URLs commonly see origin request volume drop by 60-90% because dozens of edge PoPs collapse into a single shield-to-origin path per cached object.


Back to Origin Shielding and Request Collapsing