Cloudflare Images
End-to-end image storage, transformation, and delivery solution on Cloudflare's global network.
Quick Navigation
-
Upload methods → references/upload.md
-
Transformations → references/transformations.md
-
Variants → references/variants.md
-
Workers binding → references/binding.md
-
Polish → references/polish.md
-
Signed URLs → references/security.md
-
Pricing → references/pricing.md
When to Use
-
Storing and delivering optimized images at scale
-
Transforming remote images on-the-fly
-
Resizing, cropping, converting image formats
-
Adding watermarks to images
-
Serving responsive images with srcset
-
Protecting images with signed URLs
-
Optimizing images from R2 storage
Two Usage Modes
Mode Description Billing
Storage in Images Upload images to Cloudflare, serve via variants Images Stored + Images Delivered
Transform remote Optimize images from any origin (R2, S3, etc.) Images Transformed (unique/30d)
Quick Start
Upload an Image (API)
curl --request POST
--url https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/images/v1
--header 'Authorization: Bearer <API_TOKEN>'
--header 'Content-Type: multipart/form-data'
--form file=@./image.jpg
Transform via URL
<img src="/cdn-cgi/image/width=400,quality=80,format=auto/uploads/hero.jpg" />
Transform via Workers
fetch(imageURL, { cf: { image: { width: 800, height: 600, fit: "cover", format: "auto", }, }, });
URL Format
https://<ZONE>/cdn-cgi/image/<OPTIONS>/<SOURCE-IMAGE>
-
<ZONE> — your Cloudflare domain
-
/cdn-cgi/image/ — fixed prefix for image transformations
-
<OPTIONS> — comma-separated: width=400,quality=80,format=auto
-
<SOURCE-IMAGE> — absolute path or full URL
Stored Images Delivery
https://imagedelivery.net/<ACCOUNT_HASH>/<IMAGE_ID>/<VARIANT_NAME>
Transformation Options
Option Description Example
width
Max width in pixels width=800
height
Max height in pixels height=600
fit
Resize mode fit=cover
format
Output format format=auto
quality
JPEG/WebP/AVIF quality 1-100 quality=85
gravity
Crop focus point gravity=face , gravity=auto
blur
Blur radius 1-250 blur=50
sharpen
Sharpening 0-10 sharpen=1
rotate
Rotation degrees rotate=90
trim
Remove pixels from edges trim=20;30;20;0
Fit Modes
Mode Behavior
scale-down
Shrink only, never enlarge
contain
Fit within dimensions, preserve aspect ratio
cover
Fill dimensions, crop if needed
crop
Like cover but never enlarges
pad
Fit within, add background color
Format Auto
format=auto serves WebP or AVIF based on browser support. Use with Accept header parsing in Workers.
Supported Formats
Input
- JPEG, PNG, GIF (animated), WebP (animated), SVG, HEIC
Output
- JPEG, PNG, GIF, WebP, AVIF, SVG (passthrough)
Note: HEIC must be served as AVIF/WebP/JPEG/PNG. SVG is not resized (inherently scalable).
Limits
Constraint Limit
Max image dimension 12,000 pixels
Max image area 100 megapixels
Max file size (transformations) 70 MB
Max file size (storage) 10 MB
GIF/WebP animation 50 megapixels total
AVIF output hard limit 1,200 px (1,600 explicit)
Variants per account 100
Workers Integration
Images Binding Setup
wrangler.toml
[images] binding = "IMAGES"
Transform with Binding
const response = (await env.IMAGES.input(stream).transform({ width: 800 }).transform({ blur: 20 }).output({ format: "image/avif" })).response();
Draw Watermark
const watermark = await fetch("https://example.com/watermark.png"); const image = await fetch("https://example.com/photo.jpg");
const response = ( await env.IMAGES.input(image.body) .draw(env.IMAGES.input(watermark.body).transform({ width: 100 }), { bottom: 10, right: 10, opacity: 0.75 }) .output({ format: "image/avif" }) ).response();
Get Image Info
const info = await env.IMAGES.info(stream); // { format, fileSize, width, height }
Recipes
Responsive Images with srcset
<img srcset="/cdn-cgi/image/width=320/photo.jpg 320w, /cdn-cgi/image/width=640/photo.jpg 640w, /cdn-cgi/image/width=1280/photo.jpg 1280w" sizes="(max-width: 640px) 100vw, 640px" src="/cdn-cgi/image/width=640/photo.jpg" alt="Responsive image" />
Face-Aware Cropping
fetch(imageURL, { cf: { image: { width: 200, height: 200, fit: "cover", gravity: "face", zoom: 0.5, // 0 = more background, 1 = tight crop }, }, });
Direct Creator Upload
Get one-time upload URL
curl --request POST
https://api.cloudflare.com/client/v4/accounts/{account_id}/images/v2/direct_upload
--header "Authorization: Bearer <API_TOKEN>"
Response: { "uploadURL": "https://upload.imagedelivery.net/..." }
Upload from Worker
const image = await fetch("https://example.com/image.png"); const bytes = await image.bytes();
const formData = new FormData(); formData.append("file", new File([bytes], "image.png"));
await fetch(https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/images/v1, {
method: "POST",
headers: { Authorization: Bearer ${TOKEN} },
body: formData,
});
Critical Prohibitions
-
NEVER set up Image Resizing Worker for entire zone (/* ) — blocks non-image requests
-
NEVER activate Polish and image transformations simultaneously — redundant compression
-
NEVER use flexible variants with signed URL tokens — incompatible
-
NEVER use custom ID paths with requireSignedURLs=true — not supported
-
NEVER expect SVG resizing — SVGs are passed through as-is
-
NEVER include resizing options in cacheKey — fragments cache
Troubleshooting
No Cf-Resized Header
-
Transformations not enabled on zone
-
Another Worker intercepting request
-
Using dashboard preview (doesn't simulate transforms)
Common Error Codes
Code Meaning
9401 Invalid transformation options
9402 Image too large
9403 Request loop detected
9422 Free tier limit exceeded (5,000/month)
9520 Unsupported format
Links
-
Documentation
-
Changelog
Related Skills
-
cloudflare-workers
-
For serverless compute
-
cloudflare-pages
-
For full-stack apps
-
cloudflare-r2
-
R2 object storage