---
name: netlify-image-cdn
description: Transform and optimize images on-demand using Netlify Image CDN. Use when implementing responsive images, format conversion, resizing, cropping, or dynamic image optimization without build-time processing.
---

# Netlify Image CDN

Netlify Image CDN transforms and optimizes images on demand at the edge without impacting build times. It handles
content negotiation to serve the most efficient format and caches transformed images for fast delivery.

## Quick Start

All Netlify projects have a `/.netlify/images` route without any additional enablement. Use it with query parameters to
transform any image:

```html
<!-- Resize to 400px wide, convert to WebP -->
<img src="/.netlify/images?url=/photo.jpg&w=400&fm=webp" alt="Optimized photo" />

<!-- Cover crop to 200x200 square -->
<img src="/.netlify/images?url=/photo.jpg&w=200&h=200&fit=cover" alt="Cropped photo" />

<!-- Transform a remote image -->
<img src="/.netlify/images?url=https://example.com/image.jpg&w=800&q=80" alt="Remote image" />
```

## Query Parameters

| Parameter | Required | Type | Description |
|-----------|----------|------|-------------|
| `url` | Yes | `string` | Source image path (relative) or URL (remote, requires config) |
| `w` | No | `integer` | Width in pixels |
| `h` | No | `integer` | Height in pixels |
| `fit` | No | `string` | Resize mode: `contain` (default), `cover`, `fill` |
| `position` | No | `string` | Crop anchor for `fit=cover`: `center` (default), `top`, `bottom`, `left`, `right` |
| `fm` | No | `string` | Output format: `avif`, `jpg`, `png`, `webp`, `gif`, `blurhash` |
| `q` | No | `integer` | Quality 1-100 for lossy formats (default: 75) |

The `url` value must be URI-encoded when it contains special characters.

## Fit Modes

| `fit=` | Aspect Ratio Maintained | Crops | Always Returns Exact Dimensions |
|--------|------------------------|-------|-------------------------------|
| `contain` (default) | Yes | No | No, one dimension may be smaller |
| `cover` | Yes | Yes | Yes, crops excess after proportional resize |
| `fill` | No | No | Yes, stretches/squishes to exact dimensions |

### `fit=contain` (default)

Resizes proportionally to fit within the specified dimensions. If only one dimension is given, the other is calculated
to maintain aspect ratio.

```html
<img src="/.netlify/images?url=/photo.jpg&w=600&h=400&fit=contain" />
```

### `fit=cover`

Resizes proportionally to fill the dimensions, then crops excess pixels. Requires **both** `w` and `h`. Use `position`
to control which area is retained.

```html
<img src="/.netlify/images?url=/photo.jpg&w=300&h=300&fit=cover&position=top" />
```

### `fit=fill`

Stretches the image to exactly match the specified dimensions. May distort the image if aspect ratios differ.

```html
<img src="/.netlify/images?url=/photo.jpg&w=400&h=200&fit=fill" />
```

## Format Negotiation

When `fm` is **not** specified, Image CDN automatically negotiates the best format using the browser's `Accept` header:

1. Use `webp` if the browser accepts it
2. Otherwise use `avif` if accepted
3. Otherwise serve the original format

Explicitly set `fm` only when a specific format is required (e.g., `blurhash` for placeholders).

## Remote Image Configuration

To transform images from external domains, allowlist them in `netlify.toml` using regex patterns:

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

Then reference the full URL:

```html
<img src="/.netlify/images?url=https://my-images.com/photo.jpg&w=400" />
```

Only absolute URLs to external servers need to be in `remote_images`. Local site images work without configuration.

## Redirects & Rewrites

If you do not want to use the default `/.netlify/images` path, a redirect or rewrite can be used to have a different
URL. When doing so, the parameters can remain parameters to pass in or can be statically defined.

**Using `netlify.toml`:**

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

**Using `_redirects` file:**

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

**Usage:**

```html
<!-- Transforms /photo.jpg with w=50&h=50 -->
<img src="/transform-small/photo.jpg" />
```

DO NOT create circular redirects where an image URL redirects to another URL that redirects back. This causes infinite
loops and errors.

## Custom Headers

Apply custom headers to source images on your site's domain. Headers propagate to the transformed output. ONLY do this
when explicitly asked.

**Using `netlify.toml`:**

```toml
[[headers]]
for = "/source-images/*"
[headers.values]
  Cache-Control = "public, max-age=604800, must-revalidate"
```

**Using `_headers` file:**

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

**Limitations:**
- Custom headers only work for images on the same domain
- Cannot apply custom headers to remote source images
- Netlify respects cache headers that external domains send with their images
- `Cache-Control` headers on source images apply to browsers/CDNs, not the Netlify cache itself

## Framework Integration

Many frameworks automatically use Netlify Image CDN when deployed to Netlify:

| Framework | Setup | Remote Image Allowlist |
|-----------|-------|----------------------|
| **Angular** | Automatic via `NgOptimizedImage` | `[images] remote_images` in `netlify.toml` |
| **Astro** | Automatic via `<Image />` component | `image.domains` or `image.remotePatterns` in `astro.config.mjs` |
| **Gatsby 5.13+** | Set env var `NETLIFY_IMAGE_CDN=true` | `[images] remote_images` in `netlify.toml` |
| **Next.js 13.5+** | Automatic with adapter v5 | `remotePatterns` in `next.config.js` |
| **Nuxt** | Automatic via `nuxt/image` module | `image.domains` in `nuxt.config.ts` |

## Caching & Deploy Behavior

- Transformed images are uniquely cached at the edge per transformation parameters
- Future requests for the same transformation serve the cached version
- After a new deploy, cached images are invalidated if the source changed
- Source images are also cached to speed up future transformations
- Use asset fingerprinting (content hashes in filenames) for fine-grained cache control

## Response Codes

| Condition | Response |
|-----------|----------|
| Valid query, new transformation | `200` with transformed image |
| Request for previously cached transformation | `304` Not Modified |
| Invalid parameter values or missing source | `404` Not Found |

## Common Errors & Solutions

### "404 on image transformation"

**Cause:** Invalid parameter values or source image not found.

**Fix:**

1. Verify the source image exists at the specified `url` path
2. Check parameter values are valid (e.g., `q` must be 1-100, `fit` must be `contain`, `cover`, or `fill`)
3. For remote images, ensure the domain is in `remote_images` config

### Blurry or low-quality output

**Cause:** Quality setting too low or dimensions too small.

**Fix:**

1. Increase the `q` parameter (default is 75, max is 100)
2. Ensure `w` and `h` are appropriate for the display size
3. Use `fm=png` for images that need lossless quality

### Remote image not loading

**Cause:** External domain not allowlisted.

**Fix:**

1. Add the domain to `remote_images` in `netlify.toml`:
   ```toml
   [images]
   remote_images = ["https://example\\.com/.*"]
   ```
2. Escape dots in regex patterns with double backslash
3. Redeploy after updating config

### Images not updating after deploy

**Cause:** Browser or CDN cache serving stale version.

**Fix:**

1. Use asset fingerprinting (e.g., `/photo-abc123.jpg`) for cache busting
2. Clear browser cache or use hard refresh
3. Wait a few minutes for edge cache propagation

### Circular redirect error

**Cause:** Redirect rule creates a loop with the image transformation endpoint.

**Fix:**

1. Ensure redirect `from` path does not overlap with the `to` path
2. Avoid redirecting `/.netlify/images` to another `/.netlify/images` URL
3. Use status `200` (rewrite) not `301`/`302` (redirect) when possible
