Apacheでメンテナンスモードを作る

  • Apache
  • HTTP

この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。

Apacheでメンテナンス画面を出す構成を作ったのでメモ。特定IP以外は全部503を返してメンテナンス画面に飛ばし、Retry-Afterヘッダーで再試行のタイミングも伝える、というよくあるやつ。

やりたいこと

  • 許可したIP(作業者用)以外のリクエストは全部503を返す
  • 503のときは/maintenance.htmlを表示する
  • /maintenance.html自身へのアクセスは除外する(無限ループ防止)
  • クライアントに再試行の目安としてRetry-Afterを返す

設定

ErrorDocument 503 /maintenance.html
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} !=/maintenance.html
RewriteCond %{REMOTE_ADDR} !=192.168.0.4
RewriteCond %{REMOTE_ADDR} !=192.168.0.5
RewriteRule ^.*$ - [R=503,L]
</IfModule>
<IfModule mod_headers.c>
Header always set Retry-After "3600"
</IfModule>

流れとしてはこう。

flowchart LR
  A[リクエスト] --> B{REQUEST_URIが maintenance.html?}
  B -- Yes --> H[通常通り maintenance.html を返す]
  B -- No --> C{REMOTE_ADDRが許可IP?}
  C -- Yes --> D[通常のコンテンツを返す]
  C -- No --> E[503を返す]
  E --> F[ErrorDocument -> maintenance.html]
  F --> G[Header always set で Retry-After を付与]

ポイント

Header setではなくHeader always setを使う mod_headersのHeader setはデフォルトで2xx/3xx系のレスポンスにしか適用されない。503のようなエラーレスポンスにも付けたい場合はalwaysが必須。これを忘れるとRetry-Afterが実際には送られない。

Retry-Afterは秒数指定が無難 日時指定(Sun, 14 Jun 2009 06:00:00 GMTのような形式)も仕様上は可能だが、フォーマットを間違えやすいし更新も面倒。復旧予定時刻が分かっていないなら、素直に秒数(3600など)で指定した方がミスが少ない。

/maintenance.htmlの除外を忘れない RewriteCond %{REQUEST_URI} !=/maintenance.htmlがないと、ErrorDocumentで表示しようとしたmaintenance.html自体もRewriteRuleに引っかかって無限に503を返し合うことになる。

付随リソースも対象になることに注意 maintenance.htmlがCSSや画像を外部ファイルとして読み込んでいる場合、そのリクエストは/maintenance.htmlではないので除外対象に入らず503になる。装飾なしのページになりがちなので、CSSはインラインにしておくのが安全。

プロキシ配下ではREMOTE_ADDRだけでは判定できないことがある CDNやロードバランサーを経由する構成では、REMOTE_ADDRがオリジンに届く直前のプロキシのIPになっていて、想定していたクライアントの実IPと一致しないことがある。その場合はX-Forwarded-Forを見る必要があるが、これは環境構成に依存するので個別に確認が要る。