Hi all!
I want to resize images from an external origin. Images of that source has a cache-control header with value: ‘public, max-age=3600’
But I want to cache the resized image on the CF edge for longer than that time. Probably more than half a year, or longer. So that image doesn’t count on billable usage requests.
I use these options on the cf object:
options.cf.cacheTtl = 31536000 (one year)
options.cf.cacheEverything = true
The CF wiki says: “Results of image processing are cached for one hour or longer if origin server’s Cache-Control header allows.”
But from time to time I see still revalidations. What did I wrong?
Is the resize image shared over the CF network, or is every resized image on a each CF datacenter, a requests that counts on billable requests?
Below my worker code.
addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request))
})
/**
* Fetch and log a request
* @param {Request} request
*/
async function handleRequest(request) {
// Parse request URL to get access to query string
let url = new URL(request.url)
// Cloudflare-specific options are in the cf object
let options = { cf: { image: {} } }
// Copy parameters from query string to request options
if (url.searchParams.has("w")) options.cf.image.width = url.searchParams.get("w")
if (url.searchParams.has("h")) options.cf.image.height = url.searchParams.get("h")
// set sharpen param
options.cf.image.sharpen = 0.2
// use webp if supported
const accept = request.headers.get('Accept')
if (accept && accept.includes('image/webp')) {
options.cf.image.format = 'webp'
}
// TODO decide later if we like to support avif
// use avif or webm if supported, avif is prefered
//if (accept.includes('image/avif')) {
// options.cf.image.format = 'avif'
//} else if (accept.includes('image/webp')) {
// options.cf.image.format = 'webm'
//}
// Always cache this fetch for a max of x seconds before revalidating the resource for $$$ reasons
options.cf.cacheTtl = EDGE_CACHE_TTL,
options.cf.cacheEverything = true
// Get URL of the original (full size) image to resize
const imageURL = url.searchParams.get("s")
if (!imageURL) return new Response('Missing "s" value', { status: 400 })
try {
const { hostname, pathname } = new URL(imageURL)
// Not every url has the extension already in it,
// so this is disabled for now.
// // Only accept JPEG, PNG, GIF, or WebP types
// // @see https://developers.cloudflare.com/images/url-format#supported-formats-and-limitations
// if (!/\.(jpe?g|png|gif|webp)$/i.test(pathname)) {
// return new Response('Invalid image type', { status: 400 })
// }
// Only accept domains in ALLOWED_DOMAINS setting
const domains = ALLOWED_DOMAINS.split(/\r?\n/g).map(item=>item.trim().replace('*', '')).join('|')
const domainsRegex = new RegExp(domains, 'g')
if (domainsRegex && !domainsRegex.test(hostname)) {
return new Response('Domain of src image not in list', { status: 403 })
}
} catch (err) {
return new Response('Invalid "image" value ' + err, { status: 400 })
}
// Build a request that passes through request header, so that automatic format negotiation can work
const imageRequest = new Request(imageURL, {
headers: request.headers
})
// Returning fetch() with resizing options will pass through response with the resized image
let response = await fetch(imageRequest, options)
response = new Response(response.body, response)
// Set cache control headers to cache on browser for x minutes
response.headers.set("Cache-Control", "public, max-age=" + BROWSER_CACHE_TTL)
return response
}```