はじめに
メンテナンスバナーは、告知期間が終了したら非表示にする必要があります。表示したままにすると、訪問者は古い障害がまだ続いていると誤解する可能性があります。この実験では、KV から通知を読み取り、まだ表示すべきかどうかを判断する Worker を作成します。
期限は 2 種類あります。アプリケーションの表示期限は、コードがメッセージの表示を停止する時刻を示します。KV の有効期限は、ストレージサービスがエントリを削除する時刻を示します。ストレージには意図的に古い参照レコードを残し、データが存在している間でもアプリケーションが期限切れのコンテンツを非表示にできることを確認します。その後、2 つ目のレコードが Cloud KV から自動的に期限切れになる様子を観察します。
最初に Serve Account Preferences を完了してください。この独立した VM には Node.js 22.22.0 と、プロジェクトローカルの Wrangler 4.131.1 が /home/labex/project/temporary-notices にインストールされています。自分の学習用アカウントを使用し、アカウントの読み取り、Worker の書き込み、KV の書き込みに同じ権限を付与してください。使い捨ての Worker と namespace を 1 つずつ作成し、合成メッセージだけを使用して、ログアウト前にクリーンアップします。購入済みのドメインや有料アップグレードは必要ありません。ハンドラーの作成とテストに加えて、時刻を使った観察に約 5 分かかります。
通知用 Namespace に接続する
このステップでは、一時通知用の新しい namespace に接続します。期限切れの実験によって別のアプリケーションのデータが削除されないように、専用の namespace と一意の Worker 名を使用してください。NOTICES バインディングによって、ハンドラーからこのリソースに接続します。
用意されているプロジェクトに移動します。
cd /home/labex/project/temporary-notices
一意の名前を一度だけ生成します。openssl rand -hex 6 はランダムなサフィックスを出力し、$(...) はその値を名前に埋め込みます。シェル変数に保存することで、このターミナルで続けて実行するコマンドから同じ名前を使用できます。
WORKER_NAME="labex-notices-$(openssl rand -hex 6)"
printf '%s\n' "$WORKER_NAME"
この VM を認証します。アカウントの識別情報を読み取るだけでなく、Workers Scripts Write によってデプロイと削除を実行でき、Workers KV Write によってこの実験の namespace とキーを管理できます。
npx wrangler login --device --browser=false --scopes account:read user:read workers_scripts:write workers_kv:write
表示されたデバイス用リンクをブラウザーで開き、現在のコードを入力します。要求された権限と学習用アカウントを確認して、Wrangler を認証してください。同意ページにバックグラウンドアクセスが表示される場合もあります。ターミナルに戻り、ログインが完了するまで待ちます。
Create a Feature Flag Store で付与したものと同じ Worker および KV の書き込み権限を確認します。認証する前に、学習用アカウントが正しいことを確認してください。
npx wrangler whoami --json
loggedIn: true と、学習用アカウントの name を確認します。アカウントが 1 つだけ表示される場合も確認してください。そのアカウントの id をコピーします。以下の設定の YOUR_ACCOUNT_ID をその値に置き換えてから、コマンドを実行します。ここで使う cat のヒアドキュメントは、2 つの JSON 行の間にあるすべての内容をファイルに書き込みます。> はファイルを置き換えます。区切り文字を引用符で囲んでいないため、シェルは $WORKER_NAME を展開します。
cat > wrangler.jsonc <<JSON
{
"name": "$WORKER_NAME",
"main": "src/index.js",
"compatibility_date": "2026-07-30",
"account_id": "YOUR_ACCOUNT_ID",
"workers_dev": true
}
JSON
そのアカウントに namespace を作成します。namespace のタイトルには Worker の一意の名前が含まれるため、後でこの 2 つのリソースを識別できます。--update-config=false を指定すると、バインディングの編集内容が自動的にファイルへ反映されず、自分で確認できます。
npx wrangler kv namespace create "$WORKER_NAME-notices" --update-config=false
出力に新しい namespace ID が表示されます。その ID をコピーし、次の完全な設定にある YOUR_ACCOUNT_ID と YOUR_NAMESPACE_ID を置き換えます。NOTICES はコード内で使用するバインディング名で、ID は実際の Cloudflare リソースを識別します。
cat > wrangler.jsonc <<JSON
{
"name": "$WORKER_NAME",
"main": "src/index.js",
"compatibility_date": "2026-07-30",
"account_id": "YOUR_ACCOUNT_ID",
"workers_dev": true,
"kv_namespaces": [
{ "binding": "NOTICES", "id": "YOUR_NAMESPACE_ID" }
]
}
JSON
npx wrangler kv namespace list
この実験で作成した namespace のタイトルを探し、ID がファイル内の ID と一致することを確認します。他の namespace が表示されても、そのままにしてください。この設定によって、後続のコマンドで使用するアカウントとリソースが決まります。バインディングは namespace への参照であり、データのコピーではありません。
データを削除する前に古い通知を非表示にする
このステップでは、表示動作とストレージのクリーンアップを分けて扱います。タイムスタンプは、ある時点を表す数値です。ここで displayUntil は、UTC の 1970 年 1 月 1 日 00:00:00 から数えた Unix 秒を使用します。Date.now() はミリ秒を返すため、比較する前にハンドラーで 1000 で割ります。同じ単位で比較することで、よくある期限計算のバグを避けられます。
次の引用符付きヒアドキュメントを使ってハンドラーを書き込みます。
cat > src/index.js <<'JS'
export default {
async fetch(request, env) {
const url = new URL(request.url);
const key = url.searchParams.get("key") ?? "notice:maintenance";
if (url.pathname !== "/notice" || !/^notice:[a-z]{1,20}$/.test(key)) {
return new Response("Not found", { status: 404 });
}
let entry;
try {
entry = await env.NOTICES.getWithMetadata(key, "text");
} catch {
return Response.json({ error: "Notice storage unavailable" }, { status: 503 });
}
if (entry.value === null) {
return Response.json({ visible: false, reason: "missing" });
}
let notice;
try {
notice = JSON.parse(entry.value);
} catch {
return Response.json({ visible: false, reason: "invalid" });
}
if (!notice || typeof notice.message !== "string" || !notice.message.trim() ||
!Number.isSafeInteger(notice.displayUntil) || notice.displayUntil <= 0) {
return Response.json({ visible: false, reason: "invalid" });
}
if (Math.floor(Date.now() / 1000) >= notice.displayUntil) {
return Response.json({ visible: false, reason: "expired" });
}
return Response.json({
visible: true, message: notice.message,
kind: entry.metadata?.kind === "maintenance" ? "maintenance" : "general"
});
}
};
JS
key クエリパラメーターは合成通知を選択します。指定しない場合、ハンドラーは notice:maintenance を使用します。アプリケーションは、存在しない通知、形式が不正な通知、期限切れの通知を、理由を示す JSON レスポンスとともに非表示にします。KV の読み取りに失敗した場合は、通知がないかのように扱わず 503 を返します。メタデータの kind フィールドは通知の種類を示します。メタデータがない場合や想定外の値の場合は general にフォールバックします。
期限の比較には >= を使用しているため、通知は期限の時点で非表示になり、1 秒後まで表示されることはありません。このチェックはリクエストごとに実行されます。すでにバナーを表示している Web ページでは、独自のタイマーを使って再読み込みまたは削除する必要もあります。Worker のレスポンスだけで、すでに描画されたページを変更することはできません。
意図的に古い参照用通知をローカルに保存します。期限 1 は 1970 年の既知の時点なので、アプリケーションから見ると、このレコードはすでに期限切れです。KV の有効期限は意図的に設定しないため、レコードは確認用に残ります。
npx wrangler kv key put notice:reference '{"message":"Old maintenance notice","displayUntil":1}' --binding NOTICES --local --metadata '{"kind":"maintenance"}'
npx wrangler dev --local --ip 0.0.0.0 --port 8080 > local.log 2>&1 &
DEV_PID=$!
cat local.log
バックグラウンドのサーバーは出力を local.log に書き込みます。ポート 8080 で準備完了と表示されるまで、ログのコマンドを繰り返し実行してください。準備ができたら、古い参照をリクエストします。
curl -i 'http://127.0.0.1:8080/notice?key=notice:reference'
HTTP 200 と {"visible":false,"reason":"expired"} が返ることを確認します。URL を引用符で囲むことで、URL 内の疑問符がシェルのファイル名構文として解釈されるのを防ぎます。エントリがまだ存在することを確認します。
npx wrangler kv key get notice:reference --binding NOTICES --local --text
JSON はまだ存在しています。自動削除ではなく、コードによって古い通知の表示が防止されたことが分かります。存在しないキーに対しても安全に動作することを確認します。
curl -i 'http://127.0.0.1:8080/notice?key=notice:missing'
{"visible":false,"reason":"missing"} が返ることを確認します。クリーンアップが終わるまで、ローカルサーバーは起動したままにしてください。
2 つの期限を持つ通知を公開する
このステップでは、まず Worker を公開し、その後、短時間だけ有効なクラウド通知の表示期間を開始します。時計を動かし始める前にエンドポイントを準備しておくことで、実際の結果を確認する時間を確保できます。
リモート namespace に、同じく KV の有効期限を設定しない参照用レコードを作成します。
npx wrangler kv key put notice:reference '{"message":"Old maintenance notice","displayUntil":1}' --binding NOTICES --remote --metadata '{"kind":"maintenance"}'
npx wrangler deploy
生成された Worker 名と NOTICES バインディングを確認し、出力された実際の公開 URL を保存します。
WORKER_URL="https://YOUR_WORKER.YOUR_SUBDOMAIN.workers.dev"
curl -i "$WORKER_URL/notice?key=notice:reference"
古い参照通知が expired として非表示になることを確認します。ホスト名の準備ができていない場合は、時間を置いて再試行してから、時刻を使う操作を開始してください。まだデフォルトのメンテナンスキーをリクエストしないでください。存在しない KV キーの読み取りもキャッシュされる可能性があります。
次のコマンドを実行する前に、残りの手順を読んでください。date +%s は VM の現在時刻を Unix 秒で返し、$((...)) はシェルの算術演算を実行します。ここでは 3 分後に通知の表示を停止し、その 1 分後に KV によって通知を削除させます。
DISPLAY_UNTIL=$(($(date +%s) + 180))
KV_EXPIRES=$((DISPLAY_UNTIL + 60))
実際のアプリケーション用ペイロードを書き込みます。引用符で囲んでいない JSON 区切り文字によって、数値の期限がファイルに挿入されます。
cat > notice.json <<JSON
{"message":"Maintenance starts soon","displayUntil":$DISPLAY_UNTIL}
JSON
--path はファイルから値を読み取ります。--expiration は Unix 秒で指定した絶対的な KV の有効期限を設定し、--metadata は値とともに通知のカテゴリを追加します。
npx wrangler kv key put notice:maintenance --path notice.json --binding NOTICES --remote --expiration "$KV_EXPIRES" --metadata '{"kind":"maintenance"}'
KV は、書き込み時点からの秒数で表す相対 TTL(有効期間)にも対応しています。Wrangler ではこのオプションを --ttl、バインディング API では expirationTtl と呼びます。相対有効期限と絶対有効期限のどちらも、現在から少なくとも 60 秒後に設定する必要があります。ここでは 2 つの期限を直接比較できるように、絶対有効期限を使用します。KV expiration options を参照してください。
npx wrangler kv key list --binding NOTICES --remote
notice:maintenance、その expiration、kind メタデータを探します。参照用レコードには KV の有効期限が設定されていません。次に、実際に稼働しているメッセージを読み取ります。
curl -i "$WORKER_URL/notice"
HTTP 200 と {"visible":true,"message":"Maintenance starts soon","kind":"maintenance"} が返ることを確認します。表示期間が終了する前に、このステップのチェックを実行してください。このチェックでは、実際のクラウド上の値、メタデータ、KV の有効期限、バインディング、稼働中のレスポンスを確認します。保存したタイムスタンプだけでは、通知が保存された証拠にはなりません。
表示期間を逃した場合は、2 つの時刻の代入をもう一度実行し、notice.json を書き直して、新しい期限でリモートへの書き込みを繰り返します。短時間に何度も書き込まないでください。以前の読み取り結果がキャッシュされ、置き換えた値が反映されるまで時間がかかる場合があります。反映を待って、アクティブなチェックを繰り返してください。チェックに合格してから次に進みます。
有効期間内のチェックに合格したら、同じ学習用アカウントの Dashboard で Storage & databases → Workers KV を開き、この実験の namespace を選びます。KV Pairs を開き、notice:maintenance の横の View をクリックして、メッセージと displayUntil を確認します。KV の有効期限とメタデータは CLI のキー一覧で確認してください。この画面には保存された値が表示されます。読み取りだけを行い、確認中も時間が進むことに注意してください。キーがすでに期限切れなら、画面を見るためだけに再作成せず、次のステップへ進みます。画像の名前とタイムスタンプは例であり、コピーしないでください。

