サーバーアクセスログ — アプリが受け取ったすべてのリクエストの物語
サーバーが処理するすべてのHTTPリクエストは、アクセスログに1行を残します。ここでは、フィールドごとに読み取り方と、注意すべきパターンを紹介します。
サーバーが処理するすべてのHTTPリクエストは、アクセスログに1行を残します。ほとんどの場合、これらのファイルはディスク上に静かに増大し続け、ディスクアラートが発火するか、プロダクション環境で何かが壊れるまで、何もしません。そのとき、突然多くの人がそれらを読むようになります。
以下は、結合ログフォーマットを用いたサーバーの1行の例です。
203.0.113.42 - jsmith [09/May/2026:14:32:11 +0000] "GET /api/users/profile HTTP/1.1" 200 1843 "https://example.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
8つのフィールド。それぞれが、何が起こったかに関する証拠の一つです。左から右へ進みましょう。
フィールド、1つずつ
1. クライアントIP — 203.0.113.42
サーバーにTCP接続を開いたIPアドレスです。これは ない 必然的にユーザーのIPアドレスです。もし、ロードバランサーやCDN、逆プロキシ(たとえば、Nodeアプリの前にNginxがある場合)に隠れている場合は、これはプロキシのIPアドレスになります。実際のクライアントIPアドレスはヘッダーに存在しており、それをキャプチャするためには、ログフォーマットを明示的に設定する必要があります。 X-Forwarded-For または X-Real-IP Nginxでは、そのような設定を
に追加します。Apacheでは $http_x_forwarded_for を使用します。この設定をスキップし、その後に悪用の報告や悪意あるアクターをブロックする必要が生じた場合、あなたはすべてのログ行にロードバランサのIPアドレスを見つけることになります。 log_format 2. Ident — %{X-Forwarded-For}i常にダッシュです。このフィールドは、identd検索の結果を保持するものでした。これは古く、RFC 1413というプロトコルで、サーバーがクライアントのOSからプロセスの所有者を確認するものでした。現在では誰もidentdを実行していません。このフィールドは、identdが存在していた時代に標準化されたCommon Log Formatのため、存在しています。無視してください。
3. 認証ユーザー — -
HTTP基本認証またはDigest認証が有効なときに埋め込まれるユーザー名です。ほとんどの現代アプリ(トークン認証、セッションクッキー、JWTなど)では、これはダッシュになります。htpasswdで管理者エリアを保護している場合、失敗したログインは401ステータスに
として表示され、成功したログインはユーザー名を表示します。 jsmith
4. 時刻 — - サーバーがリクエストを処理した時間(開始時刻ではなく)です。フォーマットは
です。タイムゾーンオフセットは人々が実際には認識していないほど重要です。アプリサーバーがUTCで動作しているが、APMやアラートツールがローカルタイムゾーンにある場合、ログのピークと特定のインシデントを関連付けるには、毎回変換計算が必要になります。すべてをUTCで実行してください。 [09/May/2026:14:32:11 +0000]
5. リクエストライン — 最も情報量の多いフィールドです。3つの部分:HTTPメソッド、パスとクエリ文字列、プロトコルバージョンです。 — GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS。エンドポイントに予期しないメソッドが使われている場合は、注意が必要です。 day/Mon/year:HH:MM:SS timezoneパス + クエリ文字列
— 何が求められたかを正確に示します。クエリ文字列には検索語、ID、そして設計が不十分なAPIでは認証トークンが含まれます。適切にログを記録してください。 "GET /api/users/profile HTTP/1.1"
— 2026年にはログにHTTP/1.0クライアントが現れるのは、古くなった統合または古いクライアントを偽装しているものだけです。HTTP/2およびHTTP/3は、サーバーがそれらをサポートしている場合、そのバージョンとしてログに記録されます。
- 方法 6. ステータスコード —
- サーバーが送信したHTTPステータスです。これは判断フィールドです。 — 成功。リクエストが処理されました。
- プロトコル — リダイレクト。クライアントは別の場所に移動する必要があります。
— クライアントエラー。不正なリクエスト、認証の欠如、見つからない。これは正当な使用か、プローブかの可能性があります。 200
— サーバーエラー。アプリケーションがクラッシュ、タイムアウト、または無効なデータを返した。これらすべてを調査する必要があります。
2xxインシデントを調査する際には、まず3xxをフィルタリングします。その後、最初の500のタイムスタンプを確認します。その時が失敗が開始された時間です。それ以前は事前の状況、それ以降は影響範囲です。4xx7. 発送バイト —5xxレスポンスボディのサイズ(ヘッダーを除く)です。ダッシュは0バイト(典型的にはクライアントがコンテンツをすでに持っている場合)を意味します。予期しない大きな値が、数百バイトを返すべきエンドポイントに現れることは赤旗です。認証されたユーザーが「プロフィールを取得」するエンドポイントに40MBを受信するのは、バグか、誰かがデータエクスポートを悪用している可能性があります。
8. リファラーヘッダー — 5xx リクエストを提出する前にクライアントが来た場所です。ブラウザの
ヘッダー内のURLです。(HTTP規格は1996年に誤ってスペルを間違え、今もそのままです。)直接アクセス、ブックマーク、ネイティブアプリからのリクエストは、リファラーが空になります。このフィールドは結合ログフォーマットに存在しており、元のCommon Log Formatはバイト送信までに終了します。 1843
リファラースパム — 大量のリクエストで偽のリファラーヘッダーを送り、分析に表示されるように試みる。かつては頻繁に見られましたが、現在はほとんどが廃れています。それでも、集計統計からフィルタリングする価値があります。 304 Not Modified 9. ユーザーエージェント —
クライアントが自身を識別するもの。ユーザーエージェントは非常に容易に偽造できるため、これはヒントとして扱うべきです。正当なスクリーパーは通常、誠実に識別しています: "https://example.com/dashboard"
。スクリーパーがブラウザを偽装して送信する場合、Mozilla/5.0 Chromeのフル文字列を送信します。Curlは Referer を送信しますが、誰かが
で上書きすると、それらが変更されます。
注意すべきパターン:同じエンドポイントに、機械的な速度で同じユーザーエージェント文字列が繰り返し送信される、またはiOS Safariと名乗っているが、人間のブラウザが行わないようなパターン(画像やCSSなし、順番に商品ページをスクレイピング)を示すユーザーエージェントです。 "Mozilla/5.0 (Windows NT 10.0; Win64; x64)…"
すべてのログに現れるパターン Googlebot/2.1 (+http://www.google.com/bot.html), Bingbot/2.0脆弱性スキャン curl/8.x 1つのIPまたは小さなサブネットが、急速に多数のパスにアクセスする: -A.
。すべてが404です。これは自動化されたスキャナーがチェックリストを実行しているだけであり、標的攻撃ではありません。彼らは常にすべての公開IPに対して実行しています。
質問は「なぜ彼らが私をスキャンしているのか」ではなく、「そのパスのどれが200を返しているか」です。もし
がサーバーで200を返すなら、それが実際の問題です。
認証情報の詐欺 /.env, /wp-login.php, /phpmyadmin, /admin/config.yml, /.git/config高頻度のPOSTリクエストがログインエンドポイントに送られ、ほぼすべてが401を返し、分散されたIPアドレスから送信されています。その特徴は、同じエンドポイント、一貫した応答サイズ(エラーページ)、多数の異なるソースIPアドレスが同じASNを共有していることです。応答時間も一貫しています。自動ログイン試行は、レート制限を回避するために一定の速度で実行されます。
デプロイ後の404の急増 /.env 新しいバージョンをデプロイした後、404が急増します。メール、ブックマーク、外部サイトの古いリンクが存在するURLにアクセスできなくなります。これは正常な現象ですが、404が存在すべきURLに現れた場合は、ルーティングのバグです。ルート定義と404パスを比較してください。
# Check if any sensitive paths actually returned 200
grep -E '"(GET|POST) /(wp-login\.php|\.env|\.git/config|phpmyadmin|admin)' access.log | awk '$9 == 200'
遅延応答のクラスタリング
標準ログフォーマットには応答時間は含まれていません。追加する必要があります:Apacheでは、
# Count POST /login attempts with 401 status by source IP
awk '$6 == "\"POST" && $7 == "/api/login" && $9 == "401" {print $1}' access.log \
| sort | uniq -c | sort -rn | head -20
(マイクロ秒) を
に追加します。Nginxでは、 ブロックを追加します。そのフィールドを追加した後: 遅いリクエストが特定の時間帯に集中している場合は、競合が発生しているということです。cronジョブ、バッチプロセス、またはバックアップがデータベースを圧迫している可能性があります。特定のパスが時間に関係なく常に遅い場合は、遅いクエリまたはブロッキングI/O呼び出しの可能性があります。プロファイリングが必要です。
ログからインシデントを調査する
アクセスログはタイムラインです。ユーザーが「3時頃に何かが壊れた」と報告した場合、あなたは: %D 14:50–15:10の範囲にフィルタをかける LogFormat最初の5xxを見つける — それが開始された時間です $request_time を使用します。この設定をスキップし、その後に悪用の報告や悪意あるアクターをブロックする必要が生じた場合、あなたはすべてのログ行にロードバランサのIPアドレスを見つけることになります。 log_format その数分前までに何が変更されたかを確認する:デプロイ?設定の更新?証明書のリフレッシュ?
# Nginx: show requests slower than 3 seconds (request_time in last field)
awk '{if ($NF+0 > 3) print $0}' /var/log/nginx/access.log | head -20
5xxが発生したパスを確認する — すべてのエンドポイントか、特定のエンドポイントか?
成功応答の前に後ろのバイト送信を確認する — 何かがトリップレスポンスを開始したか?
いくつかの失敗のサインを知っていると良いです:
- 502の急増
- — 上流が死滅(アプリサーバークラッシュ、接続プールが枯渇、データベースが停止)。502は正確なタイムスタンプから始まります。
- リダイレクトループ
- — 同じIPから同じパスに繰り返し301/302リダイレクトが行われます。通常はHTTPSリダイレクトの誤設定またはCloudflare SSL設定がアプリのリダイレクトロジックと競合している場合です。
- 200で0バイト
— ステータス200ですが、バイト送信が0または
- です。アプリはリクエストを受け入れ、例外を吸収し、空のボディを返したということです。典型的な未処理エラーです。 413の急増
- — クライアントがサイズ制限を超えるリクエストボディを送信しています。あなたの制限が使用ケースに合わないか、または誰かがアップロードの脆弱性を探っている可能性があります。 複数のログフォーマット(Apache Common、Apache Combined、Nginxデフォルト、カスタムフォーマット)を扱っている場合、
- アクセスログフォーマッター はフィールドをパースし、注釈を付加することで、サーバーを切り替えるたびにフィールドの位置を頭に思い描く必要をなくします。
-ログ管理 — 未設定で後悔するべきこと - ログ回転 毎日回転し、圧縮し、14~30日間保存するように設定されています。そうでなければ、
がディスクを満たし、これはプロダクション環境で実際に起こります。 中央化ログ — 1台以上のサーバーを持つ場合、個別ファイルをタールするだけではスケーリングできません。Loki + Grafana、Elasticsearch、または管理サービスに送信してください。構造化されたJSONログフォーマットは、awkでCLFをパースするよりもクエリが簡単になります。
原始ログへのアクセス制御
- ログファイルにはクエリパラメータにトークン、ユーザーの個人情報(PII)、内部パスが含まれている可能性があります。世界に公開しないでください。GDPRや類似規制の下では、ログの保存期間について慎重に判断してください。 —
logrotate敏感なクエリパラメータをログに記録しないaccess.log— アプリがURLパラメータに認証トークンやパスワードを受け入れている場合(それはすべきではないが、古いAPIでは時々見られる)は、ログレベルでフィルタリングしてディスクに保存しないでください。 - 中央集約ログ — 1台以上のサーバーを持つ場合、個別のログファイルを確認する方法は拡張性がありません。Loki + Grafana、Elasticsearch、または管理サービスにログを送信してください。構造化されたJSON形式のログは、awkでCLFを解析するよりも検索がはるかに簡単になります。
- 原始ログへのアクセス制御 — ログファイルにはトークン、ユーザーの個人情報(PII)、内部パスなどのクエリパラメータが含まれている可能性があります。世界に公開しないでください。GDPRや類似の規制に従っている場合は、ログの保存期間について慎重に検討してください。
- Don’t log sensitive query params — アプリケーションが認証トークンやパスワードをURLパラメータとして受け入れている場合(これは望ましくないが、古いAPIでは時折見られます)、ログレベルでそれらをフィルタリングしてディスクに保存する前に処理してください。
恵 スコアボードが到着しました!
スコアボード ゲームを追跡する楽しい方法です。すべてのデータはブラウザに保存されます。さらに多くの機能がまもなく登場します!
