On-the-Fly Image Resizing with Cloudflare Workers
Introduction to Edge Image Resizing
In modern web engineering, delivering media efficiently is one of the most critical aspects of frontend performance optimization. Traditionally, developers relied on complex, pre-build image pipelines that generated dozens of static assets for varying screen sizes, resolutions, and formats.
While this approach works, it introduces massive build-time overhead and fills up storage buckets with redundant files. A more modern approach shifts this computation to the edge, leveraging serverless computing to resize and optimize media assets on-the-fly.
Cloudflare Workers offer an ideal runtime environment for running edge-based image resizing pipelines. By executing code in close proximity to the end-user, Workers can intercept incoming requests for images, process parameters such as width, height, quality, and format, perform the resizing operation, and cache the resulting asset globally. This strategy dramatically reduces the Time to First Byte (TTFB) and significantly improves the Largest Contentful Paint (LCP) score, which is a core Web Vital.
Why Edge-Based Image Optimization Matters
Standard image requests typically route through a content delivery network (CDN) to an origin storage bucket like Amazon S3. If an image is too large, the client pays a heavy performance penalty in network download time and rendering overhead.
Additionally, browsers support newer, highly compressed image formats like WebP and AVIF, which can reduce payload sizes by up to 50% compared to traditional JPEG or PNG files. However, some older browsers do not support these modern formats, requiring fallback options.
Implementing logic to check user-agent headers, negotiate content types, and resize assets at a centralized origin server introduces bottlenecks. The server must handle CPU-intensive compression tasks, which can quickly exhaust resources under high traffic load.
Shifting this execution to Cloudflare Workers mitigates these issues by distributing the computational workload across a global edge network. Each edge node handles a small fraction of the traffic, scaling instantly without the need to provision backend servers.
Understanding Cloudflare's Edge Resizing Architecture
The core mechanism relies on Cloudflare's custom fetch API extensions. In a Worker environment, the fetch function accepts an options object containing a cf property.
When image resizing is enabled on the zone, developers can pass configuration parameters directly within this object, instructing the edge routing engine to resize and compress the image before returning it. The syntax is simple but powerful:
// Dynamic configuration of image resizing at the edge
const response = await fetch(imageURL, {
cf: {
image: {
width: 800,
height: 600,
fit: 'cover',
quality: 85,
format: 'avif'
}
}
});
Behind the scenes, Cloudflare intercepts this request, retrieves the original image from the origin if not cached, processes the image using optimized native libraries at the edge node, caches the processed output, and delivers it to the client. This workflow eliminates the need to run separate microservices or containerized image processing servers like Thumbor or Sharp-based Node.js instances.
Content Negotiation and Format Selection
To maximize bandwidth savings, the edge worker must evaluate what image format the client browser supports. This is accomplished by inspecting the Accept request header.
Browsers that support modern compression algorithms will send an Accept header containing image/avif or image/webp. The edge worker can parse this header and request the optimal format from Cloudflare's resizing engine.
Below is a production-grade Cloudflare Worker implementation that demonstrates parsing query parameters, executing content negotiation, enforcing caching policies, and handling error states gracefully:
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// Parse optimization parameters from the query string
const width = parseInt(url.searchParams.get('w') || '0', 10);
const height = parseInt(url.searchParams.get('h') || '0', 10);
const quality = parseInt(url.searchParams.get('q') || '85', 10);
const fit = url.searchParams.get('fit') || 'cover';
// Path to the original asset on the origin storage
const imagePath = url.pathname;
const originURL = 'https://origin.example.com' + imagePath;
// If no width or height is specified, serve the original asset
if (!width && !height) {
return fetch(originURL);
}
// Determine the optimal format based on browser support
const acceptHeader = request.headers.get('Accept') || '';
let format = 'webp'; // Default modern fallback
if (acceptHeader.includes('image/avif')) {
format = 'avif';
} else if (acceptHeader.includes('image/webp')) {
format = 'webp';
} else {
format = 'auto'; // Fallback to original source format
}
// Configure the resizing options object
const options = {
cf: {
image: {
quality,
fit,
format
}
}
};
if (width > 0) options.cf.image.width = width;
if (height > 0) options.cf.image.height = height;
// Execute the fetch request to the origin with edge resizing options
try {
const response = await fetch(originURL, options);
// Clone the response to modify headers and cache the result
const modifiedResponse = new Response(response.body, response);
modifiedResponse.headers.set('Cache-Control', 'public, max-age=31536000, immutable');
modifiedResponse.headers.set('X-Edge-Optimized', 'Bramsley-Edge-Resizer');
return modifiedResponse;
} catch (err) {
// Fallback to origin if resizing fails
return fetch(originURL);
}
}
};
Caching Strategies and Performance Considerations
Resizing images on-the-fly is a CPU-intensive operation. If every image request resulted in a fresh resizing cycle, the worker's latency would increase, and costs would rise. Therefore, robust caching is essential.
Cloudflare automatically caches the results of the cf.image fetch based on the request URL and the specific resizing parameters used. However, you can manage this cache explicitly by leveraging the Cloudflare Cache API.
To implement dynamic media pipelines efficiently, engineers must track several core query parameters during edge processing:
- w: Width of the resized image in pixels, validated against responsive breakpoints.
- h: Height of the resized image in pixels to force aspect ratios.
- q: Compression quality ranging from 1 to 100 (typically 85 for production).
- fit: Resizing mode such as cover, contain, scale-down, or crop.
- format: Desired output format like avif or webp negotiated via the Accept header.
To ensure cache efficiency, keep the configuration space low. Avoid using random width values. Instead, define a fixed set of supported widths matching your responsive design breakpoints (e.g., 320, 640, 768, 1024, 1280).
In your worker, validate the requested width parameter against this list of allowed values. If a client requests a width of 642, round it up to the nearest standardized breakpoint (768). This maximizes cache hits and minimizes CPU usage at the edge.
Optimizing Dynamic Media Delivery with Bramsley
Scaling on-the-fly image optimization without latency spikes requires highly optimized runtime environments. As Bramsley's lead systems architect notes, "True edge media delivery isn't just about resizing; it is about content negotiation, WASM-driven compression fallbacks, and smart cache key isolation."
Bramsley Digital Studio builds premium, globally distributed media networks. Our custom edge routing engines automate breakpoint grouping, integrate dynamic WebAssembly fallback pipelines, and coordinate regional caching topologies to ensure your visual assets load instantly and without layout flicker.