2026年7月29日

2026年7月29日

Apacheのログフォーマットをカスタマイズする|応答時間・実IP・除外設定の書き方

Apache のアクセスログは、mod_log_config の LogFormat で記録する項目を決め、CustomLog で出力先とフォーマットを指定します。標準の combined 形式に応答時間や実際のクライアント IP を足すだけで、遅いページの特定や不正アクセスの調査がしやすくなります。この記事では、書式指定子の読み方、WordPress の運用で役立つ項目の追加、CDN やロードバランサーの背後での実 IP の記録、不要なリクエストの除外、記録してはいけない情報までを順に説明します。

LogFormat と CustomLog の関係

LogFormat は「書式に名前を付ける」命令、CustomLog は「どのファイルに、どの書式で書くか」を決める命令です。Debian/Ubuntu 系では /etc/apache2/apache2.conf に、RHEL/AlmaLinux 系では /etc/httpd/conf/httpd.conf に、標準の書式が定義されています。

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common

CustomLog ${APACHE_LOG_DIR}/access.log combined

CustomLog は VirtualHost ごとに書けるため、サイト別にログを分けられます。どの設定ファイルで CustomLog が指定されているかは、次のように検索すると分かります。

# Debian/Ubuntu系
grep -Rn "CustomLog\|LogFormat" /etc/apache2/
# RHEL/AlmaLinux系
grep -Rn "CustomLog\|LogFormat" /etc/httpd/

既存の combined という名前を書き換えると、その名前を使っているすべてのログの形式が変わり、ログ解析ツールが読めなくなることがあります。独自の項目を足すときは、新しい名前で書式を定義し、使いたい VirtualHost だけで切り替えるのが安全です。

主な書式指定子

combined 形式の各項目の意味は次のとおりです。

  • %h:接続元のホスト(通常は IP アドレス)
  • %l:identd による識別名。ほぼ使われず - になります
  • %u:HTTP 認証のユーザー名。認証がなければ -
  • %t:リクエストを受けた日時
  • %r:リクエストの1行目(メソッド、パスとクエリ文字列、プロトコル)
  • %>s:最終的なステータスコード。内部リダイレクト後の値を記録するため %s ではなく %>s を使います
  • %b:レスポンス本体のバイト数(0 のときは -)
  • %{Referer}i、%{User-Agent}i:リクエストヘッダーの値。%{ヘッダー名}i で任意のリクエストヘッダーを記録できます

運用で追加することが多いのは次の項目です。

  • %D:リクエストの処理にかかった時間(マイクロ秒)。%T は秒単位で、短い処理はすべて 0 になるため %D のほうが使いやすいです
  • %v:リクエストを処理した VirtualHost の ServerName。複数サイトのログを1ファイルにまとめる場合に必要です
  • %{Host}i:ブラウザが送った Host ヘッダー
  • %a:クライアントの IP アドレス。後述の mod_remoteip と組み合わせると実 IP を記録できます
  • %X:レスポンス完了時の接続の状態(+ は KeepAlive で継続、- は切断、X は完了前に切断)
  • %I、%O:ヘッダーを含む受信・送信バイト数。mod_logio が必要です

書式指定子の一覧と細かな挙動は Apache の mod_log_config のドキュメントに載っています。Apache 2.4 の中でもマイナーバージョンで追加された指定子があるため、使っている版のドキュメントを確認してください(2026年10月現在)。

WordPress 運用向けの書式を作る

combined に、処理時間・VirtualHost 名・接続状態を足した書式の例です。末尾に %D を置くと、空白を含む User-Agent があっても「最後の列が処理時間」と決まるため、コマンドで集計しやすくなります。

LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %v %X %D" wp_combined

<VirtualHost *:443>
    ServerName example.com
    CustomLog ${APACHE_LOG_DIR}/example.com-access.log wp_combined
</VirtualHost>

設定を変えたら文法を確認してから再読み込みし、実際にアクセスして新しい形式で記録されているかを見ます。

sudo apache2ctl configtest
sudo systemctl reload apache2
sudo tail -f /var/log/apache2/example.com-access.log

この形式なら、遅いリクエストの上位を次のように抜き出せます。7列目がパス、最後の列が処理時間です。

# 処理時間(マイクロ秒)の長い順に上位20件
awk '{print $NF, $7}' /var/log/apache2/example.com-access.log | sort -rn | head -20

WordPress では、/wp-admin/admin-ajax.php や /wp-json/ への頻繁で重いリクエスト、/wp-cron.php の実行時間、/xmlrpc.php や /wp-login.php への集中的なアクセスがこの集計で見つかることがよくあります。処理時間はページキャッシュが効いているかの確認にも使えます。キャッシュから返したページは数ミリ秒、PHP が動いたページは数百ミリ秒以上になるのが一般的です。

