
Cloudflare Flagshipのフラグをwrangler devで切り替える:ローカルストアの操作とflags pull・remote: trueの使い分け
どうも!オペ部の西村祐二です!
Cloudflare FlagshipはCloudflareのフィーチャーフラグのサービスで、Workersからはバインディング経由でフラグを評価します。フラグはアプリという単位にまとめて作り、Workerのバインディングはapp_idでアプリを指定します。新しい機能をフラグの裏に隠して開発していると、ローカルでフラグをオンにした場合とオフにした場合の両方を試したくなります。ただ、そのたびにリモートのフラグを切り替えるわけにはいきません。
2026年11月2日のChangelogで、Flagshipのバインディングがローカル開発ではリモートのアプリを呼ばず、ローカルのフラグストアでフラグを評価することが告知されました。ローカルのフラグは、wrangler flagship flagsのコマンドに--localを付けるか、Local Explorerの画面で作成・変更します。
npx wrangler flagship flags create <APP_ID> new-checkout --local # dev を止めた状態で作る
npx wrangler flagship flags update <APP_ID> new-checkout --default on --local
npx wrangler dev # ローカルストアで評価される
# dev の実行中は http://localhost:8787/cdn-cgi/local/explorer から変更する
# リモートのフラグを使うなら、dev を止めて flags pull <APP_ID> でコピーするか、バインディングに remote: true を付ける
# <APP_ID> は wrangler.jsonc の flagship バインディングの app_id(ローカルストアだけなら Cloudflare 上にない ID でも動く)
Wrangler 4.157.0で試したところ、wrangler devの実行中は--localのコマンドもflags pullもエラーで止まりました。この記事では、CLIとLocal Explorerでフラグを変えたときにWorkerの評価がどう変わるかと、2つの使い分けをまとめます。
devは空のローカルストアで評価するようになったので、Cloudflare上で運用しているフラグと同じ値でローカルの動きを確かめたいときは、別の手段が要ります。後半では、Cloudflare上に作ったFlagshipのアプリを使います。flags pullでリモートのフラグをコピーしたときとremote: trueでリモートのフラグを評価したときの動き、--localを付け忘れたときに何が変わるかを確かめます。
今回の変更点
- 告知日: 2026年11月2日(Evaluate Flagship flags locally)
- 対象バージョン: ローカルストアでの評価は、Wranglerなら4.135.0以降、
@cloudflare/vite-pluginなら1.56.0以降です(どちらも2026年9月18日公開)。Local ExplorerでのFlagshipの管理は、Wranglerなら4.148.0以降、Vite pluginなら1.63.0以降です - 変更の要点:
wrangler dev、Cloudflare Vite plugin、Vitest integration、cf devでのローカル開発では、Flagshipのバインディングがローカルのフラグストアでフラグを評価します- ローカルストアは空の状態で始まるので、フラグを作るまではコードに書いたデフォルト値が返ります
- フラグは
--localを付けたコマンド、Local Explorer、cfCLIで管理します。リモートのフラグはnpx wrangler flagship flags pull <APP_ID>でまとめてコピーできます - これまでどおりリモートのアプリで評価したい場合は、バインディングに
remote: trueを付けます
Configurationのドキュメントによると、pullは自動では行われません。そのためwrangler devはオフラインでも動き、ローカルで変えたフラグがリモートの値で自動的に上書きされることはありません。なお、wrangler flagshipのコマンドは実行のたびにオープンベータである旨の警告を出します。Local Explorerも、画面左上のロゴの横にBetaと表示されます。
ローカルフラグストアの仕組み
図は自作した概念図で、公式の構成図ではありません。wrangler devで動くWorkerのバインディングは、手元の.wrangler/state/v3/flagshipにあるローカルフラグストアでフラグを評価します。この記事でストアのフラグを手で変えるのは、dev実行中のLocal Explorerと、devを止めて実行する--local付きのコマンドの2つです。
devのWorkerとローカルストアがリモートのアプリとやり取りするのは、remote: trueを付けたときと、devを止めてflags pullでコピーするときだけです。一方で、フラグのコマンドを--localなしで実行すると、リモートのアプリを直接操作します。

