---
title: "How to resize and optimize images with Netlify Image CDN | Netlify Knowledge Base"
description: "Guides and articles to help you get the most out of the Netlify platform."
source: "https://www.netlify.com/knowledge-base/how-to-resize-and-optimize-images-with-netlify-image-cdn/"
last_updated: "2026-09-23T19:52:52.000Z"
---
## Key takeaways

Request `/.netlify/images?url=/your-image.jpg` with query parameters and Netlify transforms the image at the edge. Nothing to install, no function to write, and `url` is the only required parameter. If your framework has an image component, use it: Angular, Astro, Nuxt, Next.js, and Gatsby all wire up to Image CDN automatically.

-   **A bare `url` still reformats.** No size or format parameters means the image keeps its dimensions but converts to `avif` or `webp` based on the browser’s `Accept` header.
-   **`fit=cover` requires both `w` and `h`.** Supplying only one silently misbehaves.
-   **Remote images need allowlisting** in `netlify.toml` under `[images] remote_images`, and the URL needs `encodeURIComponent`.
-   **`fm=blurhash` returns a text string, not image bytes.** Pointing `<img src>` at it renders nothing.
-   **A local 404 usually means you’re running the wrong dev server.** The endpoint only exists under `netlify dev`.
-   **Split Testing is not supported**, and results can be inconsistent between split-test branches.

## Transformation without a pipeline

Image optimization used to mean a build step, a plugin, and a set of pregenerated sizes you had to guess at in advance. Image CDN replaces that with a URL. You ask for the image at the size and format you want, and it’s transformed at the edge and cached.

The parameters are few enough to learn in a minute. The parts worth reading carefully are the `fit` behaviors, which decide whether you get exact dimensions or a preserved aspect ratio, and the remote-image setup, which has two easy-to-miss requirements.

```
# resize + crop to a 50px square, retain left side, convert to webp at q=80curl -vs 'https://mysitename.netlify.app/.netlify/images?url=/owl.jpeg&fit=cover&w=50&h=50&position=left&fm=webp&q=80'
```

## When to use the endpoint, and when to use your framework

**Use your framework’s native image component** where one exists. It wires to Image CDN automatically and handles responsive `srcset` generation for you. Hand-building URLs is for cases the component doesn’t cover: a transformation in a redirect rule, an image in an email template, a thumbnail generated inside a function.

There’s no legacy or deprecated form of this. `/.netlify/images` is the only programmatic surface.

Two places it won’t go: **Split Testing is not supported**, and Image CDN is **not currently part of Netlify’s HIPAA-compliant hosting offering.**

## Worked example

### Query parameters

`GET /.netlify/images?url=<source>&...`

Param

Values

Notes

`url`

Relative path or full remote URL

**Required.** The only required param.

`w`

Integer px

Width

`h`

Integer px

Height

`fit`

`contain` (default), `cover`, `fill`

Resize behavior

`position`

`center` (default), `top`, `bottom`, `left`, `right`

Only applies when `fit=cover`

`fm`

`avif`, `jpg`, `png`, `webp`, `gif`, `blurhash`

Output format. `webp` and `gif` can be animated.

`q`

Integer 1–100 (default 75)

Only for `avif`, `jpg`, `gif`, `webp`

### How `fit` behaves

`fit=`

Aspect ratio kept

Crops excess

Returns exact dimensions

`contain`

Yes

No

No, one dimension may be smaller

`cover`

No

Yes

Yes, scaled proportionally then cropped

`fill`

No

No

Yes, stretched or squished if needed

**`fit=cover` requires both `w` and `h`.** Supplying only one silently misbehaves rather than erroring, which makes it an annoying bug to chase. With `contain`, giving one dimension calculates the other to preserve the aspect ratio.

### Format negotiation

If you pass just `url` with no size or format, the image keeps its size and shape but is **still reformatted** to `avif` or `webp` based on the browser’s `Accept` header. That alone is often worth doing.

