Last updated on

Basic認証のステージングでWordPressの自動更新が止まっていた話——前提のcronから理解する

  • WordPress
  • 運用
  • セキュリティ
  • Basic認証
  • cron
  • WP-CLI

検証用のWordPressサイトに、クローラや部外者から見えないように HTTP Basic認証(ブラウザがユーザー名とパスワードを聞いてくるあのダイアログ)を載せることがある。

よしあきの環境でも、テスト用URLの手前にBasicを置いている。ところが、管理画面では「自動更新」をONにしているのに、セキュリティリリースが出てもコアの版が上がらないことがあった。プラグインも同じだ。

「BasicがWordPress.orgへの通信を止めている」と思いがちだが、本線はそこではない。止まっていたのは、更新処理が乗る予定の cron(クロン) だった。しかも、Basicを直しても サイトに誰も来なければ、既定のWP-Cronはそもそも起きにくい。問題は一枚岩ではない。

この記事の目的は次のとおりだ。

  1. Basic認証が付いていると、自動更新が静かに死にうると理解すること
  2. その背景の cron/WP-Cron を、よしあき自身がまだ分かっていなかった前提から掴むこと
  3. アクセスが少ない検証環境で、何を直すと・何を運用で補うかを切り分けること
  4. 管理画面では任意のWordPressバージョンを指定できないこと、WP-CLIの前提と使えないときの代替を押さえること

何が起きたか(現象だけ先に)

  • WordPress本体・プラグインの 自動更新は有効 にしてある
  • サイトには サイト全体のBasic認証 がある
  • それでも、しばらく版が古いまま残る

手動で「更新」ボタンを押せば、管理画面に提示されている版へは上がる。少なくとも、更新ファイルの取得と適用そのものは可能だった。そこで、**自動実行側の経路(現在の系列向けのマイナーリリースを含む)**を疑った。

ただし、Basic認証は自動更新が止まる原因のひとつにすぎない。DISABLE_WP_CRONAUTOMATIC_UPDATER_DISABLED、自動更新を制御するフィルター、ファイル権限、loopback/SSLのエラー、ホスティング側の制御などでも止まる。まず 「ツール → サイトヘルス」 で、予定イベントやバックグラウンド更新の状態を確認したい。

止まって見える要因はふたつある

よしあきが後から整理すると、検証環境ではだいたい次の二層が重なる。

内容結果
A. Basicサイト全体Basicのとき、WordPressが自分宛てに飛ばす wp-cron.php401 になりやすい訪問があっても、裏の発火が落ちる
B. アクセス不足既定のWP-Cronは HTTPアクセスのついで に動く擬似スケジュールほぼ無人のテストURLでは、予定タスクが溜まったまま

Aだけ直してもBが残る。Bだけ頑張ってもAが残ると、開いたつもりでも自動更新が進まない。両方を見る必要がある。

cronとは何か(ここが本丸)

ざっくり言うと

cron は、「決めた時刻・決めた間隔で、決めた処理を自動で走らせる仕組み」の総称だ。

サーバー管理の文脈では、Linuxの cron デーモン(crontab に書いたジョブ)を指すことが多い。WordPressの文脈では、それと似た役割を WP-Cron が担う。

よしあきが混乱していたのは、「cron=何か特別なサーバー機能」とだけ覚えていて、WordPressの自動更新とどうつながるかが見えていなかったことだった。

なぜ自動更新にcronが要るのか

自動更新は、管理者が画面を開いているときだけ動けばよい、というものではない。むしろ 誰も見ていないあいだに、次をやりたい。

  1. 「新しい版があるか」を確認する
  2. 条件が揃えば、裏でダウンロードして適用する

この「あとでやる」「定期的にやる」を引き受けるのがスケジュール機構で、WordPressではそれが WP-Cron になる。

コアもプラグインもテーマも、バックグラウンドの自動更新はだいたい 同じWP-Cronの上 に載る。だからBasicでcronが死ぬと、コアだけ・プラグインだけ、ではなくまとめて止まる

WP-Cronのクセ(本物のサーバーcronとの違い)

本物のサーバーcronは、だいたい次の形だ。

  • OSが時刻を見てジョブを起動する
  • サイトにアクセスがあるかどうかは関係ない

一方、既定のWP-Cron は少し違う。

  • だれかがサイトにHTTPリクエストしたタイミングで、「溜まっている予定タスクはないか」を確認し、あれば実行する
  • 実装の都合で、WordPressはしばしば 自分自身にHTTPリクエスト(loopback) を飛ばして wp-cron.php を叩く

つまりWP-Cronは、「時計が絶対に起こしてくれる」というより、アクセスのついでに起きる擬似スケジュールに近い。アクセスが極端に少ない検証サイトでは、それだけでも更新が遅れやすい。

