Cloudflare Workers 静的アセットで PWA を配る

一言まとめ

新規プロジェクトは Pages ではなく Workers + 静的アセットが推奨。`wrangler.jsonc` の `assets.directory` だけの Worker 無し構成から始め、API が要るときは `run_worker_first: ["/api/*"]` で同居させる。manifest / Service Worker は vite-plugin-pwa、アイコンは resvg で SVG から生成。

作成 · 更新 · #cloudflare#workers#pwa#vite
目次 8 sections
  1. 01 Pages ではなく Workers
  2. 02 Worker 無しの最小構成
  3. 03 API を同居させる構成
  4. 04 カスタムドメイン
  5. 05 PWA まわり
  6. 06 Service Worker 自体のキャッシュ
  7. 07 関連
  8. 08 更新履歴

調査時点: 2026-08〜09。複数の小さな PWA を続けて作ったときの共通構成。

Pages ではなく Workers#

Cloudflare は新規プロジェクトに Workers + 静的アセットを使う方針を打ち出しており、Pages と機能・料金は同等になっている。まだデプロイしていない段階なら移行ではなく最初の選択なので、Workers を選ぶ。静的配信だけでなく、後からサーバ側の処理(API キーの中継など)を同じ Worker に置ける。

Worker 無しの最小構成#

wrangler.jsonc
{
"name": "my-pwa",
"assets": { "directory": "./dist" }
}

main を持たず assets.directory だけ。@cloudflare/vite-plugin は API を持たないなら入れない。

API を同居させる構成#

{
"name": "my-pwa",
"main": "worker/index.ts",
"assets": {
"directory": "./dist/client",
"binding": "ASSETS",
"not_found_handling": "single-page-application",
"run_worker_first": ["/api/*"]
}
}
  • run_worker_first に配列を渡すと、/api/* だけ Worker が先に受け、それ以外はアセット優先になる。別サービスを立てずに SPA と API が同居する。
  • not_found_handling: "single-page-application" で、知らないパスは index.html に返る。ハッシュルータ(#/path)を使うなら SPA フォールバック設定自体が不要になり、_redirects の置き忘れで直リンクが 404 になる事故が消える。
  • @cloudflare/vite-plugin を入れると dev でも workerd が動き、/api/* をローカルで叩ける。ビルド出力は dist/client(アセット)と dist/<worker名>(Worker)に分かれる。
  • 型が DOM と Workers で割れるので tsconfig.worker.json を分ける。

カスタムドメイン#

"routes": [{ "pattern": "app.example.com", "custom_domain": true }]

wrangler deploy を素で打つと *.workers.dev に出る。manifest の share_target.action のように同一 origin が要件のものがあるなら、ビルド時の origin とこの設定を揃える。

PWA まわり#

  • manifest と Service Worker は vite-plugin-pwa(generateSW / autoUpdate)。生成物(sw.js / manifest.webmanifest)はアセット側に入り、オフライン動作が保たれる(wrangler deploy --dry-run で確認できる)。
  • アイコンは public/icon.svg 1 枚から @resvg/resvg-js で PNG を生成するスクリプトを置く。ImageMagick 等が無い環境でも npm だけで完結する。
  • 2 本目以降は vite.config.ts の VitePWA 節と wrangler.jsonc とアイコン生成スクリプトをコピーして始める。

Service Worker 自体のキャッシュ#

エッジキャッシュが sw.js を長く保持すると、デプロイ後も古い Service Worker が配られ続ける。静的アセットのディレクトリに _headers を置き、/sw.js に Cache-Control: no-cache を付ける(Pages でも Workers 静的アセットでも同じ書式)。

/sw.js
Cache-Control: no-cache

SW をルート直下(/sw.js)に置けば全パスがスコープに入り、Service-Worker-Allowed ヘッダは要らない。HTTPS は Cloudflare 側で常に有効なので、Service Worker の secure context 要件は満たされる。

関連#

更新履歴#

  • 2026-09-11: 初版(ローカル調査ノート)
  • 2026-10-17: Wiki へ移植、概念ページとして再構成。Service Worker のキャッシュヘッダ(2026-06 の Pages 調査より)を追加

出典

  1. https://developers.cloudflare.com/workers/static-assets/binding/
  2. https://developers.cloudflare.com/workers/static-assets/headers/

同じトピック — Cloudflare