With no `fm` specified, you get `webp` if the browser accepts it, else `avif` if accepted, else the original format.

### Remote source images

Two requirements. First, allowlist the domain in `netlify.toml`:

```
[images]  remote_images = ["https://my-images.com/.*", "https://animals.more-images.com/[bcr]at/.*"]
```

Second, percent-encode the remote URL:

```
const src = `/.netlify/images?url=${encodeURIComponent("https://my-images.com/owl.jpeg")}`;
```

Always `encodeURIComponent` the remote URL, because a URL containing `?` or `&` breaks otherwise.

On the pattern syntax: **escape only the dot.** Forward slashes are not regex metacharacters, so don’t write `https:\/\/`. In `netlify.toml`, use a single-quoted literal string like `'https://example\.com/.*'`, or double the backslash in a double-quoted string (`"https://example\\.com/.*"`). A bare `\.` inside double quotes is invalid TOML.

Remote sources must be **publicly accessible.** Netlify does not forward `Authorization` or `Cookie` headers to remote sources. For images that need auth, use self-authorizing URLs like S3 presigned URLs, and make sure your `remote_images` pattern matches them.

### Reusable transformations via redirects

To reuse the same parameters across many images, put them in a redirect.

`_redirects`:

```
/transform-small/* /.netlify/images?url=/:splat&w=50&h=50 200
```

`netlify.toml`:

```
[[redirects]]  from = "/transform-small/*"  to = "/.netlify/images?url=/:splat&w=50&h=50"  status = 200
```

Then `GET /transform-small/owl.jpeg` gives you a 50×50 transform. **Avoid cross-site redirects for transformations**, which hurt performance.

### Caching headers

`_headers`:

```
/source-images/*  Cache-Control: public, max-age=604800, must-revalidate
```

Headers set on a source image apply to the transformed asset that Image CDN serves. Two limits worth knowing: custom headers **cannot** be applied to remote source images (Netlify respects whatever the external domain sends), and `Cache-Control` on source images applies only to browsers and CDNs in front of Netlify, **not** to the Netlify cache itself. For that, see How to control CDN caching on Netlify.

### Framework integrations

Framework

Prerequisite

Remote allowlist

Angular

None. `NgOptimizedImage` uses it automatically

`[images] remote_images` in `netlify.toml`

Astro

None. `<Image />` uses it automatically

`image.domains` / `image.remotePatterns` in `astro.config.mjs`

Nuxt

None. `nuxt/image` uses it automatically

`image.domains` in `nuxt.config.ts`

Next.js

Next 13.5+ and adapter v5

`remotePatterns` in `next.config.js`

Gatsby

Env `NETLIFY_IMAGE_CDN=true` plus a Contentful, Drupal, or WordPress source plugin

`[images] remote_images` in `netlify.toml`

Note the allowlist lives in a different place per framework. Setting `remote_images` in `netlify.toml` won’t help an Astro `<Image />` call; that needs `image.domains` in the Astro config.

## Common failure modes

**You get a 404 on `/.netlify/images` locally.** Almost always because a framework dev server (`vite`, `next dev`, `astro dev`) is running instead of `netlify dev`. The endpoint, `[images]` allowlisting, and image redirects only exist under `netlify dev`. Your URL is probably fine.

