mcpField
C
cloudflare-images

cloudflare-images

github.com/jackspaceクラウド・DevOps1639 回インストール12 週間前に更新
リポジトリ

cloudflare-images について

cloudflare-imagesは、画像の保存、配信、変換を行う開発者向けのCloudflare Imagesガイドです。Images API、変換、バリアントに加え、公開画像と非公開画像の扱いを説明します。

主な機能

  1. 01ファイルのアップロードと外部画像の取り込み
  2. 02ユーザー向け直接アップロードURLの発行
  3. 03画像のサイズ、形式、効果の変換
  4. 04名前付き・柔軟なバリアントの管理
  5. 05署名付きURLによる非公開画像の配信

活用シーン

  1. 01他サービスの画像をCloudflare Imagesへ移行
  2. 02APIキーを公開せずユーザーに画像をアップロードしてもらう
  3. 03端末サイズに応じたレスポンシブ画像の配信
  4. 04有効期限付きリンクで非公開画像を共有

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jackspace/claudeskillz --skill cloudflare-images

インストール後、Claude Code で/cloudflare-imagesを入力して読み込まれたか確認します。

同梱ファイルREADME.mdSKILL.json

詳細

Cloudflare Images

ステータス:本番環境で利用可能 ✅ 最終更新日:2025-10-26 依存関係:Images が有効になっている Cloudflare アカウント 最新バージョン:Cloudflare Images API v2


概要

Cloudflare Images には、次の 2 つの強力な機能があります。

  1. Images API:画像をアップロード、保存し、自動最適化とバリアントを利用して配信します
  2. 画像変換:公開アクセス可能な画像を任意にリサイズ、最適化、変換します

主なメリット:

  • グローバル CDN による配信
  • WebP/AVIF への自動変換
  • 用途別のバリアント(最大 100 個)
  • アップロードの直接作成(ユーザーは API キーなしでアップロードできます)
  • 非公開画像用の署名付き URL
  • URL または Workers を介した任意の画像の変換

クイックスタート(5 分)

1. Cloudflare Images を有効にする

Cloudflare ダッシュボードにログイン → Images → アカウントで有効にします。

アカウント ID を取得し、Cloudflare Images: Edit 権限を持つ API トークンを作成します。

これが重要な理由:

  • すべての API 操作にアカウント ID と API トークンが必要です
  • Images の Free プランには、変換回数の上限があります
2. 最初の画像をアップロードする
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'

レスポンスには次の情報が含まれます:

  • id:画像の配信用 ID
  • variants:配信用 URL の配列

重要:

  • multipart/form-data エンコーディングを使用してください(application/json は使用しないでください)
  • 画像 ID は自動生成されます(またはカスタム ID を使用できます)
3. 画像を配信する
<img src="https://imagedelivery.net/<ACCOUNT_HASH>/<IMAGE_ID>/public" />

デフォルトの public バリアントで画像を配信できます。独自のバリアント名に置き換えてください。

4. 画像変換を有効にする

ダッシュボード → Images → Transformations → 対象のゾーンを選択 → Enable for zone

これで、任意の画像を変換できます。

<img src="/cdn-cgi/image/width=800,quality=85/uploads/photo.jpg" />

これが重要な理由:

  • Cloudflare Images の外部に保存された画像にも対応します
  • Cloudflare のグローバルネットワーク上で自動的にキャッシュされます
  • 追加のストレージ費用はかかりません
5. Workers で変換する(上級者向け)
export default {
  async fetch(request: Request): Promise<Response> {
    const imageURL = "https://example.com/image.jpg";

    return fetch(imageURL, {
      cf: {
        image: {
          width: 800,
          quality: 85,
          format: "auto" // WebP/AVIF for supporting browsers
        }
      }
    });
  }
};

3 つの機能

機能 1:Images API(アップロードとストレージ)

Cloudflare のネットワークに画像を保存し、世界中に配信します。