試してみる
環境
- Ubuntu 24.04(arm64)のコンテナ、Node.js 24
- Wrangler 4.157.0(プロジェクトに
npm i -D wrangler@4.157.0で入れました)。本文のコマンドはnpx wranglerで書いていますが、検証ではスクリプトからプロジェクトのnode_modules/.bin/wranglerを直接実行しました - 手順1〜5(2026年11月8日): Cloudflareにはログインしていません。パッケージのインストール以外は、ネットワークを切った状態で、スクリプトから非対話で実行しました。手順7で触れる、APIトークンなしで
remote: trueを起動した結果も同じ条件です - 手順6〜7(2026年11月10日): Workers Freeプランの検証用アカウントにFlagshipのアプリを作り、APIトークンで認証して、ネットワークにつないだ状態で実行しました
- コマンド出力は、毎回出るWranglerのバナーとオープンベータの警告の行と、各行の先頭の記号(絵文字)を省いて載せています。アプリのIDは
<APP_ID>に置き換えています
1. Flagshipのバインディングを持つWorkerを用意する
バインディングのapp_idには、Cloudflare上に存在しない仮のID(demo-app)を入れました。ローカルストアだけで評価するなら、Cloudflare上にアプリを作らなくても試せるかを確かめるためです。
{
"name": "flagship-local-demo",
"main": "src/index.js",
"compatibility_date": "2026-11-01",
"flagship": [
{ "binding": "FLAGS", "app_id": "demo-app" }
]
}
Workerは、new-checkoutフラグをgetBooleanDetails()で評価し、結果をそのまま返します。ターゲティングを試すため、クエリ文字列のplanを評価コンテキストに渡しています。
export default {
async fetch(request, env) {
const url = new URL(request.url);
const plan = url.searchParams.get("plan") ?? "free";
const details = await env.FLAGS.getBooleanDetails("new-checkout", false, { plan });
return Response.json({ plan, newCheckout: details.value, details });
},
};
2. フラグがない状態でwrangler devを起動する
wrangler devは--ip 127.0.0.1 --port 8787を付けて起動したので、以降のURLは127.0.0.1です。
npx wrangler dev --ip 127.0.0.1 --port 8787
バインディングの一覧で、Modeがlocalと表示されました。ログインもネットワークもない状態でも、エラーにならずに起動しました(ネットワークがないためRequest.cfを取得できないという警告は出ます。出力では省略しています)。
Your Worker has access to the following bindings:
Binding Resource Mode
env.FLAGS (demo-app) Flagship local
Starting local server...
[wrangler:info] Ready on http://127.0.0.1:8787
この時点ではストアが空なので、Workerはコードに書いたデフォルト値のfalseを返しました。reasonはERROR、errorCodeはFLAG_NOT_FOUNDです。
curl 'http://127.0.0.1:8787/?plan=free'
{"plan":"free","newCheckout":false,"details":{"flagKey":"new-checkout","value":false,"variant":"default","reason":"ERROR","errorCode":"FLAG_NOT_FOUND","errorMessage":"Flag 'new-checkout' not found"}}
3. devを止めて--localでフラグを作る
devを起動したまま別のターミナルで--localのコマンドを実行すると、次のエラーで止まりました。
npx wrangler flagship flags list demo-app --local
[ERROR] Cannot use local Flagship commands while a dev session for 'flagship-local-demo' is running. Stop the dev session and retry.
devを止めてから実行すると、フラグを作成できました。バリエーション(variation)を指定しないと、on(true)とoff(false)の2つを持つboolean型のフラグになり、offを返します。
npx wrangler flagship flags create demo-app new-checkout --local
Created flag
new-checkout [boolean] ● enabled
VARIATIONS
on true
off false default
RULES
(none) — always serves "off"
なお、npx wrangler devをバックグラウンドで起動してkill(SIGTERM)で止めた回では、その後も同じエラーが出ました。次の回はwranglerの実行ファイルを直接起動し、Ctrl+Cと同じSIGINTで止めました。wranglerとworkerdのプロセスが残っていないことを確かめてから実行すると、エラーは出ませんでした。シグナルの違いが原因か、npx経由でプロセスが残ったためかは切り分けていません。
update --default on --localでデフォルトをonに変えると、evaluate --localの結果はValue true、Reason STATICになりました。STATICは、ルールのない有効なフラグでデフォルトを返したときの理由です(Evaluation reasons)。evaluate --localを使うと、Workerを起動しなくても評価結果を確かめられます。
続けて、デフォルトをoffに戻し、planがenterpriseのときだけonを返すルールを付けました。ルールはWranglerのコマンドリファレンスにある--ruleの構文で書いています。updateの--ruleは既存のルールを置き換えるので、ルールを残したまま足す場合は--add-ruleを使います。
npx wrangler flagship flags update demo-app new-checkout --default off \
--rule "serve=on; when=plan equals enterprise" --local
npx wrangler flagship flags evaluate demo-app new-checkout --context plan=enterprise --local
npx wrangler flagship flags evaluate demo-app new-checkout --context plan=free --local
new-checkout evaluated
Value true
Variant on
Reason TARGETING_MATCH
new-checkout evaluated
Value false
Variant off
Reason DEFAULT
--localを付けずにflags list demo-appを実行すると、リモートのアプリが対象になり、In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable ...というエラーが出ました。文面のとおり、非対話のスクリプトから実行したためにAPIトークンを求められており、対話ターミナルでは出方が変わる可能性があります。APIトークンを設定した状態で--localを付けなかった場合の動きは、手順6で確かめます。
4. Workerの評価結果をCLIと比べ、無効化も試す
devを起動し直してWorkerにリクエストすると、CLIのevaluateと同じ結果になりました。
| リクエスト | value | variant | reason |
|---|---|---|---|
/?plan=free |
false |
off |
DEFAULT |
/?plan=enterprise |
true |
on |
TARGETING_MATCH |
次に、devを止めてflags disable demo-app new-checkout --localでフラグを無効にし、devを起動し直して/?plan=enterpriseにリクエストしました。ルールに当てはまるリクエストでもデフォルトのoff(false)が返り、reasonはDISABLEDでした。フラグを無効にしてデフォルトの値に戻す操作(キルスイッチ)を、リモートより先にローカルで試せます。確認したあと、devを止めてflags enable demo-app new-checkout --localで有効に戻しました。
5. devの実行中はLocal Explorerで変更する
devを起動したままhttp://127.0.0.1:8787/cdn-cgi/local/explorerを開くと、サイドバーのFlagshipの下にバインディング名のFLAGSがありました。FLAGSを選ぶと、アプリID(demo-app)のフラグ一覧が表示されます。CLIで作ったnew-checkoutもここに出ています。
フラグをクリックするとEdit flagの画面が開きます。見出しの下にはSaves to the local store. The Worker running in dev picks the change up on its next read.とあり、保存先はローカルストアで、dev中のWorkerは次に読み込んだときに変更を反映すると説明されています。ここでonのDefaultを選んでSave changesを押しました(赤枠は追記)。