ここに サイト全体のBasic認証 が重なると、次が起きる。

Basic認証がWP-Cronを止める仕組み

流れを単純化するとこうなる。

  1. WordPressが「cronを動かしたい」と考える
  2. 自分のサイトの wp-cron.php へHTTPでアクセスしようとする
  3. サーバー前面のBasicが 401(認証が必要) を返す
  4. WordPress側のリクエストには、ブラウザが覚えたBasic資格情報が付いていないことが多い
  5. cronが失敗する → 予定されていた自動更新も走らない

ポイントは、Basicが「wordpress.orgへの外向き通信」を直接遮断しているわけではないことだ。外向きの更新チェック用APIは通っていても、自分宛ての発火が401で落ちれば、自動更新の連鎖は始まらない

ブラウザで管理画面にログインして手動更新するときは、人間が認証済みの文脈で操作しているので成功する。だから「手動はできるのに自動だけ死ぬ」という症状になる。

アクセスが無い検証環境という前提

テスト環境は、限られた人しか見ない。月に一度しか触れないことも珍しくない。

そのとき、「Basicを外したから自動更新は回るはず」と思うとずれる。既定のWP-Cronのままなら、訪問がゼロのあいだは発火しない(または極端に遅れやすい)。

逆に言うと、すでに 月次でテスト環境を棚卸ししているなら、「その月次の最初に、残っているWordPressのURLをひと通り踏む」ことで、B層(アクセス不足)を補う機会を作れる。サーバーにサイトごとの本物cronを全部載せるより、実装コストは小さい。

ただし、URLを1回開けば、予定タスクや自動更新が必ずその場で完了するわけではない。あくまでWP-Cronが発火できる機会を作る運用だ。

ただし、開いた瞬間の画面がまだ古くても不思議ではない。訪問がきっかけで裏の処理が動き、更新チェックや適用は数秒〜しばらく遅れて進むことがある。月次の締めとして見るなら、

  1. 先にURLを一巡する
  2. 削除や棚卸しなど、ほかの作業をする
  3. あとで管理画面の「更新」を見る。残っていればその場で手動

「開いた瞬間に更新済みであること」を期待しない方がよい。そこで焦って「自動が死んでる、全部手動だ」と判断すると、待ち時間で済む話まで手作業に寄せてしまう。

一方、A層(Basicの401)が残っていると、一巡しても loopback が落ち、待っても自動では上がらないことがある。そのときは手動が正しい。

どう対処するか(方針の地図)

よしあきが整理した対応は、優先度つきで次のとおりだ。

1. 推奨:wp-cron.php だけBasicを外す

ページ本文や管理画面の入口はBasicのまま、cron用の入口だけ認証なしで通す

検証環境なら、wp-cron.php を公開したままにするリスクと、自動更新を止めるリスクを比較して判断する。本文や管理画面は見えないが、外部からcronを繰り返し呼ばれて負荷につながる可能性はある。許容できる環境では、訪問や月次一巡のあと、loopbackが401で死ななくなる。A層の本線はここだ。

レンタルサーバーの管理パネルのBasicは、パス単位の除外ができないことが多い。だからよしあきの環境では、サブドメイン全体をパネルで閉じるのではなく、サイトのドキュメントルートの .htaccess でBasicをかける。次の例は、WordPressがドキュメントルート直下にあり、Apache 2.4で mod_setenvifmod_authz_core が使える前提だ。サブディレクトリにWordPressがある場合は、Request_URI の条件を設置先に合わせる。

