
Netlify Image Cdn
- 1.4k installs
- 31 repo stars
- Updated August 4, 2026
- netlify/context-and-tools
netlify-image-cdn is a Claude Code skill that generates responsive, optimized image markup and Netlify Image CDN transformation URLs for developers who serve images on Netlify-hosted sites.
About
netlify-image-cdn is a Netlify-focused Claude Code skill for the built-in /.netlify/images endpoint that transforms images on the fly without extra configuration for local assets. The skill walks through query parameters such as w, h, fit, and q, remote image allowlisting, clean URL rewrites, and composing user-upload uploads with Functions plus Blobs. Developers reach for netlify-image-cdn when adding responsive img markup, tuning compression quality, or wiring upload-to-CDN pipelines on Netlify. The guide covers hosted checkout-style image delivery patterns and transformation URLs developers can drop directly into HTML or framework components.
- Built-in /.netlify/images endpoint requires zero configuration for local assets
- Supports 8 query parameters including w, h, fit, fm, q for on-the-fly resizing and format conversion
- Automatic format negotiation preferring AVIF then WebP when fm parameter is omitted
- Remote image allowlisting via netlify.toml with regex patterns for external sources
- Composes with Netlify Functions and Blobs for user-uploaded image pipelines
Netlify Image Cdn by the numbers
- 1,422 all-time installs (skills.sh)
- +126 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #305 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/netlify/context-and-tools --skill netlify-image-cdnAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.4k |
|---|---|
| repo stars | ★ 31 |
| Last updated | August 4, 2026 |
| Repository | netlify/context-and-tools ↗ |
How do you optimize images on Netlify with Image CDN?
Generate responsive, optimized image markup and transformation URLs for their Netlify-hosted sites.
Who is it for?
Frontend developers shipping image-heavy sites on Netlify who need on-the-fly resize, crop, and quality transforms without a separate CDN service.
Skip if: Teams not on Netlify or projects that already rely on Cloudinary, imgix, or a self-managed image pipeline.
When should I use this skill?
A developer asks for Netlify Image CDN URLs, responsive image markup, remote image allowlisting, or upload-to-transform workflows on Netlify.
What you get
Responsive img markup, /.netlify/images transformation URLs, remote allowlist config, and upload pipeline patterns with Functions and Blobs.
- responsive img markup
- transformation URLs
- remote allowlist config
By the numbers
- Every Netlify site exposes a built-in /.netlify/images transformation endpoint
Files
Netlify Image CDN
Every Netlify site has a built-in /.netlify/images endpoint for on-the-fly image transformation. No configuration required for local images.
Basic Usage
<img src="/.netlify/images?url=/photo.jpg&w=800&h=600&fit=cover&q=80" />Query Parameters
| Param | Description | Values |
|---|---|---|
url | Source image path (required) | Relative path or absolute URL |
w | Width in pixels | Any positive integer |
h | Height in pixels | Any positive integer |
fit | Resize behavior | contain (default), cover, fill |
position | Crop alignment (with cover) | center (default), top, bottom, left, right |
fm | Output format | avif, webp, jpg, png, gif, blurhash |
q | Quality (lossy formats) | 1-100 (default: 75) |
When fm is omitted, Netlify auto-negotiates the best format based on browser support (preferring webp, then avif).
Remote Image Allowlisting
External images must be explicitly allowed in netlify.toml:
[images]
remote_images = ["https://example\\.com/.*", "https://cdn\\.images\\.com/.*"]Values are regex patterns.
When referencing an allow-listed remote image, percent-encode the source URL before placing it in the url parameter:
<!-- source: https://cdn.example.com/marketing/banner.jpg -->
<img src="/.netlify/images?url=https%3A%2F%2Fcdn.example.com%2Fmarketing%2Fbanner.jpg&w=800&fm=webp&q=80" />Percent-encode the source value (e.g. with encodeURIComponent) whenever it contains characters that would otherwise be read as Image CDN params — ?, &, =, #, or whitespace. This applies to remote URLs and relative paths alike (a filename or user-generated key can contain them too, e.g. url=/uploads/a%26b.jpg). Basic paths without those characters don't need encoding.
Clean URL Rewrites
Create user-friendly image URLs with redirects:
# Basic optimization
[[redirects]]
from = "/img/*"
to = "/.netlify/images?url=/:splat"
status = 200
# Preset: thumbnail
[[redirects]]
from = "/img/thumb/:key"
to = "/.netlify/images?url=/uploads/:key&w=150&h=150&fit=cover"
status = 200
# Preset: hero
[[redirects]]
from = "/img/hero/:key"
to = "/.netlify/images?url=/uploads/:key&w=1200&h=675&fit=cover"
status = 200Caching
- Transformed images are cached at the CDN edge automatically
- Cache invalidates on new deploys
- Set cache headers on source images to control caching:
[[headers]]
for = "/uploads/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"User-Uploaded Images
Combine Netlify Functions (upload handler) + Netlify Blobs (storage) + Image CDN (serving/transforming) to build a complete user-uploaded image pipeline. See references/user-uploads.md for the full pattern.
User-Uploaded Images Pipeline
Compose Netlify Functions (upload handler) + Netlify Blobs (storage) + Image CDN (serving/transforming) to build a complete user-uploaded image pipeline.
Architecture
1. Upload — A Netlify Function receives multipart form data, validates, and stores in Blobs 2. Storage — Netlify Blobs stores the binary image with metadata 3. Serve — A Netlify Function retrieves the blob and serves it at /uploads/:key 4. Transform — A redirect maps /img/:key to /.netlify/images?url=/uploads/:key for CDN optimization
Dependencies
npm install @netlify/blobsUpload Handler
// netlify/functions/upload.ts
import type { Context, Config } from "@netlify/functions";
import { getStore } from "@netlify/blobs";
import { randomUUID } from "crypto";
const ALLOWED_TYPES = ["image/jpeg", "image/png", "image/gif", "image/webp"];
const MAX_SIZE = 4 * 1024 * 1024; // 4 MB
export default async (req: Request, context: Context) => {
if (req.method !== "POST") {
return new Response("Method not allowed", { status: 405 });
}
const formData = await req.formData();
const image = formData.get("image") as File;
if (!image) return Response.json({ error: "No image provided" }, { status: 400 });
if (!ALLOWED_TYPES.includes(image.type)) return Response.json({ error: "Invalid type" }, { status: 400 });
if (image.size > MAX_SIZE) return Response.json({ error: "File too large" }, { status: 400 });
const extension = image.name.split(".").pop() || "jpg";
const key = `${randomUUID()}.${extension}`;
const store = getStore({ name: "images", consistency: "strong" });
await store.set(key, image, {
metadata: {
contentType: image.type,
originalFilename: image.name,
uploadedAt: new Date().toISOString(),
},
});
return Response.json({ success: true, key, url: `/img/${key}` });
};
export const config: Config = { path: "/api/upload", method: "POST" };Serve Handler
// netlify/functions/serve-image.ts
import type { Context, Config } from "@netlify/functions";
import { getStore } from "@netlify/blobs";
export default async (req: Request, context: Context) => {
const key = context.params.key;
const store = getStore({ name: "images", consistency: "strong" });
const result = await store.getWithMetadata(key, { type: "stream" });
if (!result) return new Response("Not found", { status: 404 });
return new Response(result.data, {
headers: {
"Content-Type": result.metadata?.contentType || "image/jpeg",
"Cache-Control": "public, max-age=31536000, immutable",
},
});
};
export const config: Config = { path: "/uploads/:key" };CDN Redirect
# netlify.toml
# Basic optimized URL
[[redirects]]
from = "/img/:key"
to = "/.netlify/images?url=/uploads/:key"
status = 200
# Thumbnail preset
[[redirects]]
from = "/img/thumb/:key"
to = "/.netlify/images?url=/uploads/:key&w=150&h=150&fit=cover"
status = 200
# Hero preset
[[redirects]]
from = "/img/hero/:key"
to = "/.netlify/images?url=/uploads/:key&w=1200&h=675&fit=cover"
status = 200Client-Side Upload (React Example)
function ImageUpload({ onUpload }: { onUpload: (url: string) => void }) {
const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
const formData = new FormData();
formData.append("image", file);
const res = await fetch("/api/upload", { method: "POST", body: formData });
const { url } = await res.json();
onUpload(url);
};
return <input type="file" accept="image/*" onChange={handleChange} />;
}Astro Upload (API Route)
// src/pages/api/upload.ts
import type { APIRoute } from "astro";
import { getStore } from "@netlify/blobs";
import { randomUUID } from "crypto";
export const POST: APIRoute = async ({ request, redirect }) => {
const formData = await request.formData();
const image = formData.get("image") as File;
if (!image) return new Response("No image", { status: 400 });
const key = `${randomUUID()}.${image.name.split(".").pop() || "jpg"}`;
const store = getStore({ name: "images", consistency: "strong" });
await store.set(key, image, {
metadata: { contentType: image.type, originalFilename: image.name },
});
return redirect(`/gallery?uploaded=${key}`);
};Key Points
- Always validate file type and size on the server (client validation can be bypassed)
- Use
strongconsistency on Blobs for immediate reads after writes - The serve handler's
Cache-Control: immutablemeans the CDN caches the raw image permanently — Image CDN transformations layer on top - Without
fmparameter, Netlify auto-serves AVIF or WebP based on browser support
Related skills
How it compares
Pick netlify-image-cdn when the site already deploys on Netlify and you want zero-setup transforms instead of adding an external image SaaS.
FAQ
What endpoint does Netlify Image CDN use?
Netlify Image CDN serves transformed images from the /.netlify/images endpoint on every Netlify site. Developers pass the source url plus query parameters such as w, h, fit, and q to resize, crop, and compress images on the fly.
Does Netlify Image CDN need configuration for local images?
Netlify Image CDN requires no extra configuration for local site images referenced through /.netlify/images. Remote image sources must be allowlisted before Netlify will fetch and transform third-party URLs.