Fastly Instant Purge

When you ship a corrected asset — a patched JavaScript bundle, an updated stylesheet — stale bytes sitting in edge caches can reach users for minutes or hours if your CDN does not propagate invalidation rapidly. Fastly’s Instant Purge resolves this in under 150 milliseconds across its entire global network, turning a multi-minute wait into a near-real-time operation.

When to Use Fastly Instant Purge

Instant Purge is the right tool in the following situations:

  • Emergency rollback of a bad deploy. A broken asset is live and needs to stop reaching users within seconds, not the 5–10 minutes that AWS CloudFront invalidation typically requires.
  • Purging HTML entry points after a new fingerprinted release. Fingerprinted JS and CSS assets are immutable by design — their content hash makes them self-versioning — but the HTML that references them is not, and it must be purged on every deploy.
  • Batch invalidation of a release group. You tagged every asset in a release with a shared Surrogate-Key and need to atomically remove the entire group in a single API call rather than listing hundreds of URLs.
  • Soft purge for graceful degradation. You want stale-while-revalidate semantics: mark objects stale so the next request triggers background revalidation and users never receive an error response during the fetch.

Prefer Instant Purge over alternatives when:

Scenario Use Instant Purge Use alternative
Sub-second invalidation required Yes
Purging fingerprinted assets that will never change No — serve them indefinitely Build a new URL instead
Purging HTML at CDN with no Fastly subscription Cloudflare cache purge or Nginx
Self-hosted Nginx with no external CDN Nginx proxy cache purge
Budget-constrained and invalidation latency of minutes is acceptable CloudFront invalidation

The decision that trips up most teams is not which CDN to purge from but what to purge. In a fingerprinted build, the majority of files that change on a deploy require no purge at all, because their URLs changed with them. Sorting each artifact into one of three buckets before you write a purge script keeps the blast radius honest:

What actually needs purging after a deploy A decision tree from a new release deploy into three branches: fingerprinted assets need no purge because a new build produces new URLs, HTML entry points need a hard purge by surrogate key, and unhashed files whose path is stable need a purge by URL. New release deployed Fingerprinted asset app.a1b2c3d4.js HTML entry point / and /index.html Unhashed file robots.txt, og-image.png No purge needed new build = new URL old object ages out Hard purge by key html-entry-points every deploy, no exceptions Purge by URL path stayed the same bytes changed in place
Sort every deploy artifact into one of three buckets first; only two of them ever justify a purge call.

Prerequisites

Requirement Version / Detail
Fastly service Any paid plan; Instant Purge is available on all tiers
FASTLY_TOKEN API token Created in the Fastly web UI under Account → API Tokens; requires purge_all scope for purge-all, purge_select for URL and surrogate-key purges
SERVICE_ID Visible on the Fastly dashboard service overview page
curl 7.68+ or equivalent HTTP client Needed to send PURGE method requests
Origin emitting Surrogate-Key header Required for tag-based batch purge; VCL override or origin code change
VCL access (optional) Required to set resp.http.Surrogate-Key in vcl_deliver if your origin cannot emit it

Surrogate-key purge requires either your origin to emit Surrogate-Key response headers or a VCL snippet in vcl_deliver to inject them. If you have no VCL access, you must emit the header from application code.

Purge Configuration Reference

Key / Header Type Default Effect
Fastly-Soft-Purge Request header (integer) 0 (hard purge) Set to 1 to mark the object stale rather than deleting it; triggers stale-while-revalidate behaviour
Surrogate-Key Response header (space-separated strings) Not set Associates the cached object with one or more tags; enables tag-based batch purge
Surrogate-Control Response header Not set Overrides Cache-Control TTL at the edge only; Fastly strips this header before forwarding the response to the browser
Cache-Control Response header Origin-provided Sent to the browser verbatim after Fastly strips Surrogate-Control; controls browser TTL and immutable directive
stale-while-revalidate Cache-Control directive Not set After a soft purge marks an object stale, this directive tells Fastly how long to serve stale while revalidating in the background
stale-if-error Cache-Control directive Not set Instructs Fastly to serve a stale object if the origin returns a 5xx error during revalidation
Fastly-Key Request header Required Authenticates purge API requests; value is your API token
purge_all API endpoint REST endpoint POST /service/{id}/purge_all Wipes all objects in the service; use with caution in production

