Deploy the following code to Cloudflare Workers to receive requests for next/image in Next.js
import { optimizeImage } from 'wasm-image-optimization'; const isValidUrl = (url: string) => { try { new URL(url); return true; } catch (err) { return false; } }; const handleRequest = async (request: Request, _env: {}, ctx: ExecutionContext): Promise<Response> => { const accept = request.headers.get('accept'); const isWebp = accept ?.split(',') .map((format) => format.trim()) .some((format) => ['image/webp', '*/*', 'image/*'].includes(format)) ?? true; const url = new URL(request.url); const params = url.searchParams; const imageUrl = params.get('url'); if (!imageUrl || !isValidUrl(imageUrl)) { return new Response('url is required', { status: 400 }); } const cache = caches.default; url.searchParams.append('webp', isWebp.toString()); const cacheKey = new Request(url.toString()); const cachedResponse = await cache.match(cacheKey); if (cachedResponse) { return cachedResponse; } const width = params.get('w'); const quality = params.get('q'); const [srcImage, contentType] = await fetch(imageUrl, { cf: { cacheKey: imageUrl } }) .then(async (res) => (res.ok ? ([await res.arrayBuffer(), res.headers.get('content-type')] as const) : [])) .catch(() => []); if (!srcImage) { return new Response('image not found', { status: 404 }); } if (contentType && ['image/svg+xml', 'image/gif'].includes(contentType)) { const response = new Response(srcImage, { headers: { 'Content-Type': contentType, 'Cache-Control': 'public, max-age=31536000, immutable', }, }); ctx.waitUntil(cache.put(cacheKey, response.clone())); return response; } const format = isWebp ? 'webp' : contentType === 'image/jpeg' ? 'jpeg' : 'png'; const image = await optimizeImage({ image: srcImage, width: width ? parseInt(width) : undefined, quality: quality ? parseInt(quality) : undefined, format, }); const response = new Response(image, { headers: { 'Content-Type': `image/${format}`, 'Cache-Control': 'public, max-age=31536000, immutable', date: new Date().toUTCString(), }, }); ctx.waitUntil(cache.put(cacheKey, response.clone())); return response; }; export default { fetch: handleRequest, };
wrangler deploy
To direct Next.js image optimization requests to Cloudflare Workers, set the following
- next.config.js
/** * @type { import("next").NextConfig} */ const config = { images: { path: 'https://xxx.yyy.workers.dev/', }, }; export default config;