流れは次のとおりだ。

  1. リクエストURIが /wp-cron.php なら、環境変数(例: wp_cron_ok)を立てる(SetEnvIf
  2. 認可は <RequireAny> で、「その環境変数がある」または「正しいBasicユーザー」なら通す
  3. トップやログイン画面は未認証なら401のまま。wp-cron.php だけ未認証でも通る
# BEGIN STAGING BASIC
SetEnvIf Request_URI "^/wp-cron\.php" wp_cron_ok
AuthType Basic
AuthName "Staging"
AuthUserFile /ABS/PATH/TO/.htpasswd
<RequireAny>
  Require env wp_cron_ok
  Require valid-user
</RequireAny>
# END STAGING BASIC

AuthUserFile はサーバー上の .htpasswd絶対パスにする。BEGIN / END のコメントは、WordPress本体やプラグインが書くルールと混ぜないための囲いだ。このブロックの外(パーマリンクルールなど)は触らない。

パネルのBasicと .htaccess のBasicは混ぜない。 パネルが残っていると、そちらが先に401を返すので、.htaccess の除外は効かない。パネル側を外してから、.htaccess 側だけを正とする。

確認の目安は、未認証のまま次を叩くことだ。

  • //wp-login.php401
  • /wp-cron.php401以外(多くの場合は200)

全部401のままなら、パネルBasicが残っていないかを疑う。

よしあきの環境では、WordPressの検証サイトではこのブロックを既定にした。

2. 運用で補う:月次でURLを一巡する

すでに月次メンテがあるなら、残置しているWordPressのトップURLを、作業の最初にひと通り踏む。下層ページまで全部開く必要はない。サイトごとにトップを開き、WP-Cronが発火できる機会を作る。

よしあきの環境では、レンタルサーバーの管理APIで 簡単インストール済みのWordPress一覧(インストール先URL)が取れる。それを並べてブラウザで開く、またはHTTPで順に叩く短い手元スクリプトを用意した。一覧の正本を手メンテしなくてよいのが利点だ。

注意点はふたつある。

  • APIに載るのは、だいたい 簡単インストール経由のWordPress に限られる(手置きだけの構成は漏れることがある)
  • サイト全体Basicのままなら、トップを叩いただけでは足りず、認証付きで wp-cron.php を直接叩くか、上記1の除外が要る

3. 代替:サーバー側の本物のcronで叩く

WordPress側の擬似cronを止め(DISABLE_WP_CRON)、サーバーのcronから Basic付きで wp-cron.php を定期的に叩く方法もある。訪問ゼロでも回る。

対象となる WordPressごとにURLや実行先の登録は必要になる。ひとつのスクリプトで複数サイトを順番に処理する構成にもできるが、サイトが増えるほど登録・監視・削除の管理コストは積み上がる。残置が少ないうちは、月次一巡の方がコスパが良いことが多い。

4. 当面:手動更新で穴を埋める

自動が死んでいると分かっているあいだは、セキュリティリリースが出たら管理画面から上げる。根本対策ではないが、放置よりはましだ。月次で「更新」画面を見る運用と相性がよい。

ただし、通常の管理画面では 任意のWordPressバージョンを指定できない。表示される候補は、WordPress.orgの更新API、サイトの言語、ホスティング会社や更新制御プラグインなどの影響を受ける。1の除外と月次一巡が回っていれば、現在の系列向けのマイナーリリースは自動更新に任せ、手動は「待っても上がらないとき」か「管理画面に提示された版へ上げてよいとき」の穴埋めになる。

管理画面では任意の版を指定できない

WordPressでは、6.6.6 → 6.6.7のような保守・セキュリティ更新を マイナーリリース、6.6.x → 7.0.xのような系列を越える更新を メジャーリリース として扱う。後者は、テーマやプラグインの互換性を含めて別に判断したい。

管理画面に表示される更新候補は環境によって異なるが、任意のバージョン番号を入力して選ぶことはできない。特定のマイナーリリースへ更新したいなら、WP-CLI などで版を指定する。

wp core update --version=6.6.7

WP-CLI の前提

WP-CLI は、「その WordPress のファイルと DB に、シェルから届くこと」が前提だ。最低限は次の3つ。

  1. wp コマンドが動くシェル(PHP の CLI と WP-CLI 本体)
  2. 対象サイトのドキュメントルートで実行できることwp-config.php が見える場所)
  3. その PHP から DB に繋がること

リモートなら、これに加えて SSH(または同等のシェル) が要る。FTP だけでは wp は走らない。

よしあきの環境では、だいたい次のように分かれる。

場所WP-CLI
手元のローカル(Local など)使える。同梱の WP-CLI でドキュメントルートから実行する
共用レンタルのテスト/本番使わない。SSH なし・FTP が正

リモートでマイナーだけ当てたいからといって、WP-CLI 用に SSH を新しく用意する必要はない。使えない前提で、次の代替を選ぶ。

WP-CLI が使えないとき

1. 自動更新に任せる(本線)

マイナーはもともと自動更新の仕事だ。止まっているなら、先に A層(wp-cron.php の 401)を直す。直したあと URL を一巡して待つ。

2. 指定版のZIPを使い、公式手順で手動更新する

指定版のZIPを使った手動更新もできる。ただし、コアファイルを単純に上書きするだけでは、更新後には不要な古いファイルが残る可能性がある。

事前にファイルとDBをバックアップし、WordPress公式の手動更新手順に従う。基本は、既存の wp-adminwp-includes を入れ替え、ルート直下のコアファイルを上書きする流れになる。既存の wp-contentwp-config.php.htaccess、Basic認証用の .htpasswd は削除しない。更新後は必要に応じてデータベース更新を実行し、表示と管理画面を確認する。

3. 管理画面に提示された版へ更新する

管理画面に7.0.4のような新しい系列だけが提示されている場合、それを押すとメジャーリリースへの更新になる。テーマ/プラグインの互換性は別に判断する。

4. 急がなくてよいなら据え置き

