ApacheをつかっているWEBサイトで、メンテナンス開始時間になったら自動的にメンテナンスページに切り替わるようにする設定を紹介します。
Apacheの設定サンプル
早速Apacheの設定サンプルを紹介します。
RewriteEngine on RewriteCond %{TIME} >=20240901020000 RewriteCond %{REMOTE_ADDR} !^127\.0\.0\.1 RewriteCond %{REMOTE_ADDR} !^192\.168\.0\. RewriteCond %{REQUEST_URI} !^/error/.* [NC] RewriteCond %{REQUEST_URI} !=/favicon.ico RewriteRule ^ - [R=503,L,E=MAINTENANCE:yes,E=ERROR_DOCUMENT:error/maintenance.html] RewriteCond %{ENV:MAINTENANCE} !^yes$ RewriteRule ^ - [E=ERROR_DOCUMENT:error/503.html] ErrorDocument 503 /%{ENV:ERROR_DOCUMENT} Header always set Retry-After "Sat, 31 Aug 2024 19:00:00 GMT" env=REDIRECT_MAINTENANCE
この設定では次のようなメンテナンスページの切替を行うことができます。
%{TIME}
以降のリクエストに503 service temporarily unavailable
をレスポンスする- メンテナンスページは
/error/maintenance.html
を表示する Retry-After
ヘッダーをレスポンスに付与する- 除外設定
- 接続元IPアドレス
- URLパス
- メンテナンスより前の
503 service temporarily unavailable
に対するエラーページは/error/503.html
を表示する
動作確認環境
- OS: AlmaLinux 9.4
- Apache: Apache/2.4.57 (httpd-2.4.57-11.el9_4.1.x86_64)
メンテナンスページのお作法
メンテナンスページ表示の目的は、メンテナンス中にアプリケーションにリクエストが流入することを防ぐことや、
ユーザーにメンテナンス中であることを分かりやすく伝えることが挙げられます。
ただし、メンテナンスページを表示させるために、ApacheでURLリライトして表示を替えるだけでは、不十分とされています。
ユーザーから見えるブラウザ上の見た目としては同じですが、Webサーチエンジンのクローラーからは、
対象のURLパスが更新されたと判断され、メンテナンスページが検索インデックスに登録されてしまうからです。
検索クローラーのために、以下の2つの設定でメンテナンスページを表示することが推奨されています。
- HTTPステータスコードとして
503 service temporarily unavailable
を返す Retry-After
ヘッダーを利用することで、あらかじめ分かっているダウンタイムの時間やサイトの復旧日時について指定する
サイトのダウン タイムへの対処の仕方 | Google 検索セントラル ブログ | Google for Developers
メンテナンスページを表示する条件設定の解説
メンテナンスページを503 service temporarily unavailable
でレスポンスするための設定は次の部分です。
RewriteCond %{TIME} >=20240901020000 RewriteCond %{REMOTE_ADDR} !^127\.0\.0\.1 RewriteCond %{REMOTE_ADDR} !^192\.168\.0\. RewriteCond %{REQUEST_URI} !^/error/.* [NC] RewriteCond %{REQUEST_URI} !=/favicon.ico RewriteRule ^ - [R=503,L,E=MAINTENANCE:yes,E=ERROR_DOCUMENT:error/maintenance.html]
RewriteCond
の条件を満たすリクエストをRewriteRule
で強制的にHTTPステータスコード503にしています。
%{TIME} >=20240901020000
はメンテナンス開始時間の条件です。
TIME
はRewriteCond
で利用可能なサーバー変数で、メンテナンス開始時刻を指定します。
指定する時刻はサーバーのタイムゾーンになりますのでご注意ください。
TIME
の他にも、曜日を表すTIME_WDAY
などがあるので、曜日指定の定期メンテナンスも表現可能です。
Variables | Expressions in Apache HTTP Server - Apache HTTP Server Version 2.4
その他の条件として、REMOTE_ADDR
/REQUEST_URI
を使ってメンテナンス除外リクエストを表現しています。
除外を検討する必要がある主なリクエストは以下のようなものがあります。
- メンテナンスページ自体へのリクエスト
- メンテナンスページ設定について詳しくは後述しますが、URLリライトで表示するページへのリクエストは内部リクエストとなるため、接続元IPアドレスがローカルIPアドレスになることに注意してください
- メンテナンスページを構成する画像やCSS、JSへのリクエスト
- メンテナンスページのHTMLから呼び出されるリクエストも503の対象にすると、ページの表示が崩れてしまいます
- 例ではメンテナンスページ関連のパーツはすべて
/error/
に配置されている想定になっています
- 監視システムやLBからのヘルスチェックリクエスト
- ヘルスチェックリクエストが503になると、サーバーが切り離されてリクエストが到達しなくなる構成の場合は、除外が必要です
- ヘルスチェックURLが、メンテナンス対象範囲のリソースが利用できなくても、正常(200 OK)を返すような準備が別で必要になります
メンテナンスページの指定
503 service temporarily unavailable
レスポンスで表示されるエラーページは、ErrorDocument
で設定します。
メンテナンス開始時にApache設定変更を反映するのであれば、ErrorDocument 503 /error/maintenance.html
のような固定値で設定できます。
今回の場合、事前にメンテナンス設定を行うため、メンテナンス以前に503が発生した場合に、メンテナンスページが表示されてしまいます。
サンプルでは通常時のエラーページを/error/503.html
とした場合の、環境変数による出し分けをおこなっています。
RewriteRule ^ - [R=503,L,E=MAINTENANCE:yes,E=ERROR_DOCUMENT:error/maintenance.html] RewriteCond %{ENV:MAINTENANCE} !^yes$ RewriteRule ^ - [E=ERROR_DOCUMENT:error/503.html] ErrorDocument 503 /%{ENV:ERROR_DOCUMENT}
RewriteRule
でHTTPステータスコード503にする際にフラグで環境変数を設定します。
MAINTENANCE
: エラーページを表示する条件を満たすかどうかのフラグ変数ERROR_DOCUMENT
:ErrorDocument
に指定するページのパス文字列変数
MAINTENANCE
がyesでない場合は、ERROR_DOCUMENT
がerror/503.html
となり、
ErrorDocument 503 /error/503.html
となるので、メンテナンス前のリクエストが503になっても通常のエラーページが表示されます。
メンテナンスページHTMLの注意点
メンテナンスページのHTMLファイルから呼び出す、画像やCSS、JSのパスは、絶対パスにしてください。
HTTPレスポンスコードが503になることにより、レスポンスされるファイルは/error/maintenance.html
ですが、
Apache内部のリダイレクトにより表示されているため、ブラウザで表示されているURLパスは、リクエストした時のものになります。
このため、相対パスで画像やCSS、JSを指定していると、リクエストしたURLパスからの相対パスを参照することになり、存在しないURLパスにリクエストすることになります。
絶対パス <img src="/error/images/banner.jpg" /> 相対パス <img src="./images/banner.jpg" />
/error/maintenance.html
に上記の表記がされていて、メンテナンス中に/entry/2024/09/10/001.html
にリクエストした場合、ブラウザがリクエストする画像ファイルはそれぞれ以下のようなパスになります。
- 絶対パス: /error/images/banner.jpg
- 相対パス: /entry/2024/09/10/images/banner.jpg
Retry-Afterヘッダーの付与
検索エンジンのクローラーにメンテナンスの終了時刻を通知するためにRetry-After
ヘッダーを付与します。
Header always set Retry-After "Sat, 31 Aug 2024 19:00:00 GMT" env=REDIRECT_MAINTENANCE
Retry-After
ヘッダーの値として記載する日時は、HTTP-dateフォーマットです。
Retry-After: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
GMTに変換して記載する必要があるのでご注意ください。
Linuxのdateコマンドで変換する場合、次のコマンドを利用ください。
$ echo "2024-09-01 04:00:00 JST" | date -f - +"%a, %d %b %Y %H:%M:%S GMT" -u Sat, 31 Aug 2024 19:00:00 GMT
Retry-After - HTTP | MDN
7.1.1.1. Date/Time Formats | RFC 7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
メンテナンス開始以降にRetry-Afterヘッダーを付与する
Header
ディレクティブでは、環境変数が存在するかどうかを判定して、設定の有効・無効を制御できます。
メンテナンスページを表示する条件を満たすかどうかのフラグ変数として、MAINTENANCE
を用意していますが、
サンプルではREDIRECT_MAINTENANCE
という環境変数を使って判定しています。
MAINTENANCE
を使うと、メンテナンス時刻以降のレスポンスであってもRetry-After
ヘッダーが付与されません。
設定を検証するなかで、以下のような挙動となっていることがわかりました。
- 内部リダイレクト(URLリライト)された場合、
Header
ディレクティブは、Apacheが内部的に行うリクエストへのレスポンスヘッダに適用される - 内部リダイレクト(URLリライト)された場合、元リクエストの環境変数は
REDIRECT_
というプリフィックスが付いた状態で、内部で発生するリクエスト処理に引き継がれる
Apacheが内部的に行う/error/maintenance.html
へのレスポンスにRetry-After
ヘッダーを付与する必要があるので、
元リクエストのMAINTENANCE
が引き継がれた、REDIRECT_MAINTENANCE
が存在する場合を条件としています。
カスタムエラーレスポンスとリダイレクト | カスタムエラーレスポンス - Apache HTTP サーバ バージョン 2.4
When setting environment variables in Apache RewriteRule directives, what causes the variable name to be prefixed with "REDIRECT_"? - Stack Overflow
まとめ
事前に設定した時刻ピッタリにメンテナンスページに切替える方法について紹介してきました。
今回検証をするなかで、ディレクティブごとに環境変数がどう使えるかや、知らなかったApacheの挙動を知ることができてとても学習になりました。
サンプルでは条件設定にRewriteCond
をつかっていますが、<If>
ディレクティブを使ったバージョンも検証してみて、
使い慣れているのと、視認性が高かったRewriteCond
のほうを紹介しました。
他設定の挙動に差異があるので、機会があれば<If>
ディレクティブバージョンも紹介できればと思います。
思いのほか複雑な設定となったので、OSやバージョンによって動作の差異がある可能性があります。
あくまで参考としてご利用いただき、ご自身の環境での動作確認は万全におこなうようお願いします。
また、.htaccessに記載した場合の挙動については検証していないため、多少の手直しが必要かもしれません。
今回紹介した設定をベースにして、切替のトリガーをファイルの有無にしたり、特定のCookieがあれば除外するといった
いろいろな要件へのアレンジもできたらいいなと思います。