cloudflare-images

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cloudflare Images

Cloudflare Images

Status: Production Ready ✅ Last Updated: 2026-01-21 Dependencies: Cloudflare account with Images enabled Latest Versions: Cloudflare Images API v2, @cloudflare/workers-types@4.20260108.0
Recent Updates (2025):
  • February 2025: Content Credentials support (C2PA standard) - preserve image provenance chains, automatic cryptographic signing of transformations
  • August 2025: AI Face Cropping GA (
    gravity=face
    with
    zoom
    control, GPU-based RetinaFace, 99.4% precision)
  • May 2025: Media Transformations origin restrictions (default: same-domain only, configurable via dashboard)
  • Upcoming: Background removal, generative upscale (planned features)
Deprecation Notice: Mirage deprecated September 15, 2025. Migrate to Cloudflare Images for storage/transformations or use native
<img loading="lazy">
for lazy loading.

状态:已就绪可用于生产环境 ✅ 最后更新时间:2026-01-21 依赖项:已启用Images功能的Cloudflare账户 最新版本:Cloudflare Images API v2, @cloudflare/workers-types@4.20260108.0
2025年近期更新:
  • 2025年2月:支持内容凭证(C2PA标准)- 保留图片来源链,自动对转换操作进行加密签名
  • 2025年8月:AI人脸裁剪正式可用(
    gravity=face
    搭配
    zoom
    控制,基于GPU的RetinaFace算法,准确率99.4%)
  • 2025年5月:媒体转换源限制(默认:仅允许同域,可通过控制台配置)
  • 即将推出:背景移除、生成式放大(规划中功能)
弃用通知:Mirage已于2025年9月15日弃用。请迁移至Cloudflare Images进行存储/转换,或使用原生
<img loading="lazy">
实现懒加载。

Overview

概述

Two features: Images API (upload/store with variants) and Image Transformations (resize any image via URL or Workers).

包含两大功能:Images API(上传/存储图片并创建变体)和图片转换(通过URL或Workers调整任意图片)。

Quick Start

快速开始

1. Enable: Dashboard → Images → Get Account ID + API token (Cloudflare Images: Edit permission)
2. Upload:
bash
curl -X POST https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/images/v1 \
  -H 'Authorization: Bearer <API_TOKEN>' \
  -H 'Content-Type: multipart/form-data' \
  -F 'file=@./image.jpg'
3. Serve:
https://imagedelivery.net/<ACCOUNT_HASH>/<IMAGE_ID>/public
4. Transform (optional): Dashboard → Images → Transformations → Enable for zone
html
<img src="/cdn-cgi/image/width=800,quality=85/uploads/photo.jpg" />

1. 启用服务:进入控制台(Dashboard)→ Images → 获取账户ID和API令牌(需Cloudflare Images的编辑权限)
2. 上传图片:
bash
curl -X POST https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/images/v1 \
  -H 'Authorization: Bearer <API_TOKEN>' \
  -H 'Content-Type: multipart/form-data' \
  -F 'file=@./image.jpg'
3. 提供访问
https://imagedelivery.net/<ACCOUNT_HASH>/<IMAGE_ID>/public
4. 转换图片(可选):控制台 → Images → Transformations → 为当前区域启用该功能
html
<img src="/cdn-cgi/image/width=800,quality=85/uploads/photo.jpg" />

Upload Methods

上传方式

1. File Upload: POST to
/images/v1
with
file
(multipart/form-data), optional
id
,
requireSignedURLs
,
metadata
2. Upload via URL: POST with
url=https://example.com/image.jpg
(supports HTTP basic auth)
3. Direct Creator Upload (one-time URLs, no API key exposure):
Backend: POST to
/images/v2/direct_upload
→ returns
uploadURL
Frontend: POST file to
uploadURL
with FormData
CRITICAL CORS FIX:
  • ✅ Use
    multipart/form-data
    (let browser set header)
  • ✅ Name field
    file
    (NOT
    image
    )
  • ✅ Call
    /direct_upload
    from backend only
  • ❌ Don't set
    Content-Type: application/json
  • ❌ Don't call
    /direct_upload
    from browser