Step-by-Step Implementation

1. Emit Surrogate-Key and Surrogate-Control Headers from the Origin

Your origin must attach a Surrogate-Key header to every cacheable response. Group related assets under a shared tag so you can purge the group atomically.

# Example: Python/Flask origin server
from flask import Flask, send_from_directory, make_response

app = Flask(__name__)

@app.route('/assets/<path:filename>')
def serve_asset(filename):
    response = make_response(send_from_directory('dist/assets', filename))
    # Tag with release version; Fastly strips before browser sees it
    response.headers['Surrogate-Key'] = 'release-v2-5-0 static-assets'
    # Fastly reads this and does NOT forward it to the browser
    response.headers['Surrogate-Control'] = 'max-age=31536000'
    # Browser receives this — fingerprinted assets are immutable
    response.headers['Cache-Control'] = 'public, max-age=31536000, immutable'
    return response
# Nginx origin (upstream of Fastly): add headers for all fingerprinted assets
location ~* ^/assets/[a-z0-9._-]+\.[a-f0-9]{8,}\.(js|css|png|jpg|svg|woff2)$ {
    root /srv/app/dist;
    add_header Surrogate-Key "release-v2-5-0 static-assets";
    add_header Surrogate-Control "max-age=31536000";
    add_header Cache-Control "public, max-age=31536000, immutable";
    add_header Vary "Accept-Encoding";
}

Hash length defaults to 8 hex characters in these examples. Use 12–16 characters in monorepos with thousands of chunks to reduce collision probability.

2. Set Surrogate-Key in VCL (if origin cannot emit the header)

If your origin code cannot be changed, inject the tag from custom VCL. The snippet below shows the minimal form in vcl_deliver; for a production service you want the tag written in vcl_fetch instead, alongside the TTL and backend routing covered in the VCL routing reference.

sub vcl_deliver {
  # Tag fingerprinted assets with the release version
  if (req.url.path ~ "^/assets/[a-z0-9._-]+\.[a-f0-9]{8,}\.(js|css|png|jpg|svg|woff2)$") {
    set resp.http.Surrogate-Key = "release-v2-5-0 static-assets";
  }

  # Tag HTML entry points separately so they can be purged independently
  if (req.url ~ "\.(html)$" || req.url == "/") {
    set resp.http.Surrogate-Key = "release-v2-5-0 html-entry-points";
  }
}

The vcl_deliver subroutine runs just before Fastly sends the response to the client. Because Fastly automatically strips Surrogate-Key (and Surrogate-Control) headers from the response it forwards to the browser, end users never see these internal tags.

There is an important caveat to that convenience. A tag set in vcl_deliver shapes the response but is never written onto the stored object, so Fastly’s tag index has nothing to invalidate and a purge-by-key call returns {"status":"ok"} while changing nothing. Setting the header from the origin (Step 1) avoids the problem entirely, because the tag arrives with the body Fastly is about to cache. If you must do it in VCL, set it in vcl_fetch.

3. Purge by URL (Single Object)

A URL purge removes or marks stale a single cached object. This is the most targeted operation — use it when you know exactly which file changed and want zero blast radius.

# Hard purge: object is immediately deleted from all POPs
curl -X PURGE \
  -H "Fastly-Key: $FASTLY_TOKEN" \
  "https://your-service.global.ssl.fastly.net/assets/app-a1b2c3d4.js"

# Soft purge: object is marked stale; next request triggers background revalidation
curl -X PURGE \
  -H "Fastly-Soft-Purge: 1" \
  -H "Fastly-Key: $FASTLY_TOKEN" \
  "https://your-service.global.ssl.fastly.net/assets/app-a1b2c3d4.js"

Fastly returns HTTP 200 with a JSON body on success:

{ "status": "ok", "id": "1-1719000000-abc123" }

4. Purge by Surrogate-Key (Batch Purge)

A surrogate-key purge removes all objects tagged with a given key across every Fastly POP in a single API call. This is the recommended approach for purging an entire release at deploy time, as detailed in the Fastly surrogate keys guide.

# Purge all objects tagged with release-v2-5-0
curl -X POST \
  "https://api.fastly.com/service/$SERVICE_ID/purge/release-v2-5-0" \
  -H "Fastly-Key: $FASTLY_TOKEN"

