Cloudflare Workers 静的アセットで PWA を配る
新規プロジェクトは Pages ではなく Workers + 静的アセットが推奨。`wrangler.jsonc` の `assets.directory` だけの Worker 無し構成から始め、API が要るときは `run_worker_first: ["/api/*"]` で同居させる。manifest / Service Worker は vite-plugin-pwa、アイコンは resvg で SVG から生成。
調査時点: 2026-08〜09。複数の小さな PWA を続けて作ったときの共通構成。
Pages ではなく Workers
Cloudflare は新規プロジェクトに Workers + 静的アセットを使う方針を打ち出しており、Pages と機能・料金は同等になっている。まだデプロイしていない段階なら移行ではなく最初の選択なので、Workers を選ぶ。静的配信だけでなく、後からサーバ側の処理(API キーの中継など)を同じ Worker に置ける。
Worker 無しの最小構成
{ "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.svg1 枚から@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-cacheSW をルート直下(/sw.js)に置けば全パスがスコープに入り、Service-Worker-Allowed ヘッダは要らない。HTTPS は Cloudflare 側で常に有効なので、Service Worker の secure context 要件は満たされる。
関連
更新履歴
- 2026-09-11: 初版(ローカル調査ノート)
- 2026-10-17: Wiki へ移植、概念ページとして再構成。Service Worker のキャッシュヘッダ(2026-06 の Pages 調査より)を追加