保存すると、一覧のDefault variantがon trueに変わりました。

devを再起動せずにWorkerへリクエストすると、保存前はfalseだったplan=freeでも、true(variantはon、reasonはDEFAULT)が返りました。
devを止めてからflags get demo-app new-checkout --localを実行すると、onがデフォルトになっており、planのルールも残っていました。そのあとdevを再起動しても、Local Explorerで変えたデフォルト(on)で評価されました。Local Explorerで保存した内容もCLIで変えた内容も、同じローカルストア(.wrangler/state/v3/flagship)に入ります。
このストアは.wrangler/stateの下にあります。そのため、.wrangler/stateを削除してからflags list demo-app --localを実行すると、No flags in this app yet.と表示され、作ったフラグはなくなっていました。
6. flags pullでリモートのフラグをローカルストアにコピーする
ここからは、Cloudflare上のFlagshipのアプリを使います。リモートのアプリを操作するコマンドには、wrangler loginでのログインか、Flagshipの権限を付けたAPIトークンが要ります。Flagshipの権限がないトークンでは、wrangler flagship apps listがAuthentication error [code: 10000]になりました。アプリを作成し、--localを付けずにnew-checkoutフラグを作って、手順3と同じplanのルールを付けました。
npx wrangler flagship apps create ynishimura-flagship-local-demo
npx wrangler flagship flags create <APP_ID> new-checkout
npx wrangler flagship flags update <APP_ID> new-checkout --default off \
--rule "serve=on; when=plan equals enterprise"
wrangler.jsoncのapp_idを、作成したアプリのIDに書き換え、.wranglerを削除してローカルストアを空にしました。そのうえで、ローカルで変えたフラグとローカルにだけあるフラグをpullがどう扱うかを見るため、pullの前にローカルストアにだけ2つのフラグを作りました。リモートにないlocal-onlyと、リモートと同じキーで、ルールを付けずにデフォルトをonにしたnew-checkoutです。
npx wrangler flagship flags create <APP_ID> local-only --local
npx wrangler flagship flags create <APP_ID> new-checkout --local
npx wrangler flagship flags update <APP_ID> new-checkout --default on --local
devを止めた状態でpullを実行すると、リモートのフラグを1つコピーし、local-onlyには触れなかったと表示されました。
npx wrangler flagship flags pull <APP_ID>
Pulled 1 flag from <APP_ID> into the local flag store.
Left 1 local-only flag untouched: local-only
These do not exist in the remote app, either because they were created locally or because they were deleted remotely.
pullのあとのローカルストアでは、ローカルでデフォルトをonにしていたnew-checkoutが、リモートの内容(デフォルトoff、ルール1つ)に置き換わっていました。同じキーのフラグを上書きすることは、FlagshipのWranglerコマンドのリファレンスにもMatching keys are overwritten.と書かれています。
npx wrangler flagship flags list <APP_ID> --local
┌──────────────┬───────────┬─────────┬─────────┬───────┐
│ key │ status │ type │ default │ rules │
├──────────────┼───────────┼─────────┼─────────┼───────┤
│ local-only │ ● enabled │ boolean │ off │ 0 │
├──────────────┼───────────┼─────────┼─────────┼───────┤
│ new-checkout │ ● enabled │ boolean │ off │ 1 │
└──────────────┴───────────┴─────────┴─────────┴───────┘
devを起動してWorkerにリクエストすると、plan=freeはfalse(reasonはDEFAULT)、plan=enterpriseはtrue(reasonはTARGETING_MATCH)で、リモートのフラグの設定どおりの結果になりました。また、devを動かしたままpullを実行すると、手順3の--localのコマンドと同じエラー(Cannot use local Flagship commands while a dev session ... is running)で止まりました。
次に、devを止めて、--localを付けずにupdateを実行し、リモートのデフォルトをonに変えました。
npx wrangler flagship flags update <APP_ID> new-checkout --default on
--localなしのflags getで見たリモートのデフォルトはonになった一方で、flags get --localで見たローカルストアのデフォルトはoffのままでした。--localを付け忘れた場合も、このようにリモートのフラグだけが書き換わります。devを起動し直しても、plan=freeへのリクエストにはfalseが返りました。もう一度pullすると、ローカルストアのデフォルトもonになりました。
リモートで削除したフラグの扱いも確かめました(手順7のあとに実行しています)。--localを付けずにflags delete <APP_ID> new-checkout --forceを実行し、リモートのフラグを消しました。そのあとpullすると、new-checkoutもlocal-onlyと同じく、ローカルストアにだけあるフラグとして扱われ、ローカルストアに残りました。
Pulled 0 flags from <APP_ID> into the local flag store.
Left 2 local-only flags untouched: local-only, new-checkout
7. remote: trueでリモートのアプリのフラグを評価する
ローカルストアを空にした状態で、バインディングに"remote": trueを付けてwrangler devを起動しました。このときのリモートのnew-checkoutは、手順6でデフォルトをonに変えた状態(ルール1つ)です。手順6の最後に書いた削除は手順7のあとに行ったので、この時点ではまだ残っています。
wrangler.jsoncのflagshipの部分だけを抜き出すと、次のとおりです。
"flagship": [
{ "binding": "FLAGS", "app_id": "<APP_ID>", "remote": true }
]
バインディングのModeはremoteになり、devはリモートとの接続を確立してから起動しました(--port 8788を付けて起動しています)。
env.FLAGS (<APP_ID>) Flagship remote
Establishing remote connection...
Starting local server...
[wrangler:info] Ready on http://127.0.0.1:8788
なお、手順1〜5と同じく、APIトークンを設定せずネットワークも切った状態で起動したとき(2026年11月8日、非対話のスクリプトから実行)も、Modeはremoteになりました。ただ、リモートとの接続を始める段階で次のエラーになり、devは起動しませんでした。
[ERROR] Failed to start the remote proxy session. Error reloading remote server: In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work.
APIトークンを設定して起動したdevでは、ローカルストアは空ですが、Workerはリモートのフラグで評価した結果を返しました。
| リクエスト | value | variant | reason |
|---|---|---|---|
/?plan=free |
true |
on |
DEFAULT |
/?plan=enterprise |
true |
on |
TARGETING_MATCH |
devを動かしたまま、--localなしのupdateでリモートのデフォルトをoffに戻しました。
npx wrangler flagship flags update <APP_ID> new-checkout --default off
そのあと5秒おきに/?plan=freeへリクエストすると、devを再起動しなくても評価は変わりました。ただし、updateの完了から31秒後のリクエストまではtrueが返り、36秒後のリクエストでfalse(variantはoff)になりました。測ったのは1回だけで、5秒おきのリクエストなので、値が変わったのは31〜36秒の間というところまでしか分かりません。
Conceptsのドキュメントには、フラグを変えてから全体に反映されるまで最大30秒かかることがあり、その間は前の値が返る場合があると書かれています。今回はそれと同じくらいか、少し長くかかりました。remote: trueのdevから測った時間なので、Flagship側の反映とWranglerのリモート接続のどちらで時間がかかったのかは切り分けていません。
CLIの評価でも、反映が終わる前の値とみられる結果を確認しました。手順6でルールを付けた直後に--localなしのflags evaluateを実行すると、plan=enterpriseでもルールを付ける前の結果(Value false、Reason STATIC)が返りました。約2分後、手順7の確認まで終えてから実行し直すと、TARGETING_MATCHになりました(その間にデフォルトを2回変えていますが、ルールはそのままです)。
検証結果の考察
- 確認できたこと: Wrangler 4.157.0の
wrangler devでは、Flagshipのバインディングはローカルストアでフラグを評価し、Workerはログインもネットワークもない状態で動きました。存在しないアプリIDでも、ローカルストアにフラグを作れば評価できます。--localのコマンドとLocal Explorerはどちらも同じストアを読み書きします - 公式情報と照合したこと: 空のストアではコードのデフォルト値が返ることは、Changelogとドキュメントの記載どおりでした。Local Explorerでの変更がdevの再起動なしで反映されることも、画面の説明と一致しました。
pullが自動では行われないこと、pullが同じキーのフラグを上書きし、ローカルストアにだけあるフラグを残すことも、ドキュメントの記載どおりでした。remote: trueでは、ローカルストアが空でもリモートのフラグで評価され、ローカルストアを使わないというドキュメントの説明と矛盾しませんでした。フラグの変更が反映されるまでの時間は、ドキュメントの最大30秒に対して、今回は31〜36秒でした(1回の測定) - ドキュメントで見つけられなかったこと: Wrangler 4.157.0では、devの実行中は
--localのコマンドもpullも使えませんでした。確認した範囲のドキュメントには書かれておらず、エラーメッセージで分かりました。また、リモートで削除したフラグもローカルにだけあるフラグとして扱われ、pullのあとも残ることは、pullの出力で分かりました
Wrangler 4.157.0で試した範囲では、CLIとLocal Explorerは、devを動かしているかどうかで使い分けることになります。
| やりたいこと | dev実行中 | dev停止中 |
|---|---|---|
| フラグを変える | Local ExplorerのEdit flag | --localのコマンド |
| フラグを作る | Local ExplorerのCreate flag(今回は試していない) | flags create --local |
| リモートのフラグをローカルストアにコピーする | できない(flags pullもエラーになった) |
flags pull |
| 評価結果を確かめる | Workerへリクエスト | flags evaluate --local |
Local Explorerは、devを動かしながらオンとオフを行き来して、Workerの応答や画面の変化を見る用途に向いていそうです。決まったフラグの状態を毎回作り直したい場合は、devを止めて--localのコマンドを流すほうが、スクリプトとして手順を残しやすいです。
リモートのフラグを使う方法は2つあります。試した結果とドキュメントの記載をまとめると、次のとおりです。
flags pull |
remote: true |
|
|---|---|---|
| Workerが評価するフラグ | ローカルストアにコピーしたフラグ | リモートのアプリのフラグ |
| リモートの変更 | もう一度pullするまで反映されない |
devを再起動しなくても反映された(今回は更新から31〜36秒後) |
| ローカルでの変更 | --localやLocal Explorerで変えた値が使われる。次のpullで同じキーのフラグは上書きされる |
使われない(ドキュメントの記載。今回はローカルストアが空の状態で、リモートの値で評価されることを確認) |
| 認証 | pullを実行するときに要る(ドキュメントの記載) |
devの起動に要る |
ほかの人がダッシュボードで切り替えた値をそのまま見ながら開発したい場合はremote: true、リモートの値を出発点にしてローカルで値を変えながら試したい場合はpullが合いそうです。
今回試していないのは、次の範囲です。
wrangler loginでログインした状態で、--localなしのコマンドやremote: trueを使う場合。手順6〜7はAPIトークンで認証して試しました。また、そのトークンにはFlagship以外の権限(Workersスクリプトの編集など)も付いていたので、Flagshipの権限だけのトークンでremote: trueのdevを起動できるかは確かめていません- Wrangler 4.158.0以降(2026年11月10日時点の最新は4.160.0)で、devの実行中に
--localのコマンドとpullがエラーになるか - Vite plugin、Vitest integration、
cf devとcf flagship ... --localでのローカルストア。ドキュメントによると、保存先はVite plugin 1.xならwrangler devと同じ.wrangler/state、2.0 betaなら.cloudflare/stateで、2.0 betaでpullするときは--persist-to .cloudflare/stateが要ります。cfの--localも、--persist-to .wrangler/stateを付ければwrangler devと同じストアを操作できるとされています - Local ExplorerのTestボタンでの評価と、Create flagからの作成。Local Explorerのドキュメントでは、どちらも使えると書かれています
- percentage rollout(ユーザーを割合で振り分ける)やsplit(トラフィックを複数のバリエーションに割合で分ける)、string・number・JSON型のフラグ。ドキュメントによると、
pullはpercentage rolloutの振り分けに使うアカウントタグもローカルストアに入れますが、今回のフラグにはpercentage rolloutを設定していないので確かめていません - Wrangler 4.135.0より前のバージョンでの動き。古いWranglerを使っているプロジェクトで、この記事の手順がどう動くかは分かりません
まとめ
Cloudflare Flagshipのバインディングは、Wrangler 4.135.0以降のwrangler devではローカルのフラグストアでフラグを評価します。ストアは空で始まり、フラグがないとコードのデフォルト値が返ります。フラグは、devを止めた状態ならwrangler flagship flags ... --localで作成・変更でき、dev実行中ならLocal Explorer(/cdn-cgi/local/explorer、Wrangler 4.148.0以降)で変更できました(Local Explorerからの作成は今回試していません)。Local Explorerで保存した変更は、devを再起動しなくても次の評価から反映されました。
リモートのフラグは、自動ではローカルストアに入りません。flags pullは、リモートのフラグをローカルストアにコピーし、同じキーのフラグをリモートの内容で上書きします。ローカルストアにだけあるフラグは、リモートで削除したフラグも含めて残ります。バインディングにremote: trueを付けると、devのWorkerはリモートのアプリのフラグで評価します。リモートの変更は、devを再起動しなくても反映されました(今回の1回の測定では31〜36秒後)。ほかの人がダッシュボードで切り替えた値をそのまま見ながら開発したい場合はremote: true、リモートの値を出発点にしてローカルで値を変えながら試したい場合はpullが合いそうです。
ローカルフラグストアは、リモートのフラグを変えずに、フラグのオンとオフ両方の経路をローカルで確かめたい場合に使えます。ただし、--localを付けないコマンドはリモートのアプリを操作する点と、Wrangler 4.157.0ではdevの実行中に--localのコマンドもpullもエラーになった点に気を付ける必要がありそうです。
誰かの参考になれば幸いです。
参考リンク:
- Evaluate Flagship flags locally(Cloudflare Changelog)
- Flagship: Configuration(Local development)
- Flagship: Binding API Methods
- Flagship: Concepts(Flag propagation)
- Flagship: Evaluation reasons
- Wrangler commands: Flagship
- Flagship: Wrangler commands(Local flag store)
- Local Explorer
- Cloudflare Flagship



