

Apacheでメンテナンスモードを作る
この記事は、構成と文章の整理に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を見る必要があるが、これは環境構成に依存するので個別に確認が要る。