# Soft purge by surrogate-key (mark stale, do not delete)
curl -X POST \
  "https://api.fastly.com/service/$SERVICE_ID/purge/release-v2-5-0" \
  -H "Fastly-Key: $FASTLY_TOKEN" \
  -H "Fastly-Soft-Purge: 1"

# Purge a different tag — e.g. HTML entry points only
curl -X POST \
  "https://api.fastly.com/service/$SERVICE_ID/purge/html-entry-points" \
  -H "Fastly-Key: $FASTLY_TOKEN"

To purge multiple tags in one script without multiple round-trips, chain the calls sequentially in your deploy pipeline:

#!/usr/bin/env bash
set -euo pipefail

RELEASE_TAG="release-v2-5-0"
HTML_TAG="html-entry-points"
PURGE_URL="https://api.fastly.com/service/$SERVICE_ID/purge"

purge_tag() {
  local tag="$1"
  local soft="${2:-0}"
  curl -s -X POST \
    "$PURGE_URL/$tag" \
    -H "Fastly-Key: $FASTLY_TOKEN" \
    -H "Fastly-Soft-Purge: $soft" \
    | jq -r '.status'
}

echo "Purging release assets…"
purge_tag "$RELEASE_TAG" 1   # soft purge assets — allow stale-while-revalidate

echo "Purging HTML entry points…"
purge_tag "$HTML_TAG" 0      # hard purge HTML — users must get fresh markup immediately

echo "Purge complete."

Both calls return once the invalidation has been accepted and fanned out, not once every POP has acknowledged it — but the fan-out itself is the fast part. The sequence below is what happens between your curl exiting and the first user seeing new bytes:

Instant Purge propagation sequence A deploy job uploads a build and calls the purge API; Fastly validates the token and fans the signal out to every POP within 150 milliseconds; the next request is a miss that refills the cache from origin. Two panels below contrast soft purge, which marks objects stale, with hard purge, which evicts them. Instant Purge propagation: API call to every POP Deploy job uploads new build calls purge endpoint Fastly API validates the token queues the fan-out Every POP tag index resolved objects invalidated Next request MISS, refill origin cache warm again t = 0 ms t ≈ 20 ms t < 150 ms next user hit Soft purge · Fastly-Soft-Purge: 1 object stays, marked stale served while revalidating behind it Hard purge · the default object evicted from every POP next request blocks on origin
Purge latency does not grow with the number of POPs: the signal fans out in parallel, so the only variable cost is the origin refill after the first miss.

5. Purge All (Full Cache Wipe)

A purge-all removes every object in the service from every POP. Use it sparingly: it causes a cache cold-start, driving 100% of traffic to origin until the cache rewarms.

# Wipe the entire service cache — use only in emergencies
curl -X POST \
  "https://api.fastly.com/service/$SERVICE_ID/purge_all" \
  -H "Fastly-Key: $FASTLY_TOKEN"

When purge-all is safe: staging environments with low traffic, immediately after a major configuration change that renders all previously cached objects incorrect (for example, changing the cache key structure itself), or after a data breach where you must guarantee no stale sensitive content remains at the edge.

When purge-all is dangerous: production services with high traffic where a cold-start would overwhelm the origin; services without request coalescing (Fastly’s shield feature) that would send thousands of simultaneous origin requests per popular object.

6. Soft Purge: Stale-While-Revalidate and Stale-If-Error

A soft purge marks an object stale rather than deleting it. Fastly will continue serving the stale object immediately while revalidating in the background — this is the same semantics as the stale-while-revalidate directive in Cache-Control.

For soft purge to work correctly, the origin must emit either stale-while-revalidate in Cache-Control, or Fastly’s own stale behaviour must be configured in the service settings. Without one of these, a soft-purged object is treated as expired immediately and no grace period is provided.

# Origin response headers for soft-purge-friendly HTML entry points
Cache-Control: public, max-age=60, stale-while-revalidate=86400, stale-if-error=604800
Surrogate-Control: max-age=3600
Surrogate-Key: html-entry-points release-v2-5-0

With stale-if-error=604800, if your origin returns a 5xx error during background revalidation, Fastly will continue serving the last good cached version for up to 7 days. This is a critical reliability backstop for HTML entry points during a bad deploy — users see old content rather than an error page. The rolling back a bad deploy on Fastly guide covers the full recovery procedure.