CDN・ロードバランサーの背後で実 IP を記録する

Cloudflare などの CDN やロードバランサーを前に置くと、Apache から見た接続元はその中継サーバーになり、ログの IP がすべて同じになります。クライアントの IP は X-Forwarded-For などのヘッダーに入って届くため、mod_remoteip でそれを接続元として扱わせます。

# Debian/Ubuntu系: sudo a2enmod remoteip
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 203.0.113.0/24

RemoteIPTrustedProxy には、実際に使っている CDN やロードバランサーの IP 範囲を指定します(例の 203.0.113.0/24 は説明用のアドレスです)。CDN の IP 範囲は各社が公開しており、変わることがあるため定期的に見直してください。信頼する中継元を限定しないと、誰でもヘッダーを偽って任意の IP を名乗れるため、ログやアクセス制限が信用できなくなります。CDN によっては専用のヘッダー(Cloudflare の CF-Connecting-IP など)を使う方法もあります。

mod_remoteip を有効にしたら、書式は %h ではなく %a を使います。中継サーバー自体の IP も残したい場合は %{c}a で記録できます。

%{X-Forwarded-For}i をそのまま書式に入れる方法もありますが、これはクライアントが送った値をそのまま記録するだけで、偽装されていても区別できません。調査用の補助情報として残すのは構いませんが、IP を判断する主な列には mod_remoteip を通した %a を使うほうが安全です。

不要なリクエストを記録しない

ロードバランサーのヘルスチェックや監視サービスのアクセスは、数秒ごとに記録されてログを埋めてしまいます。mod_setenvif で目印の環境変数を付け、CustomLog の env=! で除外できます。

SetEnvIf Request_URI "^/healthz$" dontlog
SetEnvIf User-Agent "ELB-HealthChecker" dontlog

CustomLog ${APACHE_LOG_DIR}/example.com-access.log wp_combined env=!dontlog

画像や CSS など静的ファイルの記録を止めればログは小さくなりますが、ホットリンクやボットの調査に使えなくなります。除外は目的がはっきりしているものに絞ってください。

逆に、エラーだけを別ファイルに残すこともできます。Apache 2.4 では CustomLog に expr= で条件式を書けます。

CustomLog ${APACHE_LOG_DIR}/example.com-4xx5xx.log wp_combined "expr=%{REQUEST_STATUS} >= 400"

JSON 形式で出力する場合の注意

ログを Elasticsearch や Loki などの集約基盤に送る場合は、JSON 形式にすると取り込みの設定が楽になります。LogFormat の中に JSON の形を書けば出力できますが、注意点があります。

LogFormat "{ \"time\":\"%{%Y-%m-%dT%H:%M:%S%z}t\", \"client\":\"%a\", \"host\":\"%v\", \"method\":\"%m\", \"path\":\"%U\", \"status\":%>s, \"bytes\":%B, \"duration_us\":%D, \"ua\":\"%{User-Agent}i\" }" wp_json

Apache はヘッダー値に含まれる " や \、制御文字をエスケープして記録しますが、その一部は \xhh の形になり、JSON の規格では正しいエスケープではありません。変わった User-Agent が来ると、その行だけ取り込みに失敗することがあります。取り込み側のパーサーで不正な行をどう扱うかを確認してから本番に入れてください。

記録してはいけない情報と保存期間

ログの項目を増やすときは、残してはいけない情報が入らないかを考えます。

  • %{Cookie}i を記録しない:WordPress のログイン Cookie が入るため、ログが漏れるとセッションを乗っ取られるおそれがあります
  • %{Authorization}i を記録しない:Basic 認証やアプリケーションパスワードの資格情報が含まれます
  • クエリ文字列に注意する:%r や %q にはクエリ文字列が入ります。パスワード再設定のリンクのように、URL にトークンを含む仕組みもあります。ログの閲覧権限を絞り、外部に渡すときはクエリ部分を落とします

IP アドレスや User-Agent は、組み合わせ次第で個人を識別できる情報として扱われることがあります。保存期間を決めて logrotate で古いログを削除し、プライバシーポリシーでアクセスログの取得と利用目的に触れておくと安心です。

まとめ

  • LogFormat で書式に名前を付け、CustomLog で出力先と書式を指定する。既存の combined は書き換えず、新しい名前で定義する
  • 処理時間の %D を末尾に足すと、遅いページを awk で集計できる
  • CDN やロードバランサーの背後では mod_remoteip で信頼する中継元を限定し、%a を記録する
  • ヘルスチェックなどは env=! で除外し、エラーだけを expr= で別ファイルにも残せる
  • JSON 出力は \xhh のエスケープに注意する
  • Cookie や Authorization ヘッダーは記録せず、保存期間を決めてローテーションする

お気軽にご相談ください

お見積りへ お問い合わせへ
▲