Cloudflare Image Resizing 運用

公開画像のリサイズ / フォーマット変換 / 品質調整を Cloudflare Workers の cf.image で行う仕組み。

全体構成

client (web home/portal)
  └─ <img src="/cdn/img?url=...&w=400&q=80&f=webp">
        │
        └─ Cloudflare Workers (parky-api)
              cdn-image.ts:cdnImageRoutes
              ├─ allowlist 検証 (SSRF 対策)
              ├─ fetch(origin, { cf: { image: {...} } })
              ├─ Workers Cache API (1d edge cache + 30d swr)
              └─ 失敗時は origin URL へ 302 fallback
  • Cloudflare Pro / Business / Enterprise plancf.image が有効化される。Free plan では fallback により原寸返却で動く。
  • 画像本体の保管は R2 / Supabase Storage(既存)。Image Resizing は変換層のみ。
  • Cloudflare Images Platform(uploads + variants)は採用しない。Image Resizing + R2 で十分カバーできるため。

API

parky/api/src/bff/web/cdn-image.ts

Param 範囲 既定 役割
url 必須、絶対 URL、allowlist 内 元画像
w 32-4096
h 32-4096 高さ
q 1-100 80 品質
f webp / avif / auto / json auto 出力フォーマット
fit scale-down / cover / contain / crop scale-down リサイズ動作

Allowlist

buildAllowedHostSuffixes() で動的に組み立てる。

  • R2_PUBLIC_BASE の host (例: cdn.parky.co.jp)
  • SUPABASE_URL の host
  • 固定: .r2.dev, .supabase.co, cdn.parky.co.jp, assets.parky.co.jp

外部画像 (例: 旧 WP の任意画像) は変換できない。直リンクで使う。

クライアント helper

Vite portals (admin / owner / marketing)

web/packages/ui/src/cdn-image.ts@parky/ui から re-export。

import { cdnImageUrl, cdnImageSrcSet, CDN_IMAGE_PRESETS } from '@parky/ui';

<img
  src={cdnImageUrl(spot.cover_url, CDN_IMAGE_PRESETS.card)}
  srcSet={cdnImageSrcSet(spot.cover_url, [400, 800, 1200])}
  sizes="(max-width: 768px) 100vw, 50vw"
  alt={spot.name}
/>
Preset width format 用途
thumb 200x200 cover webp q75 アバター・小カード
card 400 webp q80 リスト・記事一覧
hero 1200 webp q80 LP・detail トップ
og 1200x630 cover webp q85 OGP / SNS 共有
detail 1600 webp q85 拡大ビュー

Astro home

web/home/src/lib/image-utils.ts — Supabase Storage Transform と CDN proxy の両方を扱う独自 helper。home の Astro pages はこちらを使う(記事画像が Supabase Storage 由来のため)。

<DynamicImage> Astro コンポーネントが共通入口で、toOptimizedUrl() 経由で 2 系統を自動選択。

住み分け

場所 helper 元画像の出所
web/portal/admin web/portal/owner web/portal/marketing @parky/ui cdnImageUrl R2 cdn.parky.co.jp(管理画面アップロード)
web/home (Astro) web/home/src/lib/image-utils.ts toOptimizedUrl Supabase Storage(記事添付) + R2

Cache 動作

  • Edge: 1 日 (Cache-Control: public, max-age=86400, s-maxage=86400, stale-while-revalidate=2592000)
  • Worker Cache API: caches.default.put で URL ベース cache key
  • 同 URL + 同 query は次回 HIT(x-cdn-cache: HIT ヘッダ)

トラブルシュート

全部 fallback (302) になる

  • Cloudflare account が Free plan のまま → Pro 以上に upgrade 必要
  • Image Resizing が account dashboard で有効化されていない → ON にする
  • cf-image-error レスポンスヘッダが原因。ログ確認: wrangler tail --format json | jq 'select(.message[0]=="cf-image-error")'

CORS エラー

  • proxy レスポンスは Access-Control-Allow-Origin: * を強制付与済み。
  • それでも失敗する場合は client 側で crossorigin="anonymous"<img> に付ける。

ハッシュ不一致 (SRI 連動)

  • proxy の URL は SRI 対象外(query 違いで内容変動するため)。
  • <img> 自体は SRI なし、<link rel="preload"> で integrity を付ける場合は固定 URL のみ対応する。
↗ Source markdown (cloudflare-images.md)