7. Surrogate-Control vs Cache-Control: The Header Split

Fastly reads Surrogate-Control in preference to Cache-Control when making edge TTL decisions, then strips it before forwarding the response to the browser. The browser only ever sees Cache-Control.

This gives you independent control over two TTLs:

# What the origin emits:
Surrogate-Control: max-age=86400       ← Fastly edge TTL: 24 hours
Cache-Control: public, max-age=300     ← Browser TTL: 5 minutes

# What Fastly forwards to the browser:
Cache-Control: public, max-age=300     ← Surrogate-Control is stripped and gone

For fingerprinted assets you typically want both TTLs to be one year, because the content hash guarantees freshness. For HTML entry points, a short browser TTL combined with a longer edge TTL lets you purge at the CDN without relying on users clearing their browser cache.

# HTML entry point header split
Surrogate-Control: max-age=3600, stale-while-revalidate=86400
Cache-Control: public, max-age=60, stale-while-revalidate=86400, stale-if-error=604800
Surrogate-Key: html-entry-points release-v2-5-0

Visualising the split makes the operational consequence obvious: everything you can purge lives on the left of the boundary, and everything you cannot lives on the right.

Surrogate-Control and Cache-Control across the edge boundary The origin emits Surrogate-Control, Surrogate-Key and Cache-Control. Fastly consumes and removes both surrogate headers, so the browser receives only Cache-Control, giving the edge and the browser independent time-to-live values. Edge TTL and browser TTL are set independently Origin response headers Surrogate-Control: max-age=3600 Surrogate-Key: html-entry-points Cache-Control: public, max-age=60 three headers leave the origin Fastly strips Browser response headers Cache-Control: public, max-age=60 Surrogate-Control — removed Surrogate-Key — removed one header reaches the client Surrogate-Control governs the edge copy; Cache-Control governs the browser copy. A purge reaches the edge copy only — browser copies expire on their own schedule.
Only Cache-Control survives the edge boundary, which is why a long edge TTL costs you nothing in flexibility and a long browser TTL is a commitment you cannot revoke.

8. Scoping the API Token

Purge permissions are the one part of this setup worth splitting across tokens. Fastly scopes purge rights two ways: purge_select covers URL and surrogate-key purges, purge_all covers the full-service wipe. A deploy pipeline needs only the first.

# Deploy-time token: surrogate-key and URL purges only.
# Create under Account → API Tokens with scope "purge_select",
# limited to the single service it needs to touch.
export FASTLY_TOKEN="$(vault kv get -field=purge_select secret/fastly/web)"

# Confirm the token cannot escalate to a full wipe before you trust it in CI.
curl -s -o /dev/null -w "%{http_code}\n" -X POST \
  "https://api.fastly.com/service/$SERVICE_ID/purge_all" \
  -H "Fastly-Key: $FASTLY_TOKEN"
# Expect: 403   (a 200 here means the CI token is over-scoped)

curl -s -o /dev/null -w "%{http_code}\n" -X POST \
  "https://api.fastly.com/service/$SERVICE_ID/purge/html-entry-points" \
  -H "Fastly-Key: $FASTLY_TOKEN"
# Expect: 200

Keeping the purge_all token out of CI removes the single most damaging accidental operation available on a Fastly service. If an engineer genuinely needs a full wipe during an incident, they can issue it interactively from a token held in the secrets manager under a break-glass procedure.

Verification Commands

After triggering a purge, verify propagation with targeted header inspection.

# 1. Confirm an object was purged (should return MISS or a fresh Age: 0 response)
curl -sI \
  -H "Fastly-Debug: 1" \
  "https://your-service.global.ssl.fastly.net/assets/app-a1b2c3d4.js" \
  | grep -Ei "^(x-cache|fastly-debug|surrogate-key|cache-control|age|surrogate-control):"

# Expected after a hard purge:
# X-Cache: MISS                   ← object was evicted
# Cache-Control: public, max-age=31536000, immutable
# Age: 0                          ← freshly fetched from origin

# Expected after a soft purge (before first revalidation request):
# X-Cache: HIT                    ← stale object still served
# Age: 86421                      ← original age, now stale
# X-Served-By: ...

# 2. Confirm Surrogate-Key header is being emitted by origin
curl -sI "https://your-service.global.ssl.fastly.net/assets/app-a1b2c3d4.js" \
  -H "Fastly-Debug: 1" \
  | grep -i surrogate

