タグ
#Cloudflare
Cloudflareに関する記事を公開しています。
Cloudflare D1 の無料枠を守るためだけに作らなかった 4 つの仕組み
D1 の読み取りを 1 日 2,110 万行(無料枠の 422%)から 190 万行前後まで下げる過程で、「次はこれを作ればさらに減る」という案が 4 つ出ました。公開時にキャッシュ済みの 404 を消すミドルウェア、404 ページを D1 を読まない静的ページにする案、リダイレクト表を Bulk Redirects へ同期する仕組み、スキャナをエッジで落とす WAF ルール。どれも半日で作れて効果も数字で見えていましたが、4 つとも作りませんでした。守る対象が今は圧迫されていないこと、持ち続ける費用が作る費用より高いこと、圧迫されたときの第一手は課金の方が確実なこと。判断の材料と、再訪の条件をまとめます。
Bot Fight Mode を無料プランで使うと GitHub Actions が 403 になる
Cloudflare の Bot Fight Mode を有効にしていた 2 サイトだけ、GitHub Actions からの死活チェックが 403 になりました。切って再実行すると全サイト 200。無料プランの Bot Fight Mode は Ruleset Engine の外で動くため WAF カスタムルールの Skip / Allow が効かず、例外を作るには Pro の Super Bot Fight Mode が要ります。監視・CLI・Webhook 受信・確認の curl など自分の非ブラウザ通信が全部判定の対象になる一方、守りたい Workers の起動枠(34%)も D1(30%)も圧迫されていなかったので、8 月に再検討して入れないと決めた経緯と、再訪の条件をまとめます。
Cloudflare Workers の Cron で曜日 0 は拒否され、7 は土曜になる
監視用の Worker に週 1 回の Cron を「30 23 * * 0」で入れたら、wrangler deploy がスクリプトのアップロードだけ成功して Cron の登録に失敗しました(invalid cron string)。0 を 7 に変えて登録は通りましたが、Cloudflare の曜日は 1 = 日曜〜7 = 土曜で、7 は土曜でした。公式ドキュメントの 1 行で気づき、SUN に直した経緯と、デプロイ後に schedules を確認する手順、D1 の読み取りを「1 起動あたりの行数」で 3 時間ごとに見張る Worker の設計をまとめます。
Astro の /_astro/ 画像が移行後に 404 で叩かれ続ける原因と対処
WordPress をヘッドレスに使った Astro の静的ビルドから EmDash へ作り直したあと、旧ビルドの /_astro/<名前>-1024x717_<ハッシュ>.jpeg に週 700 回以上の 404 が来ていました。User-Agent は普通のスマートフォンのブラウザで、画像検索からの着地でした。EmDash のリダイレクト表は拡張子付きのパスを引かないため 747 行入れても発火せず、ミドルウェアでファイル名からメディアを引いて 301 する形にして、週 1,640 件の 404 を 156 件に減らしました。残った 1 枚は同じ画像が別名で 2 回アップロードされていたもので、Redirect Rule 1 本で受けた経緯までまとめます。
Astro + Cloudflare で公開した記事が 404 のままになる原因
記事を公開したのに、記事の URL だけが「ページが見つかりません」のままでした。公開前にその URL を一度でも開いていると、404 のレスポンスがエッジに 30 日キャッシュされ、公開しても消えません。Astro の routeRules はリクエストのパスで照合してステータスを見ず、EmDash が公開時に失効させるタグ(コレクション名と記事 ID)は 404 には付いていないためです。purge の仕組みを足す案を作って取り下げ、404.astro で Astro.cache.set({ maxAge: 300, swr: 0 }) を呼んで 5 分に上書きするだけにした経緯と、swr を忘れると起きることを実測で書きます。
Cloudflare にデプロイしたのに反映されないのはブラウザのキャッシュ
デプロイ後にブラウザで開くと古いページのままで、ハードリロードすると新しくなる状態になりました。エッジは正しく更新されていて、原因はレスポンスに Cache-Control が 1 つも無かったことです。指示が無いとブラウザは Last-Modified から有効期限を推測するため、古い記事ほど長く保持されます。エッジ用のヘッダはエッジで取り除かれるので、ブラウザ向けの指示は別に出す必要がありました。
EmDash で記事にタグを付けても本番に反映されない原因
記事にタグを付け直しても、本番のタグ一覧に出てきませんでした。CMS がキャッシュを自動失効させているのはコンテンツの CRUD だけで、タグの割り当て・サイト設定・メニュー・ウィジェットエリアは別経路になっていました。エッジキャッシュの保持時間を 30 日に延ばしたことで顕在化した穴で、CMS 本体に patch を当てず、アプリ側のミドルウェア 1 か所で塞いだ記録です。
Cloudflare の Purge Everything で Workers Cache が消えない理由
ダッシュボードから Purge Everything を実行しても、ページが古いまま返ってきました。age が単調増加していることから消えていないと分かります。原因は消そうとしていたキャッシュがゾーンの CDN キャッシュではなく Workers Caching だったことで、ゾーンレベルの purge はこの層に届きません。あわせて、デプロイ時の purge がそもそも不要である理由も確かめました。
キャッシュを 30 日にしても D1 の読み取りが減らない原因
エッジキャッシュの保持時間を 5 分から 30 日へ延ばしても、データベースの読み取りが目標の 2 倍を超えたままでした。クエリ単位で分解すると 7 割がサイドバーの件数表示のための集計で、しかもサイドバーを消しても止まりませんでした。CMS のミドルウェアが全ページでターム一覧を先読みしていたためです。切り分けに使ったのは 1 起動あたりの読み取り行数と、クエリの実行回数がリクエスト数の何倍かという比率でした。
Cloudflare D1 の読み取りが 1 日 2,100 万行になった原因
1 つの Cloudflare アカウントで CMS のサイトを 8 つ運用していて、データベースの読み取りが 1 日 2,100 万行ありました。使用量は返した行数ではなくスキャンした行数で数えられるため、1 ページ表示するたびに数千行が乗っていたのが原因です。CMS を使っていてクエリを自分で書いていない状況で、キャッシュの保持時間とテンプレートの見直しだけで 91% 減らした記録です。1 リクエストあたりの単価が、1 アカウントに載せられるサイト数の上限を決めていました。
WordPress 移行後に本文に残った旧 URL を Portable Text から数える
WordPress から移行したあとに残る旧画像 URL を片付けようとして、公開 HTML から数えた 21 本とデータベースから数えた 58 本が食い違いました。差の正体は表示されないリンク注釈と、生 HTML ブロックの中の書き換え漏れでした。ついでに 5 記事で画像が 404 になっていたことも見つかりました。
Astro の routeRules から外してもページがキャッシュされる
エッジキャッシュから問い合わせページを外そうとして routeRules に書かずにデプロイしたら、本番では HIT が返ってきました。キャッシュの有効判定は maxAge かタグのどちらかで立ち、CMS のキャッシュヒントはタグだけを持つため、保持時間の指定が無いままキャッシュされます。外すには routeRules ではなくページ側の Astro.cache.set(false) が要ります。
Cloudflare で purge しても age が伸び続けるのは壊れていない
記事を更新してもページが変わらず、age が単調増加する。ここから「失効が壊れている」と結論しましたが、間違いでした。purge はキャッシュを消すだけで、再描画は次のアクセス時です。curl -I での確認はキャッシュから返るためサーバーに届きません。4 回続けて誤診した過程と、キャッシュのデバッグで守るべきことをまとめます。
Cloudflare で HTML がキャッシュされないのは既定の仕様
「Cloudflare を通しているからキャッシュは効いている」は静的ファイルの話でした。HTML には cf-cache-status ヘッダすら付きません。キャッシュミスではなく、判定の対象になっていない状態です。既定が拡張子ベースである理由と、Astro のサーバーレンダリングでエッジキャッシュを効かせる方法を実測とあわせて書きます。
Cloudflare Workers 無料プランで 503 が出る原因は CPU 上限
毎分 10〜31 リクエストという低負荷で 4 回に 1 回が 503。同じサイトが 1 時間 2,516 リクエストをエラーゼロで捌いた実績もあり、負荷では説明が付きません。原因は無料プランの CPU 上限 10ms でしたが、これは「1 リクエストで超えたら落ちる」という単純な効き方をしません。実測ログとあわせて機構を整理します。
Cloudflare Workers 無料プランの 3 MB 上限に数えられるもの
Cloudflare Workers の無料プランには gzip 後 3 MB というバンドルサイズ上限があります。実運用中のサイトの実測値は約 2.1 MB で、残る余裕は 900 KB ほど。画像生成ライブラリを 1 つ足すだけで +680 KB 増えて天井に届く一方、11 MB ある静的アセットは 1 バイトも数えられません。何が数えられて何が数えられないのかを実測値で整理します。
Astro.rewrite("/404") が 200 を返してソフト 404 になる
Astro SSR の動的ルートでエントリ未検出時に Astro.redirect("/404") すると 302、Astro.rewrite("/404") にしても HTTP ステータスは 200 になりソフト404になります。Astro が rewrite でステータスを 200 にリセットする機構(astro@7.0.7 のソース)と、404.astro 側で Astro.response.status = 404 を立てて正しく 404 を返す方法をまとめます。
WordPress 移行後に記事内の画像が 404 になるときの対処
WordPress をヘッドレス CMS へ移行したあと、本文が旧 /wp-content/uploads/ を参照したままで記事内の画像が 404 になる問題。404 の 9 割超は実体が移行済みで URL だけが古いので、本文を書き換えずキャッチオール動的ルート 1 本でファイル名からメディアを引いて 301 する設計と、WordPress 命名(-WxH サムネイル・-e 編集版)や .gitignore の落とし穴をまとめます。
D1 の無駄なクエリを削ったらクエリ数が増えた原因
エッジ SSR(Cloudflare Workers + D1)ではレイテンシの支配項は CPU ではなく DB への往復回数です。明らかに無駄なクエリを削ったら本数が 19→22 に増えた——その裏にあるリクエスト内キャッシュの重複排除という機構と、推測で書き換えず「表示が変わっていないこと」を HTML のバイト一致で証明しながら最適化するウォーム A/B 計測の手順をまとめます。
Google Analytics を入れずに Cloudflare でブログを監視する
ブログ運用の一次監視に欲しいのは「配信が壊れていないか」で、それは GA4(gtag.js)を入れなくても取れます。GA4 のクライアント計測とは別レイヤーの Cloudflare エッジ計測(GraphQL Analytics API の httpRequestsAdaptiveGroups)を GitHub Actions で回し、死活チェック・週次レポート・Issue 自動起票までを組む監視設計を、GA との役割分担つきでまとめます。