1つのCloudflare Workersに静的アセットを配置してWorkerも起動させるとき

Static Assetsのルーティング

1つのCloudflare WorkersにおいてStatic Assetsを配置したうえでWorker Scriptも設定をすることができます。たとえば、wrangler.jsonを以下のようにします。

{
  "name": "worker",
  "compatibility_date": "2026-02-11",
  "main": "./worker/index.ts", // Worker Script
  "assets": {
    "directory": "./dist/" // Static Assets
  }
}

Cloudflare PagesでFunctionsを利用していた場合でWorkersに移行する際なんかはこういった設定をすることになるはずです。

このときのルーティングは以下のような挙動になります。

By default, if a requested URL matches a file in the static assets directory, that file will be served — without invoking Worker code. If no matching asset is found and a Worker script is present, the request will be processed by the Worker. (https://developers.cloudflare.com/workers/static-assets/#routing-behavior)

リクエストされたURLにStatic AssetsがあればStatic Assetsが優先、なければmainで指定したWorker Scriptが実行されます。とくに難しくはないですね。

SPAあるいは404ページを返す場合

デフォルトではStatic Assetsがないリクエストが来た時に404ページを返したりしたい場合、1つ考えられる方法はWorker Script内でenv.ASSETS.fetch()を呼び出す方法です。

ただ、もっと良い方法がありそれがnot_found_handlingというオプションを使う方法です。これはsingle-page-application404-pageの2つを指定でき、Static Assetsがないリクエストが来た時、それぞれindex.htmlあるいは404.htmlを返す挙動になります。

{
  "name": "worker",
  "compatibility_date": "2026-02-11",
  "main": "./worker/index.ts",
  "assets": {
    "directory": "./dist/",
    "not_found_handling": "404-page" // or "single-page-application"
  }
}

よくあるユースケースにはオプションを用意してくれていて便利ですね。

not_found_handlingを使用しているときのWorker Script

では、not_found_handlingを使用しているときにWorker Scriptが実行されるのはどんな場合なのか気になります。

たとえば、/api/dataのようなエンドポイントをWorker Scriptで用意している場合を考えます。このとき、/api/dataをブラウザで直接アクセスしたらWorker Scriptが実行されてレスポンスが返って…きません。てっきり実行されると思っていたので驚きました。おかしいな…?

でドキュメントを見直すと以下のように書かれていました。

..., navigation requests will not invoke the Worker script. A navigation request is a request made with the Sec-Fetch-Mode: navigate header, which browsers automatically attach when navigating to a page. This reduces billable invocations of your Worker script, and is particularly useful for client-heavy applications which would otherwise invoke your Worker script very frequently and unnecessarily. (https://developers.cloudflare.com/workers/static-assets/routing/static-site-generation/#navigation-requests)

ナビゲーションリクエスト、まさしくブラウザでURLを叩いたときはWorker Scriptは実行されない、ということでした。確かにindex.html404.htmlを返したいだけなのにWorker Scriptが呼び出されたら無駄ですね。ナビゲーションリクエストではない場合、たとえばfetch("/api/data")のようにすれば、Worker Scriptは実行されるようです。

ちなみにこの動作はある時期から変更された動作のようで互換性フラグが用意されています。

確実に動かすにはrun_worker_firstを使う

確実にWorker Scriptを実行したい場合はrun_worker_firstを使います。名前の通り、先にWorker Scriptを実行しまっせ~というオプションです。

{
  "name": "worker",
  "compatibility_date": "2026-02-11",
  "main": "./worker/index.ts",
  "assets": {
    "directory": "./dist/",
    "not_found_handling": "404-page",
    "run_worker_first": ["/api/*", "!/api/docs/*"] // "true"を指定すれば常にWorkerが先
  }
}

この例では/api/以下へのリクエストはまずWorker Scriptが実行されます。ただし、/api/docs/以下へのリクエストはStatic Assetsが優先されます。*!を使えて便利ですね。

もう1つ互換性フラグを無効(assets_navigation_has_no_effectにする方法も考えられます。これを無効にすると、ナビゲーションリクエストであっても、Static Assetsがないリクエストであれば、Worker Scriptが実行されるようになります。ただ、

... as this now means the fallback pages of 200 /index.html and 404 /404.html will be served ahead of invoking a Worker script and will therefore avoid incurring a charge. (https://developers.cloudflare.com/workers/configuration/compatibility-flags/#navigation-requests-prefer-asset-serving)

とあるようにコスト面では不利になるので、Worker Scriptを動かしたいパスのみをrun_worker_firstで指定するのが良さそうです。

参考

An unhandled error has occurred. Reload 🗙