Cloudflare Docs
Cloudflare Image Optimization
Edit this page
Report an issue with this page
Log into the Cloudflare dashboard
Set theme to dark (⇧+D)

Serve private images

You can serve private images by using signed URL tokens. When an image requires a signed URL, the image cannot be accessed without a token unless it is being requested for a variant set to always allow public access.

  1. Log in to the Cloudflare dashboard and select your account.
  2. Select Images > Keys.
  3. Copy your key and use it to generate an expiring tokenized URL.

The example below uses a Worker that takes in a regular URL without a signed token and returns a tokenized URL that expires after one day. You can, however, set this expiration period to whatever you need, by changing the const EXPIRATION value.

const EXPIRATION = 60 * 60 * 24; // 1 day
const bufferToHex = buffer =>
[ Uint8Array(buffer)].map(x => x.toString(16).padStart(2, '0')).join('');
async function generateSignedUrl(url) {
// `url` is a full URL
// e.g.
const encoder = new TextEncoder();
const secretKeyData = encoder.encode(KEY);
const key = await crypto.subtle.importKey(
{ name: 'HMAC', hash: 'SHA-256' },
// Attach the expiration value to the `url`
const expiry = Math.floor( / 1000) + EXPIRATION;
url.searchParams.set('exp', expiry);
// `url` now looks like
const stringToSign = url.pathname + '?' + url.searchParams.toString();
// for example, /cheeW4oKsx5ljh8e8BoL2A/bc27a117-9509-446b-8c69-c81bfeac0a01/mobile?exp=1631289275
// Generate the signature
const mac = await crypto.subtle.sign('HMAC', key, encoder.encode(stringToSign));
const sig = bufferToHex(new Uint8Array(mac).buffer);
// And attach it to the `url`
url.searchParams.set('sig', sig);
return new Response(url);
addEventListener('fetch', event => {
const url = new URL(event.request.url);
const imageDeliveryURL = new URL(
url.pathname.slice(1).replace('https:/', '')