1. 文件上传:向
/images/v1
发送POST请求,携带
file
参数(multipart/form-data格式),可选参数包括
id
requireSignedURLs
metadata
2. 通过URL上传:发送POST请求并携带
url=https://example.com/image.jpg
(支持HTTP基础认证)
3. 创作者直接上传(一次性URL,无需暴露API密钥):
后端:向
/images/v2/direct_upload
发送POST请求 → 返回
uploadURL
前端:向
uploadURL
发送包含FormData的POST请求以上传文件
关键CORS修复:
  • ✅ 使用
    multipart/form-data
    格式(由浏览器自动设置请求头)
  • ✅ 文件字段命名为
    file
    (不能是
    image
  • ✅ 仅从后端调用
    /direct_upload
    接口(禁止从浏览器调用)
  • ❌ 不要设置
    Content-Type: application/json
  • ❌ 不要从浏览器调用
    /direct_upload

Image Transformations

图片转换

URL:
/cdn-cgi/image/<OPTIONS>/<SOURCE>
  • Sizing:
    width=800,height=600,fit=cover
  • Quality:
    quality=85
    (1-100)
  • Format:
    format=auto
    (WebP/AVIF auto-detection)
  • Cropping:
    gravity=auto
    (smart crop),
    gravity=face
    (AI face detection, Aug 2025 GA),
    gravity=center
    ,
    zoom=0.5
    (0-1 range, face crop tightness)
  • Effects:
    blur=10,sharpen=3,brightness=1.2
  • Fit:
    scale-down
    ,
    contain
    ,
    cover
    ,
    crop
    ,
    pad
Workers: Use
cf.image
object in fetch
typescript
fetch(imageURL, {
  cf: {
    image: { width: 800, quality: 85, format: 'auto', gravity: 'face', zoom: 0.8 }
  }
});

URL格式
/cdn-cgi/image/<OPTIONS>/<SOURCE>
  • 尺寸调整:
    width=800,height=600,fit=cover
  • 质量设置:
    quality=85
    (范围1-100)
  • 格式:
    format=auto
    (自动检测并转换为WebP/AVIF)
  • 裁剪
    gravity=auto
    (智能裁剪)、
    gravity=face
    (AI人脸检测,2025年8月正式可用)、
    gravity=center
    zoom=0.5
    (范围0-1,控制人脸裁剪的紧凑程度)
  • 特效:
    blur=10,sharpen=3,brightness=1.2
  • 适配方式:
    scale-down
    contain
    cover
    crop
    pad
Workers中使用:在fetch请求中使用
cf.image
对象
typescript
fetch(imageURL, {
  cf: {
    image: { width: 800, quality: 85, format: 'auto', gravity: 'face', zoom: 0.8 }
  }
});

Variants

图片变体

Named Variants (up to 100): Predefined transformations (e.g.,
avatar
,
thumbnail
)
  • Create: POST to
    /images/v1/variants
    with
    id
    ,
    options
  • Use:
    imagedelivery.net/<HASH>/<ID>/avatar
  • Works with signed URLs
Flexible Variants: Dynamic params in URL (
w=400,sharpen=3
)
  • Enable: PATCH
    /images/v1/config
    with
    {"flexible_variants": true}
  • Cannot use with signed URLs (use named variants instead)

命名变体(最多100个):预定义的转换规则(例如
avatar
thumbnail
  • 创建:向
    /images/v1/variants
    发送POST请求,携带
    id
    options
  • 使用:
    imagedelivery.net/<HASH>/<ID>/avatar
  • 支持签名URL
灵活变体:在URL中传递动态参数(
w=400,sharpen=3
  • 启用:向
    /images/v1/config
    发送PATCH请求,携带
    {"flexible_variants": true}
  • 不能与签名URL配合使用(请改用命名变体)

Signed URLs

签名URL

Generate HMAC-SHA256 tokens for private images (URL format:
?exp=<TIMESTAMP>&sig=<HMAC>
).
Algorithm:
HMAC-SHA256(signingKey, imageId + variant + expiry)
→ hex signature
See:
templates/signed-urls-generation.ts
for Workers implementation

为私有图片生成HMAC-SHA256令牌(URL格式:
?exp=<TIMESTAMP>&sig=<HMAC>
)。
算法
HMAC-SHA256(signingKey, imageId + variant + expiry)
→ 十六进制签名
参考实现
templates/signed-urls-generation.ts
中的Workers实现代码

Critical Rules

关键规则

Always Do

必须遵守

✅ Use
multipart/form-data
for Direct Creator Upload ✅ Name the file field
file
(not
image
or other names) ✅ Call
/direct_upload
API from backend only (NOT browser) ✅ Use HTTPS URLs for transformations (HTTP not supported) ✅ URL-encode special characters in image paths ✅ Enable transformations on zone before using
/cdn-cgi/image/
✅ Use named variants for private images (signed URLs) ✅ Check
Cf-Resized
header for transformation errors ✅ Set
format=auto
for automatic WebP/AVIF conversion ✅ Use
fit=scale-down
to prevent unwanted enlargement
✅ 创作者直接上传时使用
multipart/form-data
格式 ✅ 文件字段命名为
file
(不能是
image
或其他名称) ✅ 仅从后端调用
/direct_upload
接口(禁止从浏览器调用) ✅ 转换图片时使用HTTPS URL(不支持HTTP) ✅ 对图片路径中的特殊字符进行URL编码 ✅ 使用
/cdn-cgi/image/
前先为当前区域启用转换功能 ✅ 私有图片使用命名变体(配合签名URL) ✅ 检查
Cf-Resized
响应头排查转换错误 ✅ 设置
format=auto
以自动转换为WebP/AVIF格式 ✅ 使用
fit=scale-down
避免图片被意外放大

Never Do

禁止操作

❌ Use
application/json
Content-Type for file uploads ❌ Call
/direct_upload
from browser (CORS will fail) ❌ Use flexible variants with
requireSignedURLs=true
❌ Resize SVG files (they're inherently scalable) ❌ Use HTTP URLs for transformations (HTTPS only) ❌ Put spaces or unescaped Unicode in URLs ❌ Transform the same image multiple times in Workers (causes 9403 loop) ❌ Exceed 100 megapixels image size ❌ Use
/cdn-cgi/image/
endpoint in Workers (use
cf.image
instead) ❌ Forget to enable transformations on zone before use

❌ 文件上传时使用
application/json
作为Content-Type ❌ 从浏览器调用
/direct_upload
(会触发CORS错误) ❌ 在
requireSignedURLs=true
时使用灵活变体 ❌ 调整SVG文件大小(SVG本身是矢量格式,可无损缩放) ❌ 转换图片时使用HTTP URL(仅支持HTTPS) ❌ 在URL中包含空格或未转义的Unicode字符 ❌ 在Workers中多次转换同一张图片(会导致9403循环错误) ❌ 上传超过1亿像素的图片 ❌ 在Workers中使用
/cdn-cgi/image/
端点(请使用
cf.image
) ❌ 使用转换功能前忘记为当前区域启用该功能

Known Issues Prevention

已知问题预防

This skill prevents 16 documented issues.
本技能可预防16个已记录的问题。

Issue #1: Direct Creator Upload CORS Error

问题1:创作者直接上传出现CORS错误

Error:
Access to XMLHttpRequest blocked by CORS policy: Request header field content-type is not allowed
Why It Happens: Server CORS settings only allow
multipart/form-data
for Content-Type header
Prevention:
javascript
// ✅ CORRECT
const formData = new FormData();
formData.append('file', fileInput.files[0]);
await fetch(uploadURL, {
  method: 'POST',
  body: formData // Browser sets multipart/form-data automatically
});

// ❌ WRONG
await fetch(uploadURL, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' }, // CORS error
  body: JSON.stringify({ file: base64Image })
});
错误信息
Access to XMLHttpRequest blocked by CORS policy: Request header field content-type is not allowed
原因:服务器CORS设置仅允许
multipart/form-data
作为Content-Type请求头
解决方案:
javascript
// ✅ 正确做法
const formData = new FormData();
formData.append('file', fileInput.files[0]);
await fetch(uploadURL, {
  method: 'POST',
  body: formData // 浏览器自动设置multipart/form-data
});

// ❌ 错误做法
await fetch(uploadURL, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' }, // 触发CORS错误
  body: JSON.stringify({ file: base64Image })
});

Issue #2: Error 5408 - Upload Timeout

问题2:错误5408 - 上传超时

Error:
Error 5408
after ~15 seconds of upload
Why It Happens: Cloudflare has 30-second request timeout; slow uploads or large files exceed limit
Prevention:
  • Compress images before upload (client-side with Canvas API)
  • Use reasonable file size limits (e.g., max 10MB)
  • Show upload progress to user
  • Handle timeout errors gracefully
javascript
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB

if (file.size > MAX_FILE_SIZE) {
  alert('File too large. Please select an image under 10MB.');
  return;
}
错误信息:上传约15秒后出现
Error 5408
原因:Cloudflare的请求超时时间为30秒;上传速度过慢或文件过大导致超时
解决方案:
  • 上传前在客户端使用Canvas API压缩图片
  • 设置合理的文件大小限制(例如最大10MB)
  • 向用户展示上传进度
  • 优雅处理超时错误
javascript
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB

if (file.size > MAX_FILE_SIZE) {
  alert('文件过大,请选择10MB以下的图片。');
  return;
}

Issue #3: Error 400 - Invalid File Parameter

问题3:错误400 - 无效的文件参数

Error:
400 Bad Request
with unhelpful error message
Why It Happens: File field must be named
file
(not
image
,
photo
, etc.)
Prevention:
javascript
// ✅ CORRECT
formData.append('file', imageFile);

// ❌ WRONG
formData.append('image', imageFile); // 400 error
formData.append('photo', imageFile); // 400 error
错误信息
400 Bad Request
,且错误信息不明确
原因:文件字段必须命名为
file
(不能是
image
photo
等)
解决方案:
javascript
// ✅ 正确做法
formData.append('file', imageFile);

// ❌ 错误做法
formData.append('image', imageFile); // 触发400错误
formData.append('photo', imageFile); // 触发400错误

Issue #4: CORS Preflight Failures

问题4:CORS预检请求失败

Error: Preflight OPTIONS request blocked
Why It Happens: Calling
/direct_upload
API directly from browser (should be backend-only)
Prevention:
ARCHITECTURE:
Browser → Backend API → POST /direct_upload → Returns uploadURL → Browser uploads to uploadURL
Never expose API token to browser. Generate upload URL on backend, return to frontend.
错误信息:预检OPTIONS请求被阻止
原因:直接从浏览器调用
/direct_upload
接口(该接口仅允许后端调用)
解决方案:
架构流程:
浏览器 → 后端API → 调用POST /direct_upload → 返回uploadURL → 浏览器向uploadURL上传文件
绝对不要向浏览器暴露API令牌。在后端生成上传URL,再返回给前端使用。

Issue #5: Error 9401 - Invalid Arguments

问题5:错误9401 - 参数无效

Error:
Cf-Resized: err=9401
- Required cf.image options missing or invalid
Why It Happens: Missing required transformation parameters or invalid values
Prevention:
typescript
// ✅ CORRECT
fetch(imageURL, {
  cf: {
    image: {
      width: 800,
      quality: 85,
      format: 'auto'
    }
  }
});

// ❌ WRONG
fetch(imageURL, {
  cf: {
    image: {
      width: 'large', // Must be number
      quality: 150 // Max 100
    }
  }
});
错误信息
Cf-Resized: err=9401
- 缺少必需的cf.image参数或参数无效
原因:缺少必需的转换参数或参数值无效
解决方案:
typescript
// ✅ 正确做法
fetch(imageURL, {
  cf: {
    image: {
      width: 800,
      quality: 85,
      format: 'auto'
    }
  }
});

// ❌ 错误做法
fetch(imageURL, {
  cf: {
    image: {
      width: 'large', // 必须是数字
      quality: 150 // 最大值为100
    }
  }
});

Issue #6: Error 9402 - Image Too Large

问题6:错误9402 - 图片过大

Error:
Cf-Resized: err=9402
- Image too large or connection interrupted
Why It Happens: Image exceeds maximum area (100 megapixels) or download fails
Prevention:
  • Validate image dimensions before transforming
  • Use reasonable source images (max 10000x10000px)
  • Handle network errors gracefully
错误信息
Cf-Resized: err=9402
- 图片过大或连接中断
原因:图片超过最大面积限制(1亿像素)或下载失败
解决方案:
  • 转换前验证图片尺寸
  • 使用合理尺寸的源图片(最大10000x10000像素)
  • 优雅处理网络错误

Issue #7: Error 9403 - Request Loop

问题7:错误9403 - 请求循环

Error:
Cf-Resized: err=9403
- Worker fetching its own URL or already-resized image
Why It Happens: Transformation applied to already-transformed image, or Worker fetches itself
Prevention:
typescript
// ✅ CORRECT
if (url.pathname.startsWith('/images/')) {
  const originalPath = url.pathname.replace('/images/', '');
  const originURL = `https://storage.example.com/${originalPath}`;
  return fetch(originURL, { cf: { image: { width: 800 } } });
}

// ❌ WRONG
if (url.pathname.startsWith('/images/')) {
  // Fetches worker's own URL, causes loop
  return fetch(request, { cf: { image: { width: 800 } } });
}
错误信息
Cf-Resized: err=9403
- Worker请求自身URL或已转换过的图片
原因:对已转换的图片再次进行转换,或Worker请求自身URL
解决方案:
typescript
// ✅ 正确做法
if (url.pathname.startsWith('/images/')) {
  const originalPath = url.pathname.replace('/images/', '');
  const originURL = `https://storage.example.com/${originalPath}`;
  return fetch(originURL, { cf: { image: { width: 800 } } });
}

// ❌ 错误做法
if (url.pathname.startsWith('/images/')) {
  // 请求Worker自身URL,导致循环
  return fetch(request, { cf: { image: { width: 800 } } });
}

Issue #8: Error 9406/9419 - Invalid URL Format

问题8:错误9406/9419 - URL格式无效

Error:
Cf-Resized: err=9406
or
err=9419
- Non-HTTPS URL or URL has spaces/unescaped Unicode
Why It Happens: Image URL uses HTTP (not HTTPS) or contains invalid characters
Prevention:
typescript
// ✅ CORRECT
const imageURL = "https://example.com/images/photo%20name.jpg";

// ❌ WRONG
const imageURL = "http://example.com/images/photo.jpg"; // HTTP not allowed
const imageURL = "https://example.com/images/photo name.jpg"; // Space not encoded
Always use
encodeURIComponent()
for URL paths:
typescript
const filename = "photo name.jpg";
const imageURL = `https://example.com/images/${encodeURIComponent(filename)}`;
错误信息
Cf-Resized: err=9406
err=9419
- 使用非HTTPS URL或URL包含空格/未转义的Unicode字符
原因:图片URL使用HTTP(而非HTTPS)或包含无效字符
解决方案:
typescript
// ✅ 正确做法
const imageURL = "https://example.com/images/photo%20name.jpg";

// ❌ 错误做法
const imageURL = "http://example.com/images/photo.jpg"; // 不允许使用HTTP
const imageURL = "https://example.com/images/photo name.jpg"; // 空格未编码
始终使用
encodeURIComponent()
处理URL路径:
typescript
const filename = "photo name.jpg";
const imageURL = `https://example.com/images/${encodeURIComponent(filename)}`;

Issue #9: Error 9412 - Non-Image Response

问题9:错误9412 - 响应不是图片

Error:
Cf-Resized: err=9412
- Origin returned HTML instead of image
Why It Happens: Origin server returns 404 page or error page (HTML) instead of image
Prevention:
typescript
// Verify URL before transforming
const originResponse = await fetch(imageURL, { method: 'HEAD' });
const contentType = originResponse.headers.get('content-type');

if (!contentType?.startsWith('image/')) {
  return new Response('Not an image', { status: 400 });
}

return fetch(imageURL, { cf: { image: { width: 800 } } });
错误信息
Cf-Resized: err=9412
- 源服务器返回HTML而非图片
原因:源服务器返回404页面或错误页面(HTML)而非图片
解决方案:
typescript
// 转换前验证URL
const originResponse = await fetch(imageURL, { method: 'HEAD' });
const contentType = originResponse.headers.get('content-type');

if (!contentType?.startsWith('image/')) {
  return new Response('不是图片文件', { status: 400 });
}

return fetch(imageURL, { cf: { image: { width: 800 } } });

Issue #10: Error 9413 - Max Image Area Exceeded

问题10:错误9413 - 图片面积超过最大值

Error:
Cf-Resized: err=9413
- Image exceeds 100 megapixels
Why It Happens: Source image dimensions exceed 100 megapixels (e.g., 10000x10000px)
Prevention:
  • Validate image dimensions before upload
  • Pre-process oversized images
  • Reject images above threshold
typescript
const MAX_MEGAPIXELS = 100;

if (width * height > MAX_MEGAPIXELS * 1_000_000) {
  return new Response('Image too large', { status: 413 });
}
错误信息
Cf-Resized: err=9413
- 图片超过1亿像素
原因:源图片尺寸超过1亿像素(例如10000x10000像素)
解决方案:
  • 上传前验证图片尺寸
  • 预处理超大尺寸图片
  • 拒绝超过阈值的图片
typescript
const MAX_MEGAPIXELS = 100;

if (width * height > MAX_MEGAPIXELS * 1_000_000) {
  return new Response('图片尺寸过大', { status: 413 });
}

Issue #11: Flexible Variants + Signed URLs Incompatibility

问题11:灵活变体与签名URL不兼容

Error: Flexible variants don't work with private images
Why It Happens: Flexible variants cannot be used with
requireSignedURLs=true
Prevention:
typescript
// ✅ CORRECT - Use named variants for private images
await uploadImage({
  file: imageFile,
  requireSignedURLs: true // Use named variants: /public, /avatar, etc.
});

// ❌ WRONG - Flexible variants don't support signed URLs
// Cannot use: /w=400,sharpen=3 with requireSignedURLs=true
错误信息:灵活变体无法与私有图片配合使用
原因:灵活变体不能与
requireSignedURLs=true
配合使用
解决方案:
typescript
// ✅ 正确做法 - 私有图片使用命名变体
await uploadImage({
  file: imageFile,
  requireSignedURLs: true // 使用命名变体:/public, /avatar等
});

// ❌ 错误做法 - 灵活变体不支持签名URL
// 不能在requireSignedURLs=true时使用:/w=400,sharpen=3

Issue #12: SVG Resizing Limitation

问题12:SVG尺寸调整限制

Error: SVG files don't resize via transformations
Why It Happens: SVG is inherently scalable (vector format), resizing not applicable
Prevention:
typescript
// SVGs can be served but not resized
// Use any variant name as placeholder
// https://imagedelivery.net/<HASH>/<SVG_ID>/public

// SVG will be served at original size regardless of variant settings
错误信息:无法通过转换功能调整SVG文件大小
原因:SVG是矢量格式,本身支持无损缩放,因此尺寸调整不适用
解决方案:
typescript
// SVG可以被提供但不能调整大小
// 使用任意变体名称作为占位符
// https://imagedelivery.net/<HASH>/<SVG_ID>/public

// SVG将以原始尺寸提供,不受变体设置影响

Issue #13: EXIF Metadata Stripped by Format

问题13:格式转换导致EXIF元数据丢失

Error: GPS data, camera settings removed from uploaded images
Why It Happens: Default behavior strips all metadata except copyright. CRITICAL: WebP and PNG output formats ALWAYS discard metadata regardless of settings - only JPEG supports metadata preservation.
Prevention:
typescript
// ✅ CORRECT - JPEG preserves metadata
fetch(imageURL, {
  cf: {
    image: {
      width: 800,
      format: 'jpeg', // or 'auto' (may become jpeg)
      metadata: 'keep' // Preserves most EXIF including GPS
    }
  }
});

// ❌ WRONG - WebP/PNG ignore metadata setting
fetch(imageURL, {
  cf: {
    image: {
      format: 'webp',
      metadata: 'keep' // NO EFFECT - always stripped for WebP/PNG
    }
  }
});
Metadata Options (JPEG only):
  • none
    : Strip all metadata
  • copyright
    : Keep only copyright tag (default for JPEG)
  • keep
    : Preserve most EXIF metadata including GPS
Format Support:
  • ✅ JPEG: All metadata options work
  • ❌ WebP: Always strips metadata (acts as
    none
    )
  • ❌ PNG: Always strips metadata (acts as
    none
    )
错误信息:上传的图片丢失GPS数据、相机设置等元数据
原因:默认行为会剥离除版权信息外的所有元数据。重点:WebP和PNG输出格式无论如何设置都会丢弃元数据 - 只有JPEG格式支持保留元数据。
解决方案:
typescript
// ✅ 正确做法 - JPEG格式保留元数据
fetch(imageURL, {
  cf: {
    image: {
      width: 800,
      format: 'jpeg', // 或'auto'(可能自动转为jpeg)
      metadata: 'keep' // 保留大部分EXIF元数据,包括GPS信息
    }
  }
});

// ❌ 错误做法 - WebP/PNG忽略元数据设置
fetch(imageURL, {
  cf: {
    image: {
      format: 'webp',
      metadata: 'keep' // 无效 - WebP始终会剥离元数据
    }
  }
});
元数据选项(仅适用于JPEG):
  • none
    : 剥离所有元数据
  • copyright
    : 仅保留版权标签(JPEG默认设置)
  • keep
    : 保留大部分EXIF元数据,包括GPS信息
格式支持:
  • ✅ JPEG: 所有元数据选项均有效
  • ❌ WebP: 始终剥离元数据(等同于
    none
  • ❌ PNG: 始终剥离元数据(等同于
    none

Issue #14: AVIF Resolution Limit Ambiguity

问题14:AVIF分辨率限制不明确

Error: Large AVIF transformations fail or degrade to lower resolution
Why It Happens: Official docs state 1,600px hard limit for
format=avif
, but community reports indicate practical limit of 1200px for longest side as of late 2024. Note: Discrepancy between official docs (1600px) and reported behavior (1200px) needs verification.
Prevention:
typescript
// ✅ RECOMMENDED - Use format=auto instead of explicit avif
fetch(imageURL, {
  cf: {
    image: {
      width: 2000,
      format: 'auto' // Cloudflare chooses best format
    }
  }
});

// ⚠️ MAY FAIL - Explicit AVIF with large dimensions
fetch(imageURL, {
  cf: {
    image: {
      width: 2000,
      format: 'avif' // May fail if >1200px
    }
  }
});

// ✅ WORKAROUND - Use WebP for larger images
if (width > 1200) {
  format = 'webp'; // WebP supports larger dimensions
} else {
  format = 'avif'; // AVIF for smaller images
}
错误信息:大尺寸AVIF转换失败或分辨率降低
原因:官方文档说明
format=avif
的硬限制为1600像素,但社区反馈截至2024年末,实际最长边限制为1200像素。注意:官方文档(1600px)与实际行为(1200px)存在差异,需进一步验证。
解决方案:
typescript
// ✅ 推荐做法 - 使用format=auto而非显式设置avif
fetch(imageURL, {
  cf: {
    image: {
      width: 2000,
      format: 'auto' // Cloudflare自动选择最佳格式
    }
  }
});

// ⚠️ 可能失败 - 显式设置AVIF且尺寸较大
fetch(imageURL, {
  cf: {
    image: {
      width: 2000,
      format: 'avif' // 若尺寸超过1200px可能失败
    }
  }
});

// ✅ 替代方案 - 大尺寸图片使用WebP
if (width > 1200) {
  format = 'webp'; // WebP支持更大尺寸
} else {
  format = 'avif'; // 小尺寸图片使用AVIF
}

Issue #15: Image Cache by Device Type (Community-reported)

问题15:按设备类型缓存图片(社区反馈)

Error: Desktop and mobile see different cached versions, unexpected cache misses
Why It Happens: Images are cached by device type unexpectedly with no configuration option to disable this behavior in cache rules. Multiple users confirm this behavior as of Dec 2024.
Prevention:
  • Understand that purging cache requires purging for each device type
  • Use manual cache purging per device type via Cloudflare dashboard
  • Account for device-specific caching in cache invalidation strategies
Solution/Workaround: Manual cache purging per device type via Cloudflare dashboard when needed.
错误信息:桌面端和移动端看到不同的缓存版本,出现意外的缓存未命中
原因:图片会按设备类型进行意外缓存,且缓存规则中没有禁用该行为的配置选项。截至2024年12月,已有多位用户确认此行为。
解决方案:
  • 了解清除缓存时需要针对每个设备类型分别操作
  • 通过Cloudflare控制台手动按设备类型清除缓存
  • 在缓存失效策略中考虑设备特定缓存的影响
解决方法/替代方案: 必要时通过Cloudflare控制台手动按设备类型清除缓存。

Issue #16: PNG Not Cached by Default

问题16:PNG图片默认不被缓存

Error: PNG images not caching despite other image formats working
Why It Happens: Default file extensions cached by Cloudflare do NOT include
.png
. Default cached extensions:
.jpg
,
.jpeg
,
.gif
,
.webp
,
.bmp
,
.ico
,
.svg
,
.tif
,
.tiff
(PNG is missing).
Prevention: Create explicit cache rule for PNG files:
  1. Dashboard → Caching → Cache Rules
  2. Add rule:
    URI Path
    ends with
    .png
    → Cache Everything

错误信息:PNG图片未被缓存,其他图片格式正常
原因:Cloudflare默认缓存的文件扩展名不包含
.png
。默认缓存的扩展名包括:
.jpg
,
.jpeg
,
.gif
,
.webp
,
.bmp
,
.ico
,
.svg
,
.tif
,
.tiff
(缺少PNG)。
解决方案: 为PNG文件创建显式缓存规则:
  1. 控制台 → 缓存 → 缓存规则
  2. 添加规则:
    URI路径
    结尾为
    .png
    → 缓存所有内容

Using Bundled Resources

使用捆绑资源

Templates (templates/)

模板(templates/)

Copy-paste ready code for common patterns:
  1. wrangler-images-binding.jsonc - Wrangler configuration (no binding needed)
  2. upload-api-basic.ts - Upload file to Images API
  3. upload-via-url.ts - Ingest image from external URL
  4. direct-creator-upload-backend.ts - Generate one-time upload URLs
  5. direct-creator-upload-frontend.html - User upload form
  6. transform-via-url.ts - URL transformation examples
  7. transform-via-workers.ts - Workers transformation patterns
  8. variants-management.ts - Create/list/delete variants
  9. signed-urls-generation.ts - HMAC-SHA256 signed URL generation
  10. responsive-images-srcset.html - Responsive image patterns
  11. batch-upload.ts - Batch API for high-volume uploads
Usage:
bash
cp templates/upload-api-basic.ts src/upload.ts
可直接复制使用的常见场景代码:
  1. wrangler-images-binding.jsonc - Wrangler配置文件(无需绑定)
  2. upload-api-basic.ts - 向Images API上传文件
  3. upload-via-url.ts - 从外部URL导入图片
  4. direct-creator-upload-backend.ts - 生成一次性上传URL
  5. direct-creator-upload-frontend.html - 用户上传表单
  6. transform-via-url.ts - URL转换示例
  7. transform-via-workers.ts - Workers转换模式
  8. variants-management.ts - 创建/列出/删除变体
  9. signed-urls-generation.ts - HMAC-SHA256签名URL生成
  10. responsive-images-srcset.html - 响应式图片实现模式
  11. batch-upload.ts - 高批量上传的批量API使用
使用方法:
bash
cp templates/upload-api-basic.ts src/upload.ts

Edit with your account ID and API token

替换为你的账户ID和API令牌

undefined
undefined

References (references/)

参考文档(references/)

In-depth documentation Claude can load as needed:
  1. api-reference.md - Complete API endpoints (upload, list, delete, variants)
  2. transformation-options.md - All transform params with examples
  3. variants-guide.md - Named vs flexible variants, when to use each
  4. signed-urls-guide.md - HMAC-SHA256 implementation details
  5. direct-upload-complete-workflow.md - Full architecture and flow
  6. responsive-images-patterns.md - srcset, sizes, art direction
  7. format-optimization.md - WebP/AVIF auto-conversion strategies
  8. top-errors.md - All 13+ errors with detailed troubleshooting
When to load:
  • Deep-dive into specific feature
  • Troubleshooting complex issues
  • Understanding API details
  • Implementing advanced patterns
Claude可按需加载的深度文档:
  1. api-reference.md - 完整API端点说明(上传、列出、删除、变体)
  2. transformation-options.md - 所有转换参数及示例
  3. variants-guide.md - 命名变体与灵活变体的对比及适用场景
  4. signed-urls-guide.md - HMAC-SHA256实现细节
  5. direct-upload-complete-workflow.md - 完整架构及流程
  6. responsive-images-patterns.md - srcset、sizes、图片艺术指导
  7. format-optimization.md - WebP/AVIF自动转换策略
  8. top-errors.md - 所有13+错误的详细故障排查
加载时机:
  • 深入研究特定功能
  • 排查复杂问题
  • 了解API细节
  • 实现高级模式

Scripts (scripts/)

脚本(scripts/)

check-versions.sh - Verify API endpoints are current

check-versions.sh - 验证API端点是否为最新版本

Advanced Topics

高级主题

Custom Domains: Serve from your domain via
/cdn-cgi/imagedelivery/<HASH>/<ID>/<VARIANT>
(requires domain on Cloudflare, proxied). Use Transform Rules for custom paths.
Batch API: High-volume uploads via
batch.imagedelivery.net
with batch tokens (Dashboard → Images → Batch API)
Webhooks: Notifications for Direct Creator Upload (Dashboard → Notifications → Webhooks). Payload includes
imageId
,
status
,
metadata
.
Content Credentials (C2PA): Cloudflare Images preserves and signs image provenance chains. When transforming images with Content Credentials, Cloudflare automatically appends transformations to the manifest and cryptographically signs them using DigiCert certificates. Enable via Dashboard → Images → Transformations → Preserve Content Credentials. Note: Only works if source images already contain C2PA metadata (certain cameras, DALL-E, compatible editing software). Verify at contentcredentials.org/verify.
Browser Cache TTL: Default Browser Cache TTL is 2 days (172,800 seconds). Customizable from 1 hour to 1 year (account-level or per-variant). Important: Private images (signed URLs) do NOT respect TTL settings. Can cause issues when re-uploading images with same Custom ID - users may see old version for up to 2 days. Solution: Use unique image IDs (append timestamp/hash) or manually purge cache after re-upload.
Product Merge Migration (Nov 2023): Cloudflare merged Image Resizing into Images product with new pricing: $0.50 per 1,000 unique transformations monthly (billing once per 30 days per unique transformation). Migration Gotchas: Old Image Resizing bills based on uncached requests (unpredictable costs due to variable cache behavior). New billing is per "unique transformation" (image ID + params combination), not request count. Same transformation requested 1,000 times/month = billed once. Existing customers can continue using legacy version or migrate voluntarily.
R2 Integration for Cost Optimization: Cloudflare Images is built on R2 + Image Resizing. For cost savings, store original images in R2 and use Image Transformations on-demand:
  1. Upload images to R2 bucket
  2. Serve via custom domain with
    /cdn-cgi/image/
    transformations
  3. No variants stored (transformations are ephemeral/cached)
Cost Comparison:
  • R2: 10GB storage + unlimited bandwidth (free tier)
  • Cloudflare Images: $5/month (100k images) + $1 per 100k transformations
  • R2 + Transformations: R2 storage + $0.50 per 1k unique transformations
Use Case: If you don't need named variants, batch uploads, or Direct Creator Upload features, R2 is more cost-effective.
Example Workers Pattern with R2:
typescript
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const imageKey = url.pathname.replace('/images/', '');
    const originURL = `https://r2-bucket.example.com/${imageKey}`;

    return fetch(originURL, {
      cf: {
        image: {
          width: 800,
          quality: 85,
          format: 'auto'
        }
      }
    });
  }
};

自定义域名:通过
/cdn-cgi/imagedelivery/<HASH>/<ID>/<VARIANT>
从你的域名提供图片访问(要求域名已接入Cloudflare且处于代理模式)。可使用转换规则配置自定义路径。
批量API:通过
batch.imagedelivery.net
使用批量令牌进行高批量上传(控制台 → Images → Batch API)
Webhooks:创作者直接上传的通知(控制台 → 通知 → Webhooks)。Payload包含
imageId
status
metadata
内容凭证(C2PA):Cloudflare Images保留并签名图片来源链。当转换包含内容凭证的图片时,Cloudflare会自动将转换操作添加到清单中,并使用DigiCert证书进行加密签名。可通过控制台 → Images → Transformations → 保留内容凭证启用该功能。注意:仅当源图片已包含C2PA元数据时有效(部分相机、DALL-E、兼容的编辑软件支持)。可在contentcredentials.org/verify验证。
浏览器缓存TTL:默认浏览器缓存TTL为2天(172800秒)。可在账户级别或变体级别自定义,范围从1小时到1年。重点:私有图片(签名URL)不遵守TTL设置。如果使用相同的自定义ID重新上传图片,用户可能会在长达2天内看到旧版本。解决方案:使用唯一的图片ID(附加时间戳/哈希值)或重新上传后手动清除缓存。
产品合并迁移(2023年11月):Cloudflare将Image Resizing合并到Images产品中,新定价为:每月每1000次唯一转换收费0.5美元(每30天按唯一转换次数计费)。迁移注意事项:旧版Image Resizing根据未缓存请求次数计费(由于缓存行为不可预测,成本可能波动)。新定价按“唯一转换”次数计费(图片ID+参数的组合),同一转换每月被请求1000次仅计费1次。现有客户可继续使用旧版本或自愿迁移。
R2集成优化成本:Cloudflare Images基于R2 + Image Resizing构建。为节省成本,可将原始图片存储在R2中,按需使用转换功能:
  1. 将图片上传到R2存储桶
  2. 通过自定义域名配合
    /cdn-cgi/image/
    转换功能提供访问
  3. 无需存储变体(转换操作是临时的/可缓存的)
成本对比:
  • R2:10GB存储 + 无限带宽(免费层)
  • Cloudflare Images:每月5美元(10万张图片) + 每10万次转换收费1美元
  • R2 + 转换功能:R2存储费用 + 每1000次唯一转换收费0.5美元
适用场景:如果不需要命名变体、批量上传或创作者直接上传功能,R2更具成本效益。
Workers与R2集成示例:
typescript
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    const imageKey = url.pathname.replace('/images/', '');
    const originURL = `https://r2-bucket.example.com/${imageKey}`;

    return fetch(originURL, {
      cf: {
        image: {
          width: 800,
          quality: 85,
          format: 'auto'
        }
      }
    });
  }
};