# You should see Surrogate-Key in the debug headers (Fastly strips it from normal responses)

# 3. Verify Surrogate-Control is stripped (browsers should never see it)
curl -sI "https://your-service.global.ssl.fastly.net/assets/app-a1b2c3d4.js" \
  | grep -i surrogate-control
# No output expected — Fastly strips this before forwarding

# 4. Check Age header to confirm cache warming after purge
sleep 5
curl -sI "https://your-service.global.ssl.fastly.net/assets/app-a1b2c3d4.js" \
  | grep -i "^age:"
# Should show a small value (e.g. Age: 5) confirming the object was re-cached

# 5. Confirm purge API authentication works
curl -s -o /dev/null -w "%{http_code}" \
  -X POST \
  "https://api.fastly.com/service/$SERVICE_ID/purge/test-tag" \
  -H "Fastly-Key: $FASTLY_TOKEN"
# Expect: 200

Edge Cases and Known Issues

Surrogate-Key Header Not Present After Purge

If a subsequent request does not show Surrogate-Key in Fastly debug headers, the origin is not emitting the header and no VCL override is in place. Objects without surrogate keys can only be purged by URL or via purge-all. Diagnose by fetching the origin directly (bypassing Fastly) and checking whether Surrogate-Key appears in the response:

curl -sI https://your-origin.internal/assets/app-a1b2c3d4.js | grep -i surrogate

If no header appears, add it in origin server configuration or via a VCL snippet as shown in Step 2 above.

Purge Scope Mismatch: Shielding and POP Hierarchy

Fastly’s shielding feature routes all origin fetches through a designated shield POP before distributing content to edge POPs. A purge call hits all POPs including the shield. However, if an edge POP has a locally cached copy that was populated from the shield before the purge arrived, there is a narrow race window (typically under 50 ms) where a stale response could be served. This is eliminated by using soft purge with stale-while-revalidate, since the background revalidation will always win before the grace period expires.

Surrogate-Key Size Limit: 16 KB Per Response

Fastly limits the Surrogate-Key header to 16 KB per response. A large monorepo releasing thousands of chunks each tagged individually will hit this limit. The correct pattern is to use coarse-grained tags (release-v2-5-0, static-assets) rather than per-file tags. Per-file granularity should be achieved through URL-based purge, not surrogate keys. The cache key architecture guide covers strategies for keeping the key space manageable.

Purge-All Rate Limits

The purge-all endpoint is rate-limited to one call per service per second on standard plans. In CI/CD pipelines that run multiple deploy jobs in parallel, a race condition can cause a 429 Too Many Requests response. Serialize purge-all calls and add exponential backoff:

#!/usr/bin/env bash
set -euo pipefail

purge_all_with_retry() {
  local attempt=0
  local max_attempts=5
  while [ $attempt -lt $max_attempts ]; do
    local status
    status=$(curl -s -o /dev/null -w "%{http_code}" \
      -X POST \
      "https://api.fastly.com/service/$SERVICE_ID/purge_all" \
      -H "Fastly-Key: $FASTLY_TOKEN")
    if [ "$status" = "200" ]; then
      echo "Purge-all succeeded on attempt $((attempt + 1))"
      return 0
    fi
    attempt=$((attempt + 1))
    sleep $((2 ** attempt))
  done
  echo "Purge-all failed after $max_attempts attempts" >&2
  return 1
}

purge_all_with_retry

Fingerprinted Assets Do Not Require Purging

A common misunderstanding: assets whose filenames embed a content hashapp-a1b2c3d4.js — never need to be purged. Their cache key is unique to their content. Deploying a new version creates a new URL; the old URL continues to serve correctly from cache for its TTL. The ETag vs immutable cache-control reference explains why immutable is safe for these assets. Only the HTML entry points and any non-fingerprinted resources (robots.txt, sitemap.xml, OG images) need active purging on deploy.

Token Permission Errors (403 Forbidden)

A 403 from the purge API means either the token is invalid or the token’s scope does not include the required permission. URL purge and surrogate-key purge require the purge_select scope; purge-all requires purge_all. Create separate tokens per scope and store them in your CI/CD secrets manager, not in source code.

Soft Purge Requires Grace-Period Configuration