アップロード方法:

  1. ファイルのアップロード - サーバーからファイルを直接アップロードします
  2. URL によるアップロード - 外部 URL から画像を取り込みます
  3. アップロードの直接作成 - ユーザーのアップロード用に一度限りのアップロード URL を生成します

配信方法:

  • デフォルトのドメイン:imagedelivery.net
  • カスタムドメイン:/cdn-cgi/imagedelivery/...
  • 署名付き URL:有効期限トークン付きの非公開画像

参照:templates/upload-api-basic.ts、templates/direct-creator-upload-backend.ts

機能 2:画像変換

画像(Images に保存されたもの、外部のものを問わず)を最適化し、リサイズします。

2 つの方法:

  1. URL による変換 - 特別な URL 形式を使用します
  2. Workers による変換 - fetch を介してプログラムから制御します

一般的な変換:

  • リサイズ:width=800,height=600,fit=cover
  • 最適化:quality=85,format=auto
  • 効果:blur=10,sharpen=3
  • 切り抜き:gravity=face,zoom=0.5

参照:templates/transform-via-url.ts、templates/transform-via-workers.ts

機能 3:バリアント

用途に応じて使い分けられる、あらかじめ定義された画像サイズです。

名前付きバリアント(最大 100 個):

  • 一度作成すれば、どこでも使用できます
  • 例:thumbnail、avatar、hero
  • 変換内容を統一できます

フレキシブルバリアント(動的):

  • アカウントごとに有効化します
  • URL 内で変換パラメーターを使用します
  • 例:w=400,sharpen=3
  • 署名付き URL とは併用できません

参照:templates/variants-management.ts、references/variants-guide.md


Images API - アップロード方法

方法 1:ファイルのアップロード(基本)
curl --request POST \
  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' \
  --form 'requireSignedURLs=false' \
  --form 'metadata={"key":"value"}'

主なオプション:

  • file:画像ファイル(必須)
  • id:カスタム ID(任意、デフォルトでは自動生成)
  • requireSignedURLs:非公開画像の場合は true(デフォルト:false)
  • metadata:JSON オブジェクト(最大 1024 バイト、エンドユーザーには表示されません)

レスポンス:

{
  "result": {
    "id": "2cdc28f0-017a-49c4-9ed7-87056c83901",
    "filename": "image.jpg",
    "uploaded": "2022-01-31T16:39:28.458Z",
    "requireSignedURLs": false,
    "variants": [
      "https://imagedelivery.net/Vi7wi5KSItxGFsWRG2Us6Q/2cdc28f0.../public"
    ]
  }
}

参照:templates/upload-api-basic.ts

方法 2:URL 経由でアップロード

事前にダウンロードせず、外部ソースから画像を取り込みます。

curl --request POST \
  https://api.cloudflare.com/client/v4/accounts/{account_id}/images/v1 \
  --header "Authorization: Bearer <API_TOKEN>" \
  --form 'url=https://example.com/image.jpg' \
  --form 'metadata={"source":"external"}'

使用する場面:

  • 別のサービスから画像を移行する場合
  • ユーザーから提供された URL を取り込む場合
  • 外部ソースの画像をバックアップする場合

重要:

  • URL は公開アクセス可能であるか、認証が必要です
  • HTTP ベーシック認証に対応しています:https://user:[email protected]/image.jpg
  • 同じリクエストで file と url を併用できません

参照:templates/upload-via-url.ts

方法 3:直接クリエーターアップロード ⭐

ユーザーが Cloudflare に直接アップロードするための使い捨てアップロード URL を生成します(API キーは公開されません)。

バックエンドのエンドポイント(アップロード URL を生成):

const response = await fetch(
  `https://api.cloudflare.com/client/v4/accounts/${accountId}/images/v2/direct_upload`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      requireSignedURLs: true,
      metadata: { userId: '12345' },
      expiry: '2025-10-26T18:00:00Z' // Optional: default 30min, max 6hr
    })
  }
);

const { uploadURL, id } = await response.json();
// Return uploadURL to frontend

フロントエンドのアップロード(HTML + JavaScript):

