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 plan で
cf.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 のみ対応する。