非表示になったコンテンツと自動期限切れを観察する
このステップでは、メンテナンスキーを手動で削除せずに、2 つの期限を観察します。元のペイロードと結果を比較できるように、notice.json は変更しないでください。
予定している 2 つの時刻と現在時刻を表示します。
printf 'displayUntil=%s
KV expiration=%s
now=%s
' "$DISPLAY_UNTIL" "$KV_EXPIRES" "$(date +%s)"
現在時刻が displayUntil に達するまで待ちます。次のコマンドは、残りの待機時間だけを計算します。期限をすでに過ぎている場合、条件分岐によって待機をスキップします。sleep は秒数を受け取り、if は負の待機時間が渡されるのを防ぎます。
WAIT_SECONDS=$((DISPLAY_UNTIL - $(date +%s) + 1))
if [ "$WAIT_SECONDS" -gt 0 ]; then sleep "$WAIT_SECONDS"; fi
curl -i "$WORKER_URL/notice"
メッセージが表示されなくなっていることを確認します。KV の有効期限前であれば、{"visible":false,"reason":"expired"} が返ります。KV によってキーがすでに期限切れになった後に確認した場合、reason は missing になることがあります。どちらの場合も表示は防止されています。保持している参照レコードで、アプリケーションの表示期限の動作を直接確認できます。
curl -i "$WORKER_URL/notice?key=notice:reference"
npx wrangler kv key get notice:reference --binding NOTICES --remote --text
エンドポイントは参照レコードを expired として非表示にしますが、KV の読み取りでは古い JSON がまだ返ります。保存されているデータが利用可能な状態でも、アプリケーションの表示期限が役立つことが分かります。
次に、KV の有効期限まで待ちます。
WAIT_SECONDS=$((KV_EXPIRES - $(date +%s) + 1))
if [ "$WAIT_SECONDS" -gt 0 ]; then sleep "$WAIT_SECONDS"; fi
npx wrangler kv key list --binding NOTICES --remote
curl -i "$WORKER_URL/notice"
一覧には notice:reference だけが残り、デフォルトのエンドポイントは {"visible":false,"reason":"missing"} を返すはずです。メンテナンスキーに対して delete コマンドを実行しないでください。この観察では自動期限切れを確認します。エントリが残っている場合は、15 秒間隔で読み取り専用のチェックを最大 2 分間繰り返してください。これは実験で設定した観察時間であり、正確な削除時刻を保証するものではありません。収束しなかった場合は、成功したと主張せず、結果を「判定不能」として報告してください。認証エラーやネットワークエラーは、存在しないことの証明にはなりません。
KV の有効期限と読み取りキャッシュは別の概念です。より長い読み取りキャッシュ期間が要求されていても、有効期限は適用されます。ただし、保存された設定の変更が伝播するまでに時間がかかる場合があります。そのため、以前の読み取り後に書き込んだ新しい期限は、直ちに全体へ反映されるスケジュール保証ではありません。この実験では、ハンドラーが実際に読み取ったレコードに含まれる期限を確認します。
使い捨てのクラウドリソースを削除する
このステップでは、Wrangler の認証が有効な間に 2 つのリソースを削除します。namespace は Worker より長く残る可能性があるため、アプリケーションだけを削除しても、そのデータはクリーンアップされません。
このターミナルで起動したローカル開発プロセスを停止します。
kill "$DEV_PID"
削除する前に、保存したリソース参照を確認します。
cat wrangler.jsonc
labex-notices-... という Worker 名と、NOTICES namespace の ID を確認します。この設定で選択される Worker を削除します。
npx wrangler delete
確認を求められた場合は、表示された名前がこの実験のものと一致することを確認し、y を入力して確定します。続いて、NOTICES が参照している namespace だけを削除します。これにより、保持していた参照レコードも削除されます。
npx wrangler kv namespace delete --binding NOTICES
確認プロンプトが表示されたら、受け入れる前に namespace を確認してください。独立したチェックで削除対象のリソースが存在しないことを確認できるように、wrangler.jsonc はそのまま残します。
npx wrangler kv namespace list
この実験で作成した namespace が表示されず、関係のない namespace は残っているはずです。Dashboard の一覧を更新し、この実験の Worker と namespace が消えていることを確認します。リクエストの失敗やログインの期限切れは、削除の証明にはなりません。認証済みのインベントリを確認できるように、ログアウトする前にこのステップのチェックを実行してください。
VM の認証を終了する
このステップでは、クリーンアップのチェックに合格した後で Wrangler から切断します。ログアウトすると、この VM に保存された Wrangler の認証が終了します。クラウドリソースが削除されたり、通常の Dashboard のブラウザーセッションからログアウトしたりすることはありません。
npx wrangler logout
npx wrangler whoami --json
構造化された結果で "loggedIn": false と報告されることを確認します。この未認証のコマンドは、ここでは想定どおりゼロ以外の終了ステータスで終了する場合があります。接続エラーだけが表示され、認証状態が明示されない場合は、接続が回復してから再試行してください。
残っているローカルファイルとローカル KV の状態は、この使い捨て VM に属するものです。すでに削除したクラウドリソースとは別のものです。これで実験を終了できます。
まとめ
リクエストごとに表示期限を確認し、存在しないデータや不正なデータを安全に処理し、KV メタデータからカテゴリを読み取る通知リーダーを作成しました。保持した古いレコードによって、コンテンツを先に削除しなくても非表示にできることを確認しました。2 つ目のレコードでは、絶対時刻による KV の有効期限と、自動削除を確認する別のチェックを実証しました。
表示期限、ストレージの有効期限、読み取りキャッシュの動作を区別し、使い捨てのリソースを削除してログアウトしました。次は、KV に小規模なリダイレクトカタログをインポートして管理します。



