How to use Next.js Image Optimization with Cloudflare

Caching webp images with a fallback using Next.js & a CDN like Cloudflare

Gru’s Plan Meme: Using Cloudflare & Transform Rules to serve webp images with a fallback

Skip the chatter, take me to the solution! 👇

Recently I noticed Cloudflare didn’t cache any of our images optimized with Next.js. After a quick fix using Cloudflare’s Page Rules, all images were served properly from the CDN cache. It worked perfectly fine, but… for browsers without webp support images did not load at all. Although no image loads faster than no image, we’d like to have our images working on all browser 😅

Here’s the problem: the next/image component uses a single url to serve webp images and a fallback for browsers without webp support. Cloudflare however can only cache a single image for a specific url, hence the loading problem if webp was cached, but is not supported by the browser.

So how can we serve webp with a fallback using Cloudflare?

Note: You can most likely use a similar setup for other CDNs than Cloudflare.

How to setup Next.js and Cloudflare to cache webp with a fallback

We want…

  • … Cloudflare to cache all images optimized by Next.js…
  • … and serve a fallback if our browser lacks webp support

Therefore we need to serve webp images and their fallback on two separate URLs (unlike Next.js does by default). We can easily achieve this by appending some additional query parameter to the URL in case the http accept header does not allow webp. Next.js will just ignore the additional parameter, and Cloudflare will cache non-webp images on the new URL.

The idea is simple: Append an additional query parameter to serve webp images and their fallback on separate URLs

Step 1 | Add Page Rule to Cache Next.js Images

Add a Page Rule for the Next.js image route with the following settings:

URL: [your domain]/_next/image?*
Cache Level: Cache Everything
Edge Cache TTL: [appropriate TTL for your use case]

Cloudflare Page Rule settings to cache Next.js optimized images

Cloudflare now caches all images optimized with Next.js, but can’t properly handle browsers that don’t support webp. We’ll fix that in the next step.

Step 2 | Add Transform Rule to Cache Fallback on separate URL

Under Transform Rules create an URL Rewrite Rule. Click on “Edit expression” to use the Expression Editor instead of the builder. Then use the following settings:

Rule Name: [some name for your rule]

Expression Editor: (http.request.uri.path eq “/_next/image” and not any(http.request.headers[“accept”][*] contains “image/webp”))

Rewrite Query too…
Dynamic | concat(http.request.uri.query, “&webp=false”)

Cloudflare URL Rewrite Rule settings to serve webp and their fallback on two separate URLs

So what does it do? The expression makes sure that the rule is only applied on the next/image route and if the browser supports webp:

http.request.uri.path eq “/_next/image”
Only apply this rule to the next/image route

not any(http.request.headers[“accept”][*] contains “image/webp”)
Only apply this rule if the browser doesn’t support webp

The dynamic query string rewrite just appends our custom query parameter to the request’s query string. You can name the parameter and its value whatever you want (at least as long as it doesn’t collide with Next.js parameters 😉).

With a setup like this all requests from browsers without webp support will be rewritten to a separate URL. Next.js will generate a non-webp image on the first request. Cloudflare will cache the image for the rewritten url and serve it for subsequent requests. Browsers with webp support will still receive the webp image cached at the original URL.