<form id="upload-form">
  <input type="file" id="file-input" accept="image/*" />
  <button type="submit">Upload</button>
</form>

<script>
document.getElementById('upload-form').addEventListener('submit', async (e) => {
  e.preventDefault();

  const fileInput = document.getElementById('file-input');
  const formData = new FormData();
  formData.append('file', fileInput.files[0]); // MUST be named 'file'

  const uploadURL = 'UPLOAD_URL_FROM_BACKEND'; // Get from backend

  const response = await fetch(uploadURL, {
    method: 'POST',
    body: formData // NO Content-Type header, browser sets multipart/form-data
  });

  if (response.ok) {
    console.log('Upload successful!');
  }
});
</script>

重要な理由:

  • ブラウザーに API キーを公開しません
  • ユーザーは Cloudflare に直接アップロードします(より高速で、中継サーバーも不要です)
  • 使い捨て URL は、使用後またはタイムアウト後に期限切れになります
  • アップロードの成功/失敗を通知する Webhook を利用できます

重要な CORS の修正方法:

  • ✅ 必須:multipart/form-data エンコーディングを使用します(ヘッダーはブラウザーに設定させます)
  • ✅ 必須:フィールド名には file を指定します(image など、ほかの名前は使用しないでください)
  • ✅ 必須:バックエンドからのみ /direct_upload API を呼び出します
  • ❌ 禁止:Content-Type: application/json または image/jpeg を設定すること
  • ❌ 禁止:ブラウザーから /direct_upload を呼び出すこと(CORS エラーが発生します)

参照:templates/direct-creator-upload-backend.ts、templates/direct-creator-upload-frontend.html、references/direct-upload-complete-workflow.md


画像変換

URL 変換

特別な URL 形式を使って画像を変換します。

URL パターン:

https://<ZONE>/cdn-cgi/image/<OPTIONS>/<SOURCE-IMAGE>

例:

<img src="/cdn-cgi/image/width=800,quality=85,format=auto/uploads/photo.jpg" />

よく使うオプション:

  • サイズ:width=800、height=600、fit=cover
  • 品質:quality=85(1~100)
  • 形式:format=auto(WebP/AVIF を自動検出)、format=webp、format=jpeg
  • トリミング:gravity=auto(スマートトリミング)、gravity=face、trim=10
  • 効果:blur=10、sharpen=3、brightness=1.2、contrast=1.1
  • 回転:rotate=90、flip=h(水平反転)、flip=v(垂直反転)

フィットオプション:

  • scale-down:サイズに収まるよう縮小します(拡大はしません)
  • contain:縦横比を維持して、指定サイズ内に収まるよう変更します
  • cover:指定サイズを満たすよう変更します(トリミングされる場合があります)
  • crop:指定サイズに合わせてトリミングします
  • pad:サイズを変更して余白を追加します(background オプションと併用します)

形式の自動検出:

<img src="/cdn-cgi/image/format=auto/image.jpg" />

Cloudflare は次の形式で配信します:

  • AVIF 対応ブラウザー(Chrome、Edge)には AVIF
  • AVIF 非対応ブラウザー(Safari、Firefox)には WebP
  • フォールバックとして元の形式(JPEG)

参照:templates/transform-via-url.ts、references/transformation-options.md

Workers による変換

独自の URL スキームを使ったプログラムによる画像変換です。

基本パターン:

export default {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);

    // Custom URL scheme: /images/thumbnail/photo.jpg
    if (url.pathname.startsWith('/images/thumbnail/')) {
      const imagePath = url.pathname.replace('/images/thumbnail/', '');
      const imageURL = `https://storage.example.com/${imagePath}`;

      return fetch(imageURL, {
        cf: {
          image: {
            width: 300,
            height: 300,
            fit: 'cover',
            quality: 85
          }
        }
      });
    }

    return new Response('Not found', { status: 404 });
  }
};

応用:コンテンツネゴシエーション:

const accept = request.headers.get('accept') || '';