穴の前提が「権限のあるログイン済みユーザー」なら、検証環境のアカウントが少なくパスワードも十分複雑なら、一旦このままでよいことがある。メジャー越えを急ぐ理由にはならない。

理解のための短い対話

記事化の前後で、よしあきが実際に投げた疑問に近い形で残す。

よしあき: 本来なら自動更新されるはずなのに、Basic認証が邪魔して更新されていない様子。どうにかならないかな。

AI(助手): 更新機能そのものより、裏で動くはずのWP-Cronの発火を疑うのが近道です。サイト全体Basicだと、自分宛ての wp-cron.php が401になりやすいです。

よしあき: 背景にcronがある、と言われるが、cron自体がいまいち分かっていない。

AI(助手): 「あとで・定期的にやる」ための仕組みの総称です。WordPress既定のWP-Cronは、サーバーの時計任せというより、サイトへのアクセスのついでに動く擬似スケジュールに近いです。

よしあき: Basic認証が付いていると自動更新が機能しない。さっきの対応方針——wp-cron.php だけBasicを外す、またはサーバーcronで叩く——は、プラグインの自動更新にも効く?

AI(助手): 効きます。コアもプラグインも、バックグラウンド更新はだいたい同じWP-Cronの上に乗ります。

よしあき: テスト環境は限られた人しかアクセスしない。そもそもアクセスがなければ、今回の対応をしても自動更新は機能しない?

AI(助手): 既定のWP-Cronだけなら、その理解でだいたい合っています。Basic対応は「来たときに発火できる土台」の話で、無人の定期実行までは保証しません。

よしあき: サーバー側の本物のcronは、サブドメインごとに実装が必要?実装コストは重い?

AI(助手): サイトごとに実行先の登録が必要です。ひとつのスクリプトにまとめることはできますが、対象の追加・監視・削除は必要なので、残置が少ないうちは月次でURLを踏む方が安いことが多いです。

よしあき: 月1でテスト環境をメンテしているから、URLを開くのはコストなしでできそう。ただ、開いた時点ではまだcronが走っておらず未更新に見えて、「手動でやらねば」と判断しそう。

AI(助手): 開いた瞬間は古く見えて普通です。一巡のあと他作業をしてから更新画面を見る、が誤判断を減らせます。Basicの401が残っていると、待っても自動では上がらないこともあります。

よしあき: まとめて確認するなら、最初に全ページへアクセスする工程を足す?月1アクセスを自動化できない?レンタルサーバーのAPIで、WordPressが入っているURL一覧は取れない?

AI(助手): 全ページは不要です。サイトごとにトップを開き、WP-Cronが発火できる機会を作ります。管理APIのWordPress一覧からURLを取り、月次の最初に開く/叩く運用にできます。ただし、その1回で更新完了まで保証されるわけではありません。

よしあき: WordPressのマイナーアップデートは、管理画面からはできないよね。その場合どう対応したらいい。

AI(助手): 管理画面では任意のバージョン番号を指定できません。表示される候補は環境によって異なります。現在の系列向けのマイナーリリースは自動更新に任せ、特定の版を指定したいならWP-CLIなどを使います。

よしあき: WP-CLI を使うにはどんな前提が必要。使えない場合には、どんな対応方法がある。

AI(助手): シェルからそのサイトのファイルと DB に届くこと、リモートなら SSH が要ります。FTP だけでは走りません。使えないなら、自動更新を直すか、指定版ZIPを使って公式手順で手動更新するか、管理画面に提示された版へ更新するか、急がなければ据え置きです。

いまのところの結論

  • Basic認証付きステージングでは、WordPress/プラグインの自動更新が静かに死ぬことがある(自分宛ての wp-cron.php が401)
  • それに加え、アクセスが少ない検証サイトでは、既定のWP-Cron自体が起きにくい
  • cronは「あとで・定期的にやる」ための仕組み。自動更新はその上に乗っている
  • 構造の本線は .htaccesswp-cron.php だけBasicから外すこと(パネルBasicは使わない)。訪問ゼロ対策は 月次のURL一巡(またはサーバーcron)で補える
  • 月次で開いた直後に未更新でも焦らない。一巡→他作業→更新画面、の順が現実的
  • 管理画面では任意のバージョンを指定できない。 現在の系列向けのマイナーリリースは自動更新(またはWP-CLIで版指定)。リモートにSSHが無ければ、公式手順による手動更新、管理画面に提示された版への更新、据え置きから選ぶ

「自動更新をONにしたから安心」は、Basicの手前でも、無人の検証URLでも、そのままでは成立しない。よしあきは、ステージングを閉じるときに cronの出口(Basic除外) と、月に一度は発火の機会を作る運用をセットで考えることにした。マイナーリリースが止まって見えても、管理画面に提示された更新を押す前に、現在の系列向けの更新を誰が入れるかを切り分ける。

参考資料