Skip to main content
A custom next/image loader sends every image request to Bunny Optimizer, which resizes and converts it at the edge. Your origin never runs /_next/image, and the pull zone caches each width. The example app covers both setups on this page.
Next.js apps deployed to Magic Containers with a CDN endpoint already come with a pull zone in front of the whole app. Turn on Optimizer for that zone with the steps in Add Optimizer. For a static Next.js site on Bunny Storage, see Resize images with Bunny Optimizer.

Set up Optimizer and the loader

1

Create a pull zone

Create a pull zone with your app’s origin URL, then turn on URL Query String vary. Without it, ?width=1080 comes back as the cached ?width=640 image.
2

Enable Optimizer

Enable Bunny Optimizer on the pull zone, then turn on the Dynamic Image API and automatic WebP under Optimizer → Settings. Next.js already minifies CSS and JavaScript, so turn Optimizer’s minification off.
3

Add the loader

The loader turns each next/image width into an Optimizer URL. It needs "use client" because the App Router sends the loader to the browser.
lib/bunny-loader.ts
Any parameters already on src stay, so Dynamic Image API options combine with the responsive widths:
Optimizer defaults to quality 85. The loader sends Next.js’s default of 75 unless a component sets its own.
4

Configure Next.js

next.config.ts
Next.js 16 only accepts quality 75 unless you list other values in images.qualities. The headers() rule caches public/images for a day, since Next.js serves public files with max-age=0.

Choose what the pull zone serves

Visitors load HTML from your host, and Next.js loads scripts, styles, fonts, and images from the pull zone.
1

Respect origin cache headers

Set the pull zone to respect the origin’s Cache-Control header. Next.js sends year-long immutable headers on /_next/static/*.
2

Add CORS headers for fonts

Add woff2 and woff under Headers. next/font serves fonts from /_next/static/media on the pull zone hostname.
3

Block every other path

Add an edge rule with the Block Request action that triggers when the request URL matches none of */_next/static/* and */images/*. Without it, the pull zone serves your pages on its own hostname, and Next.js sends s-maxage=31536000 on static pages, so the edge can cache a year-old copy of your HTML.
4

Build with the pull zone URL

Next.js inlines NEXT_PUBLIC_ variables at build time:
Deploy the standalone build, for example to Magic Containers. The <script> tags and image srcset URLs in the page source should start with your pull zone hostname.
NEXT_PUBLIC_CDN_URL and ALLOWED_ORIGINS must be set when next build runs. If you build in Docker, pass them as build arguments.

Check it works

Nothing should load from /_next/image in the network tab. Point these at your pull zone or public hostname:
Last modified on October 15, 2026