Skip to main content
A custom image service sends every <Image> width to Bunny Optimizer, which resizes and converts it at the edge. Your server never resizes an image, and the pull zone caches each width. The example app covers both setups on this page, and runs Astro with the Node adapter.
Astro 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 Astro site on Bunny Storage, see Resize images with Bunny Optimizer.

Set up Optimizer and the image service

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. Astro already minifies CSS and JavaScript, so turn Optimizer’s minification off.
3

Add the image service

The service spreads Astro’s baseService, so Astro still works out the srcset widths, and only getURL changes. It adds ?width= and ?quality= to each image on your own zone, and leaves images on other hosts alone, since Optimizer can’t reach them.
src/bunny-image-service.ts
4

Configure Astro

astro.config.mjs
Use <Image> with a layout, and Astro writes the srcset:
Images in src/assets get a hashed /_astro URL, which the Node adapter serves with a one-year immutable header. Images in public are served with max-age=0, so the pull zone revalidates them with your origin on every cache miss.

Choose what the pull zone serves

Visitors load HTML from your host, and build.assetsPrefix points /_astro files and images at the pull zone.
1

Respect origin cache headers

Set the pull zone to respect the origin’s Cache-Control header, so the /_astro files stay cached for a year.
2

Block every other path

Add an edge rule with the Block Request action that triggers when the request URL matches none of */_astro/* and */images/*. Without it, the pull zone serves your pages on its own hostname.
3

Build with the pull zone URL

Astro reads CDN_URL when it builds:
Deploy dist and start it with node ./dist/server/entry.mjs, for example on Magic Containers. The image srcset URLs in the page source should start with your pull zone hostname.If you self-host fonts, they load from the pull zone hostname too, so add woff2 and woff under the pull zone’s Headers for CORS.
CDN_URL must be set when astro build runs. If you build in Docker, declare it as an ARG in the build stage and pass it with --build-arg.

Check it works

Point these at your pull zone or public hostname:
Last modified on October 15, 2026