# PHP の緊急脆弱性で Zenlogic が止まる前に、WordPress を静的化して Cloudflare Workers で配信した

> @dotbind-kondo · published 2026-10-22T02:33:49Z · #cloudflare #php #wordpress #workers #zenlogic

## trail

- **前提** Simply Static で静的化して Cloudflare に退避する案から始まった
- **却下** Simply Static — 脆弱な PHP 上で新しいプラグインを動かすことになる。外から wget で取れば足りる
- **観察** 最初のミラーのカバー率は、アクセス上位のリクエスト数で見て 63%。/old/ などの WP 外の静的ディレクトリが抜けていた
- **決定** GraphQL の上位 5,000 パスを基準に、欠けたものを追加取得して約 86% にした
- **却下** 25MiB 超を R2 で配信する案 — アクセス上位優先なら後回しでよく、リソースも増える
- **観察** Redirection の正規表現は先頭一致でなく、本番でも /old/old/... と転送が続いていた
- **決定** 転送ルールは _redirects ではなく Worker で再現した（正規表現が要るため）。^ を付けてループを防いだ
- **観察** www.example/* が *.example/.../event* に勝ち、特設ページがお知らせページになった
- **決定** www を明示した特設ページのルートを追加して解消した（ゾーンのルート変更はユーザーが手で実施）

## 背景

2026-10-21、PHP 本体にログイン不要の RCE が 2 件見つかった（CVE-2026-109618 / 109619、CVSS 9.2、通称 php2shell）。mod_php・PHP-FPM のどちらでも、WordPress を含む構成で再現するとされ、Session Upload Progress（ext/session）が関係するとみられている。`session.upload_progress.enabled=0` では防げないと追記されていた。修正版は日本時間 23:00 に公開予定だった。

これを受けて、Zenlogic ホスティングが緊急メンテナンスを告知した。

- 10-21 22:00〜10-22 18:00（予定）に Apache を停止する（Web にアクセスできない）。
- PHP 8.1 は、パッチの状況によっては最新版に上げる可能性がある。

学会サイト（クライアントA）の旧サイトは、この Zenlogic 上の WordPress（6.2 系・PHP 8.1）で、前段に Cloudflare がある。ゾーン（ドメイン）のプランは Free、Workers は Paid プラン。何もしなければ、約 20 時間サイト全体がエラー画面になる。そこで、止まる前に静的コピーを取り、Cloudflare から配信することにした。

当初の相談は「Simply Static で静的化して Cloudflare に退避するのはどうか」だった。

## 調査

- **規模**：サイトマップは 214 個のサブサイトマップに分かれていて、合計 1,266 URL（うちお知らせが 1,068）。静的化できる量だった。
- **振り分け**：既に一部のパス（特設ページ・ニュースメールのバックナンバー）は、新サイトの Worker にルートで振り分けられていた。それ以外は全部オリジンに届いていた。
- **動的機能**：
  - サイト内検索は Google カスタム検索で、静的化しても動く。
  - 決済フォームは外部サービスへの POST なので、これも動く。
  - 止まるのは、施設名称検索（WP の `?s=`）・用語集の検索（POST）・英語版の問い合わせフォーム。
  - アクセス上位の名簿 2 ページは、全件（約 1.8 万行と 9 千行）がページ内の表になっていて、絞り込みは JS だった。静的化しても使える。
- **リダイレクト**：WP の Redirection プラグインに 155 件のルールがあった（REST で取得）。
  - アクセスが多いのは、`/guideline/pdf/*.pdf` などを、昔の静的サイトのディレクトリ `/old/` へ転送する正規表現ルール。
  - これらの正規表現は先頭一致になっていない。そのため存在しない PDF は、本番でも `/old/old/old/…` と転送が続き、元から開けなくなっていた。
- **アクセス上位**：Cloudflare の GraphQL（httpRequestsAdaptiveGroups）で直近 23 時間の上位 5,000 パス（約 46.5 万リクエスト）を取り、写しのカバー率をリクエスト数で重み付けして測った。
  - 最初のミラーだけでは 63% だった。WP の外にある静的ディレクトリ（`/old/`・`/share/`・`/sikkanpg/` など）が抜けていたためで、追加取得して 71% になった。
  - これに既存ルートの範囲 15% を足すと約 86%。残りは、本番でも 404 になるものや wp-admin・wp-cron など、静的版では要らないものだった。
- **wget の保存名**：
  - クエリ付きの CSS・JS は `?` が `@` に置き換えられて保存される（`style.css@ver=6.2`）。そのため、ファイル名を戻す処理が要る。
  - 本当に `@` を含むファイル名（`img＠sm.png` のような画像。＠は実際は半角）を取り違えたので、「`@` のあとに `=` があるときだけクエリとみなす」に直した。
  - 日本語の URL は UTF-8 のファイル名で保存され、Workers の静的アセットでそのまま配信できた。
- **ファイルサイズ**：Workers の静的アセットは 1 ファイル 25MiB まで。超えたのは 9 ファイル（動画 4 本で計約 5GB、PDF 数個）。ファイル数の上限はプランによって違うが、Workers が Paid プランなので、約 6,300 ファイル・2.5GB でも問題にならなかった。

## 判断

- **Simply Static は使わず、外から wget でミラーした。** 脆弱な PHP 上で新しいプラグインを動かさずに済み、サーバーに手を入れずに速く取れる。
- **配信は Worker 1 本にした**（静的アセット＋未一致時の処理）。
  - `html_handling: auto-trailing-slash`。
  - アセットに無い URL では、Redirection の有効ルールを Worker で再現する。固定 URL は大文字小文字と末尾スラッシュを区別しない。正規表現はループしないよう `^` を付け、行き先が壊れていたものは `$1` に直した。
  - それでも無ければ、メンテナンスのお知らせを 404 で返す。GET・HEAD 以外は 503 にして、PHP に届かないようにした。
- **R2 は使わなかった。** 25MiB を超えるファイルは、アクセス上位を優先する方針で今回は配信しない。
- **ルートの切り替え**：`www/*` と apex の `/*` をこの Worker に付けた。ゾーンのルート操作だけで、数秒で切り替えられ、戻すのも 2 行を削除するだけ。
- **落とし穴（Workers ルートの優先順位）**：Workers ルートはパスの長さより先に、ホスト名の具体さで優先される。`www.example/*` が、既存の `*.example/…/event*` に勝ってしまい、特設ページがお知らせページになった。www を明示したルートを新サイトの Worker 側に追加して直した。バックナンバーは www 版のルートもあったので無事だった。
- **プレビュー**：workers.dev の URL はデプロイ時は無効にしていた。確認のために一時的に有効にし、切り替え後に `workers_dev: false` と `preview_urls: false` で閉じた。手で付けたゾーンのルートは、`wrangler deploy`（設定にルートが無い状態）では消えなかった。
- **お知らせページ**：クライアント側が用意した HTML を元に、「実施予定日時」の見出し、終了時刻が前後しうる旨の注記、トップページへのボタン、noindex を足した。
- **今後の扱い**：停止中は PHP が動かないので、静的化は表示を続けるための対策。セキュリティ上の効果は、停止前の約 1 時間、外から PHP を隠せたことぐらい。

## Next Action

- メンテ明け（10-22 18:00 以降）の作業：
  1. Zenlogic のコントロールパネル（WEB → PHP 設定）で PHP のバージョンを確認する。8.1 はサポートが終わっているので、上がっている可能性が高い。
  2. 静的版のまま、オリジンに直接つないで WP 6.2 と古いプラグインが動くかを確認する。
  3. 問題がなければ、ルート 2 本を削除して WP に戻す。www を明示した特設ページのルートは残してよい。
- PHP の更新で WP が壊れていたら、静的版のまま修正する（別見積もり）。
- apex は静的アセットが先に応答するため、www にリダイレクトされない（未対応）。必要なら Worker 側で直す。
- Worker と前処理スクリプトは残しておく。次にオリジンが止まるときも、ルートを付けるだけで再利用できる。写しは取り直しが必要。

※ クライアント名・ドメイン・Worker 名・特設ページ名・Cloudflare のアカウント ID・workers.dev のサブドメイン・費用の見積もりは省略した。パスは相対パスにした。
