Workers: watermark images hosted on Cloudflare Images

We're trying to apply a watermark with a simple worker script. Code is taken from documentation. It seems that worker's fetch method completely ignores cf.image options. It just returns original image without any modification. It seems a lot of people have this problem. Is it even possible to apply a watermark to an image hosted on Cloudflare Images? Without this feature we would have to store twice the amount of images, and it's a no-go for us. Worker code for reference:
const CLOUDFLARE_IMAGES_PATH = 'https://imagedelivery.net/0lmOObetrqINsoEQE6c8AQ'
const WATERMARK = {
bottom: 5,
left: 5,
opacity: 0.8,
url: 'https://imagedelivery.net/0lmOObetrqINsoEQE6c8AQ/c544da56-c5cd-47d1-4207-9d3af0451900/origin',
}

addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
const url = new URL(request.url)
const image = url.searchParams.get("image")
if (!image) return new Response('Missing "image" value', { status: 400 })
if (image.startsWith('http')) return new Response('Provide imageId/variant in "image" value', { status: 400 })
if (!/(origin|thumb|list|x0grid|x1grid|x2grid)$/.test(image)) {
return new Response('Disallowed variant name', { status: 400 })
}

const imageURL = [CLOUDFLARE_IMAGES_PATH, image].join('/')

return fetch(imageURL, {
cf: {
image: {
draw: [WATERMARK],
}
}
})
}
const CLOUDFLARE_IMAGES_PATH = 'https://imagedelivery.net/0lmOObetrqINsoEQE6c8AQ'
const WATERMARK = {
bottom: 5,
left: 5,
opacity: 0.8,
url: 'https://imagedelivery.net/0lmOObetrqINsoEQE6c8AQ/c544da56-c5cd-47d1-4207-9d3af0451900/origin',
}

addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
const url = new URL(request.url)
const image = url.searchParams.get("image")
if (!image) return new Response('Missing "image" value', { status: 400 })
if (image.startsWith('http')) return new Response('Provide imageId/variant in "image" value', { status: 400 })
if (!/(origin|thumb|list|x0grid|x1grid|x2grid)$/.test(image)) {
return new Response('Disallowed variant name', { status: 400 })
}

const imageURL = [CLOUDFLARE_IMAGES_PATH, image].join('/')

return fetch(imageURL, {
cf: {
image: {
draw: [WATERMARK],
}
}
})
}
3 Replies
Gabriel Francisco
facing the same problem now you got it working?
Deanna
Deanna2w ago
hey there — currently, the draw options are supported only for transformations (remote images), but we recently released an Images binding which you can use as a workaround: https://developers.cloudflare.com/images/transform-images/bindings/ https://developers.cloudflare.com/images/transform-images/draw-overlays/#draw-using-the-images-binding
Cloudflare Docs
Bind to Workers API · Cloudflare Images docs
A binding connects your Worker to external resources on the Developer Platform, like Images, R2 buckets, or KV Namespaces.
Cloudflare Docs
Draw overlays and watermarks · Cloudflare Images docs
You can draw additional images on top of a resized image, with transparency and blending effects. This enables adding of watermarks, logos, signatures, vignettes, and other effects to resized images.
Deanna
Deanna2w ago
curious about both of your use cases. are you looking to watermark on upload or on delivery? if the latter, if we provided a way to define a watermark in a predefined variant, would that meet your use case?

Did you find this page helpful?