Soft purge only has the intended graceful effect if Fastly knows how long to serve stale. Configure this in one of two ways: emit stale-while-revalidate in Cache-Control from the origin, or set a stale TTL in your Fastly service settings under Health Checks → Stale Content. Without one of these, a soft-purged object is treated as expired immediately, making soft and hard purge functionally identical.

Performance Impact

Purge Type Propagation Latency Origin Load After Purge Recommended Use
Hard purge by URL <150 ms One origin request per POP Single known-bad asset
Soft purge by URL <150 ms Background revalidation only HTML entry points with stale-while-revalidate
Hard purge by Surrogate-Key <150 ms One request per tagged object per POP requesting it Full release invalidation
Soft purge by Surrogate-Key <150 ms Background revalidation per object Preferred for HTML at scale
Purge-all <150 ms (signal), then cache cold 100% origin miss until rewarm Emergency only
CloudFront invalidation (comparison) 5–10 min Queued; eventual Acceptable when latency budget permits

Placed against the alternatives, the gap is not a matter of tuning — it is architectural:

Invalidation propagation time by mechanism Fastly instant purge completes in under 150 milliseconds, Cloudflare cache-tag purge in seconds, CloudFront invalidation in minutes, and a plain TTL expiry only when the object's time-to-live elapses. How long until the edge stops serving the old bytes Fastly instant purge under 150 ms, all POPs Cloudflare cache tag seconds, varies by plan CloudFront invalidation 5 to 10 minutes, queued Waiting for TTL expiry whole remaining TTL Bar lengths are illustrative, not a linear time axis.
Instant purge is fast enough that invalidation stops being a scheduling problem and becomes a synchronous step in the deploy.

Fastly’s sub-150 ms propagation is achieved through its distributed invalidation bus, which fan-outs the purge signal to all POPs simultaneously rather than waiting for each to poll a central invalidation queue. This architecture means purge latency does not scale with the number of POPs — adding a new region does not increase purge propagation time.

Soft purge has essentially zero additional origin load compared to no purge at all: the background revalidation request for each object happens lazily on the next cache hit, and Fastly coalesces simultaneous revalidation requests for the same object into a single origin fetch (request coalescing). This makes soft-by-surrogate-key the safest option for purging large asset groups in production.

Pre-Deploy Checklist

Run this list once when you wire Fastly into a release pipeline, and again whenever the tagging scheme changes. Every item is verifiable with a single command from the sections above.

Frequently Asked Questions

Should I purge fingerprinted JS and CSS assets after every deploy?

No. Fingerprinted assets whose filenames contain a content hash are immutable by definition — their URL uniquely identifies their content. Deploying new versions creates new URLs; the old URLs remain valid and correctly cached. Purge only the HTML entry points (and any non-fingerprinted resources such as robots.txt or favicon.ico) that reference the new hashed URLs. This is the most important operational distinction when working with a content-hashing build pipeline.

What is the difference between hard purge and soft purge, and when should I choose soft?

A hard purge deletes the cached object immediately. The next request is a cache miss and goes to origin, potentially causing a thundering herd if thousands of users request the same object simultaneously. A soft purge marks the object stale; Fastly serves the stale version to the next visitor while simultaneously sending a background revalidation request to origin. Choose soft purge for HTML entry points and any resource where a brief period of stale content is acceptable but a cache miss storm is not. Choose hard purge when serving any stale version — even for 50 ms — is unacceptable (for example, after invalidating security-sensitive content).

Does Fastly’s Surrogate-Control header reach the browser?

No. Fastly strips Surrogate-Control from all responses before forwarding them to the client. The browser only ever sees Cache-Control. This means you can set a very long edge TTL in Surrogate-Control (enabling aggressive caching at the CDN) while independently setting a shorter browser TTL in Cache-Control — and you retain the ability to purge the edge copy at any time without touching the browser cache.

How do I integrate Fastly purge into a CI/CD pipeline?

Add a purge step at the end of your deploy job, after new assets are uploaded to origin. Purge HTML entry points by surrogate key (html-entry-points or release-v{version}) using a hard purge, and optionally issue a soft purge for the previous release’s assets to accelerate revalidation. Store SERVICE_ID and FASTLY_TOKEN as encrypted CI/CD secrets. The CI/CD asset pipeline integration guide shows how to wire this into GitHub Actions alongside hash manifest generation and atomic CDN deploy steps.