let format: 'avif' | 'webp' | 'auto' = 'auto';
if (/image\/avif/.test(accept)) {
  format = 'avif';
} else if (/image\/webp/.test(accept)) {
  format = 'webp';
}

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

Workers による変換の利点:

  • 独自の URL スキーム:画像の保存場所を隠せます
  • プリセット名:ピクセル値の代わりに thumbnail、avatar、large を使えます
  • コンテンツネゴシエーション:ブラウザーに応じて最適な形式を配信できます
  • アクセス制御:配信前に認証を確認できます
  • 動的なサイズ設定:デバイスタイプに基づいてサイズを計算できます

参照:templates/transform-via-workers.ts、references/transformation-options.md


バリアント管理

名前付きバリアント(最大 100 個)

用途ごとに、事前定義した変換を作成します。

API 経由で作成:

curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/images/v1/variants" \
  --header "Authorization: Bearer <API_TOKEN>" \
  --header "Content-Type: application/json" \
  --data '{
    "id": "avatar",
    "options": {
      "fit": "cover",
      "width": 200,
      "height": 200,
      "metadata": "none"
    },
    "neverRequireSignedURLs": false
  }'

URL で使用:

<img src="https://imagedelivery.net/<ACCOUNT_HASH>/<IMAGE_ID>/avatar" />

使用する場面:

  • アプリ全体で画像サイズを統一する場合
  • プライベート画像(署名付き URL で使用できます)
  • シンプルで予測しやすい URL が必要な場合

参照:templates/variants-management.ts

柔軟なバリアント

URL 内のパラメーターを使用して、動的に変換します。

有効化(アカウントごとに初回のみ):

curl --request PATCH \
  https://api.cloudflare.com/client/v4/accounts/{account_id}/images/v1/config \
  --header "Authorization: Bearer <API_TOKEN>" \
  --header "Content-Type: application/json" \
  --data '{"flexible_variants": true}'

URL で使用:

<img src="https://imagedelivery.net/<ACCOUNT_HASH>/<IMAGE_ID>/w=400,sharpen=3" />

使用する場面:

  • サイズを動的に設定する必要がある場合
  • パブリック画像のみ(署名付き URL では使用できません)
  • 迅速なプロトタイピング

重要:

  • ❌ requireSignedURLs=true とは併用できません
  • ✅ プライベート画像には名前付きバリアントを使用してください

参照:references/variants-guide.md


署名付き URL(プライベート画像)

HMAC-SHA256 トークンを使用して、プライベート画像用の有効期限付き URL を生成します。

URL 形式:

https://imagedelivery.net/<ACCOUNT_HASH>/<IMAGE_ID>/<VARIANT>?exp=<EXPIRY>&sig=<SIGNATURE>

署名の生成(Workers の例):

async function generateSignedURL(
  imageId: string,
  variant: string,
  expirySeconds: number = 3600
): Promise<string> {
  const accountHash = 'YOUR_ACCOUNT_HASH';
  const signingKey = 'YOUR_SIGNING_KEY'; // Dashboard → Images → Keys

  const expiry = Math.floor(Date.now() / 1000) + expirySeconds;
  const stringToSign = `${imageId}${variant}${expiry}`;

  const encoder = new TextEncoder();
  const keyData = encoder.encode(signingKey);
  const messageData = encoder.encode(stringToSign);

  const cryptoKey = await crypto.subtle.importKey(
    'raw',
    keyData,
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['sign']
  );

  const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageData);
  const sig = Array.from(new Uint8Array(signature))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('');

  return `https://imagedelivery.net/${accountHash}/${imageId}/${variant}?exp=${expiry}&sig=${sig}`;
}

使用方法:

const signedURL = await generateSignedURL('image-id', 'public', 3600);
// Returns URL valid for 1 hour

使用する場面:

  • ユーザープロフィール写真(共有されるまでは非公開)
  • 有料コンテンツ(有効期限付きアクセス)
  • 一時的なダウンロード
  • 安全な画像配信

参照先:templates/signed-urls-generation.ts、references/signed-urls-guide.md