**`fm=blurhash` renders nothing.** It returns a BlurHash **text string, not image bytes.** Pointing an `<img src>` or a CSS background at it produces an empty box. Fetch the string server-side or ahead of time, decode it client-side with a BlurHash library ([blurha.sh](https://blurha.sh)), then load the real image as a separate request **without** `fm=blurhash`.

**Your `fit=cover` crop is wrong.** Check that you passed both `w` and `h`. With only one, it misbehaves silently.

**Your remote image 404s or won’t load.** Three things to check: is the domain in `remote_images`, did you `encodeURIComponent` the URL, and is the source publicly accessible? Netlify won’t forward `Authorization` or `Cookie` headers.

**Your `remote_images` pattern doesn’t match.** Look for over-escaping. Forward slashes aren’t metacharacters, so `https:\/\/` is wrong. And in a double-quoted TOML string, a bare `\.` is invalid; use a single-quoted string instead.

**You get a 404 on a transformation you think is valid.** Invalid transformation parameter values return `404`. Check your `fit`, `fm`, and `q` values against the allowed sets.

**Your custom cache headers aren’t applying.** They can’t be applied to remote source images. And `Cache-Control` on a source image reaches browsers and upstream CDNs, not the Netlify cache.

**Your image results differ between split-test branches.** Split Testing isn’t supported with Image CDN.

**You changed a source image and the old one is still served.** It shouldn’t be. Atomic deploys are respected, so changing a source image in a new deploy re-runs transformations on new requests.

## Reference

### Response codes

Code

Meaning

`404`

Invalid transformation parameter values

`200`

Valid, new transformation. Returns content plus `content-type`.

`304`

Previously transformed

### Format selection order

With no `fm` parameter: `webp` if the browser accepts it, else `avif` if accepted, else the original.

### Caching and deploys

Transformed results are uniquely cached on Netlify’s edge. Atomic deploys are respected: changing a source image in a new deploy re-runs transformations on new requests, so stale assets aren’t served.

### Local development

Run `netlify dev`, which mimics production including Image CDN. A framework dev server does not provide the endpoint.

### User-uploaded image pipelines

For a full user-upload pipeline composing Functions, Blobs, and Image CDN together, the source skill carries a dedicated guide. The shape is: a function receives the upload, stores the bytes in Netlify Blobs, and serves them back through `/.netlify/images` for transformation. See How to store files and objects with Netlify Blobs for the storage half.

### Limitations

-   Split Testing is not supported; image results may be inconsistent between split-test branches.
-   Not currently supported in Netlify’s HIPAA-compliant hosting offering.

## FAQs

**How do I optimize images on Netlify?** Request `/.netlify/images?url=/your-image.jpg` with parameters like `w`, `h`, `fit`, `fm`, and `q`. Or use your framework’s image component, which wires to Image CDN automatically. Even a bare `url` with no other parameters converts the image to `webp` or `avif` based on the browser.

**How do I resize an image on the fly?** Add `w` and `h` to the `/.netlify/images` request. Use `fit=cover` with both dimensions to crop to exact size, or `fit=contain` to preserve the aspect ratio.

**How do I serve images from an external domain?** Allowlist the domain in `netlify.toml` under `[images] remote_images` as a regex, then pass the URL through `encodeURIComponent` in the `url` parameter. The source has to be publicly accessible.

**Why does my `fit=cover` crop look wrong?** `fit=cover` requires both `w` and `h`. With only one it silently misbehaves.

**Why is my blurhash placeholder blank?** `fm=blurhash` returns a text string, not an image. Fetch it ahead of time, decode it client-side with a BlurHash library, then request the real image separately without `fm=blurhash`.

**Why do I get a 404 on `/.netlify/images` locally?** You’re probably running `vite`, `next dev`, or `astro dev` instead of `netlify dev`. The endpoint only exists under `netlify dev`.

**Can I apply the same transformation to many images?** Yes. Put the parameters in a redirect rule, so `/transform-small/*` rewrites to `/.netlify/images?url=/:splat&w=50&h=50` with status 200.

**What image formats can Netlify Image CDN output?** `avif`, `jpg`, `png`, `webp`, `gif`, and `blurhash` (a text string). `webp` and `gif` can be animated.

**Does Image CDN work with Next.js `next/image`?** Yes, on Next 13.5+ with adapter v5. Set your remote domains in `remotePatterns` in `next.config.js`.

## Related

-   [BlurHash](https://blurha.sh)

This article is generated from Netlify’s open-source agent guidance at [netlify/context-and-tools](https://github.com/netlify/context-and-tools), the same reference our AI coding agents use.

* * *

Got images to serve? Start at [netlify.new](https://netlify.new).