Invalidating R2 object cache

Is it possible to invalidate an object cache so that when it’s rewritten (e.g. avatar image in the app’s profile gets updated by the user), the latest uploaded object becomes available right away?

Currently, I use SotoS3 to upload the asset, which works great. This is the config I use for now, thinking that 5 mins of cache works, but there’s a small delay until the new asset is available:

        let putObjectRequest = S3.PutObjectRequest(
            body: .data(data),
            bucket: bucket,
            cacheControl: "Cache-Control: public, max-age=300, s-maxage=300",
            contentType: mimeType,
            key: key

Would it be possible to set the cache to say, 3600s but remove the old object/cache the new one when a new asset is uploaded?


– Tito

R2 shouldn’t have it’s own cache - could you clarify for me how you are accessing the object after upload? Is it a custom domain, an url or the S3 API?

1 Like

Hello Eria,
I access the object via custom domain. Sample:

The above image is:

Thanks for sharing!

In that case you would need to purge the cache using the CDN cache purge functions for your zone:

To automate this, you would use the API:

Ah! Excellent!

So, in order to do that programmatically, I can use:


I have a few questions:

  1. The page Purge cache · Cloudflare Cache (CDN) docs lists lists the Purge cache options. Just below, it indicates that URL is a purge option for the Free plan. But then the page ​Purge cache by prefix (URL) · Cloudflare Cache (CDN) docs states it’s for Enterprise only. (?)
  2. Knowing the URL (as shown above), what would the request body look like?:
curl --request POST \
  --url \
  --header 'Content-Type: application/json' \
  --header 'X-Auth-Email: ' \
  --data '{
  "tags": [

Should I pass the ETag, or CF-RAY perhaps? Or assuming Purge cache by prefix (URL) is available, where would I pass the above URL?

Thanks again!

@erisa-cf could you please include a curl command to illustrate how I could purge cache a given asset URL? It would help a lot. Thank you very much! :pray:

I was able to find the solution after a few minor errors. The following snippet is for Vapor (Swift on Server), but it’s brief and simple enough to adopt in other languages:

private func purgeCloudflareCache(req: Request, zoneID: String, urls: [String]) async throws {
	let uri = URI(string: "\(zoneID)/purge_cache")
	let body = try JSONEncoder().encode(["files": urls])
	let request = ClientRequest(method: .POST, url: uri, headers: [
		"X-Auth-Key": MY_AUTH_KEY,
		"X-Auth-Email": MY_AUTH_EMAIL,
		"Content-Type": "application/json",
	], body: .init(data: body))
	let response = try await req.application.client.send(request)
	guard response.status.code >= 200, response.status.code <= 299 else {
		throw Abort(.internalServerError, reason: "Failed to purge cache with status: \(response.status): \(urls). Response: \(response)")

The takeaway is: include both, X-Auth-Key and X-Auth-Email in the request headers. The X-Auth-Key is the Global API Key and the X-Auth-Email is the email used to open your Cloudflare account.

I hope this helps!

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.