レスポンシブ画像

画面サイズに応じて最適なサイズの画像を配信します。

名前付きバリアントを使用する場合:

<img
  srcset="
    https://imagedelivery.net/<HASH>/<ID>/mobile 480w,
    https://imagedelivery.net/<HASH>/<ID>/tablet 768w,
    https://imagedelivery.net/<HASH>/<ID>/desktop 1920w
  "
  sizes="(max-width: 480px) 480px, (max-width: 768px) 768px, 1920px"
  src="https://imagedelivery.net/<HASH>/<ID>/desktop"
  alt="Responsive image"
/>

柔軟なバリアントを使用する場合:

<img
  srcset="
    https://imagedelivery.net/<HASH>/<ID>/w=480,f=auto 480w,
    https://imagedelivery.net/<HASH>/<ID>/w=768,f=auto 768w,
    https://imagedelivery.net/<HASH>/<ID>/w=1920,f=auto 1920w
  "
  sizes="(max-width: 480px) 480px, (max-width: 768px) 768px, 1920px"
  src="https://imagedelivery.net/<HASH>/<ID>/w=1920,f=auto"
  alt="Responsive image"
/>

アートディレクション(モバイルとデスクトップで異なるトリミングを使用):

<picture>
  <source
    media="(max-width: 767px)"
    srcset="https://imagedelivery.net/<HASH>/<ID>/mobile-square"
  />
  <source
    media="(min-width: 768px)"
    srcset="https://imagedelivery.net/<HASH>/<ID>/desktop-wide"
  />
  <img src="https://imagedelivery.net/<HASH>/<ID>/desktop-wide" alt="Hero image" />
</picture>

参照先:templates/responsive-images-srcset.html、references/responsive-images-patterns.md


重要なルール

必ず行うこと

✅ Direct Creator Upload には multipart/form-data を使用します ✅ ファイルフィールドには file という名前を付けます(image などの名前は使用しません) ✅ /direct_upload API はバックエンドからのみ呼び出します(ブラウザーからは呼び出しません) ✅ 変換には HTTPS URL を使用します(HTTP はサポートされていません) ✅ 画像パス内の特殊文字は URL エンコードします ✅ /cdn-cgi/image/ を使用する前に、ゾーンで変換を有効にします ✅ プライベート画像には名前付きバリアント(署名付き URL)を使用します ✅ 変換エラーを確認するには Cf-Resized ヘッダーを確認します ✅ WebP/AVIF への自動変換には format=auto を設定します ✅ 意図しない拡大を防ぐには fit=scale-down を使用します

絶対に行わないこと

❌ ファイルのアップロードに application/json の Content-Type を使用しません ❌ ブラウザーから /direct_upload を呼び出しません(CORS エラーが発生します) ❌ requireSignedURLs=true で柔軟なバリアントを使用しません ❌ SVG ファイルのサイズを変更しません(元から拡大縮小可能です) ❌ 変換に HTTP URL を使用しません(HTTPS のみ) ❌ URL にスペースやエスケープされていない Unicode を含めません ❌ Workers 内で同じ画像を複数回変換しません(9403 ループが発生します) ❌ 画像サイズを 100 メガピクセル以上にしません ❌ Workers で /cdn-cgi/image/ エンドポイントを使用しません(代わりに cf.image を使用します) ❌ 使用前にゾーンで変換を有効にするのを忘れません


既知の問題の予防

このスキルは、文書化されている 13 件以上の問題を防止します。

問題 #1:Direct Creator Upload の CORS エラー

エラー:Access to XMLHttpRequest blocked by CORS policy: Request header field content-type is not allowed

出典:Cloudflare Community #345739、#368114

原因:サーバーの CORS 設定では、Content-Type ヘッダーに multipart/form-data のみが許可されています

予防策:

// ✅ 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 })
});
問題 #2:エラー 5408 - アップロードのタイムアウト

エラー:アップロード開始から約 15 秒後に Error 5408

出典:Cloudflare Community #571336

