Skip to content
Cache Tags
Esc
↑↓navigate↵open⌘Jpreview
On this page

Purge targets

Send purges to your frontend as a signed webhook, to Cloudflare, or to anything else through your own target.

A target is where the package sends purges. Add targets in the backoffice under Settings > Advanced > Cache tags > Targets, or by name under CacheTags:Targets in appsettings.json. The built-in types are webhook, the default, and cloudflare. You can register your own.

{
  "CacheTags": {
    "Targets": {
      "nextjs": { "Url": "https://www.example.com/api/cache-tags", "Secret": "<at least 32 characters>" },
      "cdn": {
        "Type": "cloudflare",
        "Settings": { "ZoneId": "<zone id>", "ApiToken": "<token>" }
      }
    }
  }
}

Every target gets every purge. Set "Enabled": false to pause a target without removing it.

In the backoffice

Administrators add, edit, test and remove targets under Settings > Advanced > Cache tags > Targets. The tab also lists the targets in appsettings.json, read-only and without their secrets or header values.

Each value, whether the signing secret, a header or a Cloudflare token, is either typed in or names a secret under CacheTags:Secrets in the site’s configuration. The package reads a named secret when it sends a purge.

{
  "CacheTags": {
    "Secrets": {
      "VercelSecret": "<at least 32 characters>",
      "VercelBypass": "<bypass secret>"
    }
  }
}
CacheTags__Secrets__VercelSecret=<at least 32 characters>
CacheTags__Secrets__VercelBypass=<bypass secret>

A target that names VercelBypass reads CacheTags:Secrets:VercelBypass, and nothing outside that section. A secret’s name uses letters, digits and underscores, so it can always be set as an environment variable. So an administrator can send a target only the values you put under CacheTags:Secrets, never the database connection string or other settings.

  • A missing secret fails the purge, not the save. If a secret that a target names is not set on a server, the target shows a problem. Its purges from that server fail with an error that names the header or signing secret, never the secret’s name. The package retries them with backoff, up to 10 tries over about 17 minutes, and then lists them under Failed purges, where Retry sends them again once the secret is deployed.
  • Send test purge sends the target a purge of x-cache-tags-test, a key nothing carries, and says whether the target accepted it. It works before you save.
  • The name never changes, because the package queues purges under it. Deleting a target sets its unsent purges aside.
  • A name in appsettings.json wins. The package ignores a backoffice target with the same name, and logs a warning.
  • Other servers pick up a change to a target within 5 seconds.

Public addresses only

A backoffice target can only connect to public internet addresses, so an administrator cannot point one at a service inside your network. Set CacheTags:AllowPrivateNetworkTargets to true to allow private addresses, such as localhost in development.

The limit doesn’t apply to targets in appsettings.json, or to target types you register, which make their own requests.

To check the address it connects to, a backoffice target connects directly, not through HTTPS_PROXY or the system proxy, unless AllowPrivateNetworkTargets is on. If your site reaches the internet only through a proxy, configure its targets in appsettings.json, which use the proxy as usual.

Request headers

A webhook can send its own headers with every purge, such as a gateway’s API key, or x-vercel-protection-bypass for a protected Vercel deployment:

"nextjs": {
  "Url": "https://www.example.com/api/cache-tags",
  "Secret": "<at least 32 characters>",
  "Headers": { "x-vercel-protection-bypass": "<bypass secret>" }
}

A target sends at most 20 headers, each value on one line. The package refuses headers it sets itself, such as Host, Content-Type, User-Agent and the signature headers. The signature, not a header, proves that a purge came from Umbraco.

The webhook

POST https://www.example.com/api/cache-tags
Content-Type: application/json
User-Agent: TheBuilder.CacheTags/<version>
X-Cache-Tags-Timestamp: 1792663200
X-Cache-Tags-Signature: sha256=<lowercase hex>

{
  "version": 1,
  "id": "<purge id>",
  "changeId": "<change id>",
  "expire": ["content-e000...", "x-dictionary-da-dk"],
  "stale": ["type-article", "children-e000..."]
}
X-Cache-Tags-Timestampheaderinteger

When the package signed the purge, in Unix seconds. Refuse a purge more than 5 minutes off your clock.

X-Cache-Tags-Signatureheaderstring

sha256= and the lowercase hex HMAC-SHA256 of POST\n<path>\n<timestamp>\n<raw body>, keyed with the secret’s UTF-8 bytes.

versionbodyinteger

Always 1 for now. A receiver that does not know the version must answer with an error, so the purge fails and shows under Failed purges instead of being lost.

idbodystring

The purge. A retry sends the same id, so a receiver can ignore a purge it has already handled.

changeIdbodystring

The change the purge is for. One change can send several purges.

expirebodystring[]

Keys that name one item or a site’s own output: content-, media- and x- keys. Purge them now.

stalebodystring[]

Listing keys (type-, children-, search-) and umbraco. Serve them stale while they refresh if the receiver can, or purge them too.

Any 2xx answer counts as delivered. Anything else, or no answer within 10 seconds, counts as a failure, and the package tries again after 2, 4, 8 and more seconds.

  • Use a secret of at least 32 characters, such as one from openssl rand -base64 32, and a different one in each environment. The signature does not cover the host, so with the same secret and path, a purge for one receiver is valid at another.
  • The URL must be https, except on localhost. The package does not follow redirects.

The Next.js guide has a receiver to copy.

Cloudflare

The cloudflare type purges a Cloudflare zone by cache tag. Create an API token with the Zone > Cache Purge permission for the zone.

Cloudflare reads tags from a Cache-Tag response header. The package posts the keys to POST /client/v4/zones/{zone}/purge_cache as tags, at most 100 per call. If a call fails, for example because of a bad token or a rate limit, the package retries it like any purge, and the backoffice shows the error with the purge.

Cloudflare in front of your frontend

This is the setup to prefer, with the Delivery API called from the frontend’s server only. The frontend copies each response’s keys into its own Cache-Tag header. Add Cloudflare as a target alongside your frontend’s webhook, since both caches hold the page.

Cloudflare in front of the Delivery API

This works for a public Delivery API only, since Cloudflare would serve a response fetched with an API key to anyone, and browsers can still call the API directly. Set CacheTags:HeaderName to Cache-Tag, and raise CacheTags:MaxHeaderKeys to about 300: Cloudflare keeps 16 KB of tags per response. A proxy between Cloudflare and Umbraco must then allow response headers of about 15 KB, such as nginx with a proxy_buffer_size of 16k.

Your own target

Implement IPurgeTarget for another CDN or cache, and register it as a singleton:

public sealed class FastlyTarget(IHttpClientFactory http) : IPurgeTarget
{
    public string Type => "fastly";

    public async Task<string?> SendAsync(PurgeRequest request, CacheTagsTargetOptions target, CancellationToken cancellationToken)
    {
        var keys = request.Expire.Concat(request.Stale);
        // Send the keys with target.Settings["ServiceId"] and target.Settings["ApiToken"].
        // Return null on success, or what went wrong, and the package retries with backoff.
        return null;
    }
}

builder.Services.AddSingleton<IPurgeTarget, FastlyTarget>();

Then configure a target with "Type": "fastly" and its Settings, in appsettings.json or the backoffice. The target’s Headers are available too, if your type uses them.

Was this page helpful?