Troubleshooting

故障排查

Problem: Images not transforming

问题:图片未被转换

Symptoms:
/cdn-cgi/image/...
returns original image or 404
Solutions:
  1. Enable transformations on zone: Dashboard → Images → Transformations → Enable for zone
  2. Verify zone is proxied through Cloudflare (orange cloud)
  3. Check source image is publicly accessible
  4. Wait 5-10 minutes for settings to propagate
症状
/cdn-cgi/image/...
返回原始图片或404错误
解决方案:
  1. 为当前区域启用转换功能:控制台 → Images → Transformations → 为区域启用
  2. 确认区域已通过Cloudflare代理(橙色云朵图标)
  3. 检查源图片是否可公开访问
  4. 等待5-10分钟让设置生效

Problem: Direct upload returns CORS error

问题:直接上传返回CORS错误

Symptoms:
Access-Control-Allow-Origin
error in browser console
Solutions:
  1. Use
    multipart/form-data
    encoding (let browser set Content-Type)
  2. Don't call
    /direct_upload
    from browser; call from backend
  3. Name file field
    file
    (not
    image
    )
  4. Remove manual Content-Type header
症状:浏览器控制台出现
Access-Control-Allow-Origin
错误
解决方案:
  1. 使用
    multipart/form-data
    编码(由浏览器自动设置Content-Type)
  2. 不要从浏览器调用
    /direct_upload
    ;仅从后端调用
  3. 文件字段命名为
    file
    (不能是
    image
  4. 移除手动设置的Content-Type请求头

Problem: Worker transformations return 9403 loop error

问题:Worker转换返回9403循环错误

Symptoms:
Cf-Resized: err=9403
in response headers
Solutions:
  1. Don't fetch Worker's own URL (use external origin)
  2. Don't transform already-resized images
  3. Check URL routing logic to avoid loops
症状:响应头中出现
Cf-Resized: err=9403
解决方案:
  1. 不要请求Worker自身的URL(使用外部源)
  2. 不要转换已被转换过的图片
  3. 检查URL路由逻辑避免循环

Problem: Signed URLs not working

问题:签名URL无法使用

Symptoms: 403 Forbidden when accessing signed URL
Solutions:
  1. Verify image uploaded with
    requireSignedURLs=true
  2. Check signature generation (HMAC-SHA256)
  3. Ensure expiry timestamp is in future
  4. Verify signing key matches dashboard (Images → Keys)
  5. Cannot use flexible variants with signed URLs (use named variants)
症状:访问签名URL时返回403 Forbidden
解决方案:
  1. 确认图片上传时设置了
    requireSignedURLs=true
  2. 检查签名生成逻辑(HMAC-SHA256)
  3. 确保过期时间戳在未来
  4. 验证签名密钥与控制台中的一致(Images → Keys)
  5. 不能将灵活变体与签名URL配合使用(请使用命名变体)

Problem: Images uploaded but not appearing

问题:图片已上传但未在控制台显示

Symptoms: Upload returns 200 OK but image not in dashboard
Solutions:
  1. Check for
    draft: true
    in response (Direct Creator Upload)
  2. Wait for upload to complete (check via GET
    /images/v1/{id}
    )
  3. Verify account ID matches
  4. Check for upload errors in webhooks
症状:上传返回200 OK,但控制台中看不到图片
解决方案:
  1. 检查响应中是否包含
    draft: true
    (创作者直接上传)
  2. 等待上传完成(可通过GET
    /images/v1/{id}
    检查)
  3. 确认账户ID匹配
  4. 检查Webhooks中的上传错误

Problem: Re-uploaded image still shows old version

问题:重新上传图片后仍显示旧版本

Symptoms: Even users who never visited page see old image after re-upload
Solutions:
  1. Use unique image IDs for each upload:
    const imageId = \
    ${baseId}-${Date.now()}`;`
  2. Set shorter Cache-Control headers (e.g., max-age=86400 for 1 day instead of 30 days)
  3. Manual purge via dashboard after re-upload with same ID
  4. Understand default Browser Cache TTL is 2 days - users may see cached version
Root Cause: Cloudflare respects origin Cache-Control headers. If origin sets long max-age (e.g., 30 days), images remain cached even after re-upload with same ID.
症状:即使是从未访问过页面的用户,重新上传后仍看到旧图片
解决方案:
  1. 每次上传使用唯一的图片ID:
    const imageId = 
    ${baseId}-${Date.now()}
    ;
  2. 设置更短的Cache-Control请求头(例如max-age=86400,即1天,而非30天)
  3. 使用相同ID重新上传后手动清除控制台中的缓存
  4. 了解默认浏览器缓存TTL为2天 - 用户可能会看到缓存的旧版本
根本原因:Cloudflare遵守源服务器的Cache-Control请求头。如果源服务器设置了较长的max-age(例如30天),即使重新上传相同ID的图片,旧版本仍会被缓存。

Problem: PNG images not caching

问题:PNG图片未被缓存

Symptoms: PNG images bypass cache while other formats (JPEG, WebP) cache correctly
Solutions:
  1. Create explicit cache rule: Dashboard → Caching → Cache Rules
  2. Add rule:
    URI Path
    ends with
    .png
    → Cache Everything
  3. Default cached extensions don't include
    .png
    (only
    .jpg
    ,
    .jpeg
    ,
    .gif
    ,
    .webp
    ,
    .bmp
    ,
    .ico
    ,
    .svg
    ,
    .tif
    ,
    .tiff
    )

症状:PNG图片绕过缓存,而其他格式(JPEG、WebP)缓存正常
解决方案:
  1. 创建显式缓存规则:控制台 → 缓存 → 缓存规则
  2. 添加规则:
    URI路径
    结尾为
    .png
    → 缓存所有内容
  3. 默认缓存扩展名不包含
    .png
    (仅包含
    .jpg
    ,
    .jpeg
    ,
    .gif
    ,
    .webp
    ,
    .bmp
    ,
    .ico
    ,
    .svg
    ,
    .tif
    ,
    .tiff

Official Documentation

官方文档

Package Versions

包版本

Last Verified: 2026-01-21 API Version: v2 (direct uploads), v1 (standard uploads) Optional: @cloudflare/workers-types@4.20260108.0

Skill Version: 2.1.0 | Changes: Added 3 new issues (AVIF limits, cache by device, PNG caching), enhanced metadata issue with format details, added Content Credentials, Browser Cache TTL, Product Merge notes, R2 integration pattern, Mirage deprecation notice, and cache troubleshooting guidance. Error count: 13 → 16.
最后验证时间:2026-01-21 API版本: v2(直接上传), v1(标准上传) 可选依赖: @cloudflare/workers-types@4.20260108.0

技能版本: 2.1.0 | 更新内容: 新增3个问题(AVIF限制、按设备缓存、PNG缓存),增强元数据问题的格式细节说明,添加内容凭证、浏览器缓存TTL、产品合并说明、R2集成模式、Mirage弃用通知及缓存故障排查指南。错误数量:13 → 16。