原因:Cloudflare のリクエストタイムアウトは 30 秒です。アップロードが遅い場合やファイルが大きい場合は、上限を超えます

予防策:

  • アップロード前に画像を圧縮します(Canvas API を使用したクライアント側での圧縮)
  • 適切なファイルサイズの上限を設定します(例:最大 10MB)
  • ユーザーにアップロードの進行状況を表示します
  • タイムアウトエラーを適切に処理します
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;
}
問題 #3:エラー 400 - 無効なファイルパラメーター

エラー:400 Bad Request と、役に立たないエラーメッセージ

出典:Cloudflare Community #487629

原因:ファイルフィールドの名前は file である必要があります(image、photo などではありません)

予防策:

// ✅ CORRECT
formData.append('file', imageFile);

// ❌ WRONG
formData.append('image', imageFile); // 400 error
formData.append('photo', imageFile); // 400 error
問題 #4:CORS プリフライトの失敗

エラー:プリフライト OPTIONS リクエストがブロックされます

出典:Cloudflare Community #306805

原因:ブラウザーから /direct_upload API を直接呼び出しています(バックエンドからのみ呼び出す必要があります)

予防策:

ARCHITECTURE:
Browser → Backend API → POST /direct_upload → Returns uploadURL → Browser uploads to uploadURL

API トークンをブラウザーに公開しないでください。バックエンドでアップロード URL を生成し、フロントエンドに返します。

Issue #5:エラー 9401 - 引数が無効です

エラー:Cf-Resized: err=9401 - 必須の cf.image オプションがないか、無効です

出典:Cloudflare Images Docs - トラブルシューティング

発生する理由:必須の変換パラメーターがないか、値が無効です

防止策:

// ✅ 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
    }
  }
});
Issue #6:エラー 9402 - 画像が大きすぎます

エラー:Cf-Resized: err=9402 - 画像が大きすぎるか、接続が中断されました

出典:Cloudflare Images Docs - トラブルシューティング

発生する理由:画像が最大面積(100 メガピクセル)を超えているか、ダウンロードに失敗しました

防止策:

  • 変換前に画像の寸法を検証します
  • 適切なサイズのソース画像を使用します(最大 10000x10000px)
  • ネットワークエラーを適切に処理します
Issue #7:エラー 9403 - リクエストループ

エラー:Cf-Resized: err=9403 - Worker が自身の URL またはすでにサイズ変更済みの画像を取得しています

出典:Cloudflare Images Docs - トラブルシューティング

発生する理由:変換済みの画像に変換を適用しているか、Worker が自身を取得しています

防止策:

// ✅ 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 } } });
}
Issue #8:エラー 9406/9419 - URL 形式が無効です

エラー:Cf-Resized: err=9406 または err=9419 - URL が HTTPS ではないか、URL にスペースまたはエスケープされていない Unicode が含まれています

出典:Cloudflare Images Docs - トラブルシューティング

発生する理由:画像 URL が HTTP(HTTPS ではない)を使用しているか、無効な文字が含まれています

防止策:

// ✅ 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

URL パスには必ず encodeURIComponent() を使用してください:

const filename = "photo name.jpg";
const imageURL = `https://example.com/images/${encodeURIComponent(filename)}`;
Issue #9:エラー 9412 - 画像以外のレスポンス

エラー:Cf-Resized: err=9412 - オリジンが画像ではなく HTML を返しました

出典:Cloudflare Images Docs - トラブルシューティング

発生する理由:オリジンサーバーが画像ではなく、404 ページまたはエラーページ(HTML)を返しています

防止策:

// 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 } } });
Issue #10:エラー 9413 - 画像の最大面積を超えています

エラー:Cf-Resized: err=9413 - 画像が 100 メガピクセルを超えています

出典:Cloudflare Images Docs - トラブルシューティング

発生する理由:ソース画像の寸法が 100 メガピクセルを超えています(例:10000x10000px)

防止策:

  • アップロード前に画像の寸法を検証します
  • 大きすぎる画像を事前に処理します
  • しきい値を超える画像を拒否します
const MAX_MEGAPIXELS = 100;

if (width * height > MAX_MEGAPIXELS * 1_000_000) {
  return new Response('Image too large', { status: 413 });
}
Issue #11:フレキシブルバリアントと署名付き URL の非互換性

エラー:フレキシブルバリアントは非公開画像では機能しません

出典:Cloudflare Images Docs - フレキシブルバリアントを有効にする

発生する理由:フレキシブルバリアントは requireSignedURLs=true と併用できません

防止策:

// ✅ 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
Issue #12:SVG のサイズ変更に関する制限

エラー:SVG ファイルは変換でサイズ変更できません

出典:Cloudflare Images Docs - SVG ファイル

発生する理由:SVG は本質的に拡大縮小可能なベクター形式であり、サイズ変更は適用できません

防止策:

// 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
Issue #13:EXIF メタデータはデフォルトで削除されます

エラー:アップロードした JPEG から GPS データやカメラ設定が削除されます

出典:Cloudflare Images Docs - URL による変換

発生する理由:デフォルトの動作では、著作権情報を除くすべてのメタデータが削除されます

防止策:

// Preserve metadata
fetch(imageURL, {
  cf: {
    image: {
      width: 800,
      metadata: 'keep' // Options: 'none', 'copyright', 'keep'
    }
  }
});

オプション:

  • none:すべてのメタデータを削除します
  • copyright:著作権タグのみを保持します(JPEG のデフォルト)
  • keep:GPS を含むほとんどの EXIF メタデータを保持します

バンドルされたリソースの使用

テンプレート(templates/)

よく使うパターンの、コピーしてすぐ使えるコードです。

  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 - 大量アップロード向けの Batch API です

使用方法:

cp templates/upload-api-basic.ts src/upload.ts
# Edit with your account ID and API token
リファレンス(references/)

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/)

check-versions.sh - API エンドポイントが最新かどうかを確認します


高度なトピック

カスタムドメイン

imagedelivery.net の代わりに、独自のドメインから画像を配信します。

URL 形式:

https://example.com/cdn-cgi/imagedelivery/<ACCOUNT_HASH>/<IMAGE_ID>/<VARIANT>

要件:

  • ドメインが Cloudflare 上にあること(Images と同じアカウント)
  • Cloudflare 経由でプロキシされていること(オレンジ色の雲)

カスタムパス(Transform Rules):

/images/... を /cdn-cgi/imagedelivery/... に書き換えます:

  1. ダッシュボード → Rules → Transform Rules → Rewrite URL
  2. 一致条件:starts_with(http.request.uri.path, "/images/")
  3. 書き換え先:/cdn-cgi/imagedelivery/<ACCOUNT_HASH>${substring(http.request.uri.path, 7)}

これで /images/{id}/{variant} は /cdn-cgi/imagedelivery/{hash}/{id}/{variant} に変わります。

参照:カスタムドメインから画像を配信する

Batch API

バッチトークンを使った大量アップロードです。

ホスト:api.cloudflare.com の代わりに batch.imagedelivery.net

使用方法:

# Create batch token in dashboard: Images → Batch API

curl "https://batch.imagedelivery.net/images/v1" \
  --header "Authorization: Bearer <BATCH_TOKEN>" \
  --form 'file=@./image.jpg'

使用する場面:

  • 数千枚の画像を移行するとき
  • 一括アップロードのワークフロー
  • 画像の取り込みを自動化するとき

参照:templates/batch-upload.ts

Webhook

アップロードの成功または失敗に関する通知を受け取ります(Direct Creator Upload のみ)。

設定方法:

  1. ダッシュボード → Notifications → Destinations → Webhooks → Create
  2. Webhook URL を入力してテストします
  3. Notifications → All Notifications → Add → Images → Select webhook

ペイロード(例):

{
  "imageId": "2cdc28f0-017a-49c4-9ed7-87056c83901",
  "status": "uploaded",
  "metadata": {"userId": "12345"}
}

使用する場面:

  • アップロード後にデータベースを更新するとき
  • 画像処理パイプラインを起動するとき
  • アップロード状況をユーザーに通知するとき

参照:Webhook を設定する


トラブルシューティング

問題:画像が変換されない

症状:/cdn-cgi/image/... が元の画像を返す、または 404 を返します

解決方法:

  1. ゾーンで変換を有効にします:ダッシュボード → Images → Transformations → Enable for zone
  2. ゾーンが Cloudflare 経由でプロキシされていることを確認します(オレンジ色の雲)
  3. 元画像が公開アクセス可能であることを確認します
  4. 設定が反映されるまで 5~10 分待ちます
問題:Direct Upload で CORS エラーが発生する

症状:ブラウザーのコンソールに Access-Control-Allow-Origin エラーが表示されます

解決方法:

  1. multipart/form-data 形式を使用します(Content-Type はブラウザーに設定させてください)
  2. ブラウザーから /direct_upload を呼び出さず、バックエンドから呼び出します
  3. ファイルフィールドの名前を file にします(image ではありません)
  4. 手動で設定した Content-Type ヘッダーを削除します
問題:Worker の変換で 9403 ループエラーが発生する

症状:レスポンスヘッダーに Cf-Resized: err=9403 が含まれる

解決策:

  1. Worker 自身の URL を取得しないようにします(外部オリジンを使用します)
  2. すでにサイズ変更された画像を変換しないようにします
  3. ループを避けるため、URL のルーティングロジックを確認します
問題:署名付き URL が機能しない

症状:署名付き URL にアクセスすると 403 Forbidden が返される

解決策:

  1. 画像が requireSignedURLs=true でアップロードされたことを確認します
  2. 署名の生成方法(HMAC-SHA256)を確認します
  3. 有効期限のタイムスタンプが未来になっていることを確認します
  4. 署名キーがダッシュボード(Images → Keys)と一致することを確認します
  5. 署名付き URL ではフレキシブルバリアントを使用できません(名前付きバリアントを使用します)
問題:画像はアップロードされたが表示されない

症状:アップロードは 200 OK を返すが、画像がダッシュボードに表示されない

解決策:

  1. レスポンスに draft: true が含まれていないか確認します(Direct Creator Upload)
  2. アップロードが完了するまで待ちます(GET /images/v1/{id} で確認します)
  3. アカウント ID が一致することを確認します
  4. Webhook でアップロードエラーを確認します

セットアップの完全なチェックリスト

  • Images が有効になっている Cloudflare アカウント
  • アカウント ID と API トークンを取得済み(Images: Edit 権限)
  • (任意)ゾーンで画像変換が有効
  • (任意)一般的なユースケース向けのバリアントを作成済み
  • (任意)動的なサイズ指定が必要な場合は、フレキシブルバリアントを有効化済み
  • (任意)プライベート画像用の署名キーを取得済み
  • (任意)アップロード通知用に Webhook を設定済み
  • (任意)Transform Rules でカスタムドメインを設定済み
  • アップロード方法を実装済み(ファイル、URL、または Direct Creator)
  • 配信 URL をテスト済み(imagedelivery.net またはカスタムドメイン)
  • 変換をテスト済み(URL または Workers)
  • エラー処理を実装済み(CORS、タイムアウト、サイズ制限)

公式ドキュメント


パッケージのバージョン(2025-10-26 確認済み)

API バージョン:v2(直接アップロード用)、v1(標準アップロード用)

npm パッケージは不要 - Cloudflare のネイティブ API を使用します

任意:

  • @cloudflare/workers-types@latest - Workers 用の TypeScript 型

質問や問題がありますか?

  1. よくある問題については references/top-errors.md を確認します
  2. セットアップ手順をすべて確認します
  3. 公式ドキュメントを確認します:https://developers.cloudflare.com/images/
  4. ゾーンで変換が有効になっていることを確認します
  5. Direct Creator Upload の CORS 設定を確認します

コメント