Regular review of Apache logs exposes reconnaissance, brute-force probing, and exploit attempts before they become outages or data loss. The access log captures request paths, status codes, and traffic patterns that often reveal malicious intent long before an alerting system notices.

In most deployments, the Apache access log uses a combined format that includes the client address, timestamp, request line, HTTP status, response size, referrer, and user-agent. Those fields enable fast pivots: aggregate by IP to find “top talkers,” aggregate by URI to identify scanning targets, and search for payload patterns like traversal strings or encoded script tags.

Correct interpretation depends on deployment details such as logrotate, time zone, and reverse proxies or load balancers. When a proxy sits in front of Apache, the “client IP” in the first field can be the proxy unless mod_remoteip (or an equivalent mechanism) is configured. Pattern searches also produce false positives, so matches work best as leads to validate by reviewing full log lines and correlating with server errors.

Steps to analyze threats in Apache logs:

  1. List available Apache access logs, including rotated files.
    $ sudo ls -lh /var/log/apache2/access.log*
    -rw-r----- 1 root adm 2.3K Jan 11 06:16 /var/log/apache2/access.log

    Common paths include /var/log/apache2/access.log on Ubuntu and Debian, and /var/log/httpd/access_log on Red Hat-based systems.

    Compressed rotations (for example, access.log.2.gz) are readable with zless and searchable with zgrep.

  2. Preview recent entries to confirm the log format and field order.
    $ sudo tail -n 3 /var/log/apache2/access.log
    192.0.2.17 - - [11/Jan/2026:06:16:35 +0800] "PUT /uploads/test.txt HTTP/1.1" 405 495 "-" "curl/8.5.0"
    66.249.66.1 - - [11/Jan/2026:06:16:35 +0800] "GET /robots.txt HTTP/1.1" 404 432 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
    52.167.144.1 - - [11/Jan/2026:06:16:35 +0800] "GET / HTTP/1.1" 200 10926 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"

    The request line appears inside quotes as METHOD URI PROTOCOL, and status code and byte count follow.

  3. Identify the highest-volume source IPs.
    $ sudo awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -nr | head -10
         13 203.0.113.45
          2 198.51.100.23
          1 66.249.66.1
          1 52.167.144.1
          1 192.0.2.17

    Reverse proxies can mask client IPs unless mod_remoteip (or an equivalent) is configured, so validate what the first field represents before acting on it.

  4. Count HTTP methods to spot unexpected verbs.
    $ sudo awk '{print $6}' /var/log/apache2/access.log | tr -d '"' | sort | uniq -c | sort -nr
         15 GET
          1 PUT
          1 PROPFIND
          1 OPTIONS

    Rare methods like PROPFIND, PUT, DELETE, or TRACE often indicate scanning or misconfiguration.

  5. Search for uncommon HTTP methods in request lines.
    $ sudo grep -E '"(PUT|DELETE|TRACE|OPTIONS|CONNECT|PROPFIND) ' /var/log/apache2/access.log | head -5
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "PROPFIND / HTTP/1.1" 405 500 "-" "Mozilla/5.0 (X11; Linux x86_64)"
    198.51.100.23 - - [11/Jan/2026:06:16:35 +0800] "OPTIONS /api/login HTTP/1.1" 200 137 "https://host.example.net/login" "Mozilla/5.0 (X11; Linux x86_64)"
    192.0.2.17 - - [11/Jan/2026:06:16:35 +0800] "PUT /uploads/test.txt HTTP/1.1" 405 495 "-" "curl/8.5.0"

    TRACE is commonly disabled for security reasons, and sustained PUT or DELETE requests can indicate an attempt to modify server content.

  6. Pivot into a single source IP by listing its most-requested URIs.
    $ sudo grep '^203.0.113.45 ' /var/log/apache2/access.log | awk '{print $7}' | sort | uniq -c | sort -nr | head -15
          2 /
          1 /xmlrpc.php
          1 /wp-login.php
          1 /shell.php
          1 /server-status
          1 /search?q=%27%20or%201%3D1%23
    ##### snipped #####

    Replacing 203.0.113.45 with an IP from the “top talkers” output keeps investigation focused.

  7. Count requests per minute for a single IP to spot bursts.
    $ sudo grep '^203.0.113.45 ' /var/log/apache2/access.log | cut -d[ -f2 | cut -d] -f1 | awk -F: '{print $1":"$2":"$3}' | sort | uniq -c | sort -nr | head -10
         13 11/Jan/2026:06:16

    Sustained high counts across many consecutive minutes are typical of automated scanning or brute-force activity.

  8. Identify scanning behavior by listing the most-requested missing pages (404).
    $ sudo awk '$9 == 404 {print $7}' /var/log/apache2/access.log | sort | uniq -c | sort -nr | head -20
          1 /xmlrpc.php
          1 /wp-login.php
          1 /shell.php
          1 /search?q=%3Cscript%3Ealert(1)%3C%2Fscript%3E
          1 /search?q=%27%20or%201%3D1%23
          1 /robots.txt
          1 /product?id=1%20union%20select%201,2,3
          1 /phpmyadmin/
          1 /etc/passwd
          1 /.env
          1 /cgi-bin/status?cmd=id%3Bwhoami
          1 /%2e%2e%2f%2e%2e%2fetc%2fpasswd

    Repeated 404s clustered around well-known admin or framework paths usually indicate probing for vulnerable endpoints.

  9. Review authentication and authorization probing via 401 and 403 responses.
    $ sudo awk '$9 == 401 || $9 == 403 {print $1, $7, $9}' /var/log/apache2/access.log | head -10
    203.0.113.45 /server-status 403

    High volumes of 401s against a login endpoint can indicate credential stuffing, and repeated 403s against admin paths can indicate permission probing.

  10. Search for common exploit targets and misconfiguration leaks.
    $ sudo grep -Ei '"(GET|POST) /(wp-login\.php|xmlrpc\.php|\.env|phpmyadmin|\.git|admin|setup|config)' /var/log/apache2/access.log | head -8
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "GET /wp-login.php HTTP/1.1" 404 432 "-" "Mozilla/5.0 (X11; Linux x86_64)"
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "GET /xmlrpc.php HTTP/1.1" 404 432 "-" "Mozilla/5.0 (X11; Linux x86_64)"
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "GET /.env HTTP/1.1" 404 432 "-" "Mozilla/5.0 (X11; Linux x86_64)"
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "GET /phpmyadmin/ HTTP/1.1" 404 432 "-" "Mozilla/5.0 (X11; Linux x86_64)"

    Paths like /.env and /.git/config should never be publicly accessible; any 200 responses for these require immediate remediation.

  11. Spot directory traversal attempts, including URL-encoded variants.
    $ sudo grep -Ei '(\.\./|%2e%2e%2f|%2fetc%2fpasswd|%2fproc%2fself%2fenviron)' /var/log/apache2/access.log | head -8
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "GET /%2e%2e%2f%2e%2e%2fetc%2fpasswd HTTP/1.1" 404 432 "-" "Mozilla/5.0 (X11; Linux x86_64)"

    Traversal attempts can indicate active exploitation, especially when paired with successful 200/206 responses.

  12. Search for XSS-style payload indicators in URLs and parameters.
    $ sudo grep -Ei '(<script|%3cscript%3e|onerror=|javascript:|%22%3e%3c)' /var/log/apache2/access.log | head -8
    198.51.100.23 - - [11/Jan/2026:06:16:35 +0800] "GET /search?q=%3Cscript%3Ealert(1)%3C%2Fscript%3E HTTP/1.1" 404 432 "-" "curl/8.5.0"

    Encoding often hides payloads, so searches should include both raw and percent-encoded forms.

  13. Inspect for SQL injection patterns in request parameters.
    $ sudo grep -Ei 'union(\+|%20|[[:space:]])+select|or(\+|%20|[[:space:]])+1=1|information_schema|sleep(\(|%28)|benchmark(\(|%28)|%27|%22' /var/log/apache2/access.log | head -8
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "GET /search?q=%27%20or%201%3D1%23 HTTP/1.1" 404 432 "-" "Mozilla/5.0 (X11; Linux x86_64)"
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "GET /product?id=1%20union%20select%201,2,3 HTTP/1.1" 404 432 "-" "Mozilla/5.0 (X11; Linux x86_64)"

    Any signs of SQL injection warrant immediate validation of input handling and parameterized queries in the application layer.

  14. Identify command-injection style separators and suspicious execution hints.
    $ sudo grep -Ei '(cmd=|exec=|%3b|%7c|%60|\$\(|%24%28|/bin/(sh|bash))' /var/log/apache2/access.log | head -8
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "GET /cgi-bin/status?cmd=id%3Bwhoami HTTP/1.1" 404 432 "-" "Mozilla/5.0 (X11; Linux x86_64)"

    Command injection attempts can be destructive when an endpoint passes parameters into shell execution, so prioritize investigation when 200/302 responses appear.

  15. Spot requests for high-risk file extensions commonly used in web shells or executables.
    $ sudo grep -Ei '"(GET|POST) [^ ]*\.(php|phtml|phar|jsp|asp|aspx|exe|sh|py)(\?| |")' /var/log/apache2/access.log | head -8
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "GET /wp-login.php HTTP/1.1" 404 432 "-" "Mozilla/5.0 (X11; Linux x86_64)"
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "GET /xmlrpc.php HTTP/1.1" 404 432 "-" "Mozilla/5.0 (X11; Linux x86_64)"
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "GET /shell.php HTTP/1.1" 404 432 "-" "Mozilla/5.0 (X11; Linux x86_64)"

    Extension scans often accompany directory traversal and exploit-kit probing.

  16. Find anomalous or automation-heavy user agents by frequency.
    $ sudo awk -F'"' '{print $6}' /var/log/apache2/access.log | sort | uniq -c | sort -nr | head -15
         14 Mozilla/5.0 (X11; Linux x86_64)
          2 curl/8.5.0
          1 Mozilla/5.0 (Windows NT 10.0; Win64; x64)
          1 Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)

    Legitimate monitoring can also appear as curl or python-requests, so correlate user-agent findings with URI targets and status codes.

  17. Review 5xx responses to locate endpoints under stress or possible exploitation.
    $ sudo awk '$9 ~ /^5/ {print $7}' /var/log/apache2/access.log | sort | uniq -c | sort -nr | head -20

    Spikes in 500-series responses during probing can indicate crashes, unhandled exceptions, or resource exhaustion.

  18. Correlate suspicious windows with the Apache error log to confirm server-side failures.
    $ sudo tail -n 20 /var/log/apache2/error.log
    ##### snipped #####
    [Sun Jan 11 06:05:31.343777 2026] [mpm_event:notice] [pid 14924:tid 279759407484960] AH00489: Apache/2.4.58 (Ubuntu) OpenSSL/3.0.13 configured -- resuming normal operations
    [Sun Jan 11 06:05:31.343785 2026] [core:notice] [pid 14924:tid 279759407484960] AH00094: Command line: '/usr/sbin/apache2'
    [Sun Jan 11 06:05:31.454493 2026] [authz_core:error] [pid 14927:tid 279759220764960] [client 203.0.113.45:0] AH01630: client denied by server configuration: /var/www/html/server-status
    ##### snipped #####

    Error logs can contain stack traces and sensitive paths, so handle exports with the same care as production data.

  19. Filter the access log to a specific incident window using an exact timestamp prefix.
    $ sudo grep '\[11/Jan/2026:06:' /var/log/apache2/access.log | head -3
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "GET / HTTP/1.1" 200 10926 "-" "Mozilla/5.0 (X11; Linux x86_64)"
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "GET /wp-login.php HTTP/1.1" 404 432 "-" "Mozilla/5.0 (X11; Linux x86_64)"
    203.0.113.45 - - [11/Jan/2026:06:16:35 +0800] "GET /xmlrpc.php HTTP/1.1" 404 432 "-" "Mozilla/5.0 (X11; Linux x86_64)"

    Matching on [DD/Mon/YYYY:HH:MM: keeps searches aligned to the Apache timestamp format.

  20. Build a quick suspect list by counting IPs that hit high-risk exploit patterns.
    $ sudo grep -Ei '(\.\./|%2e%2e%2f|%2fetc%2fpasswd|%3cscript%3e|<script|union(\+|%20|[[:space:]])+select|or(\+|%20|[[:space:]])+1=1|sleep(\(|%28)|benchmark(\(|%28)|;|%3b|\||%7c|%60|\$\(|%24%28)' /var/log/apache2/access.log | awk '{print $1}' | sort | uniq -c | sort -nr | head -20
         13 203.0.113.45
          2 198.51.100.23
          1 66.249.66.1
          1 52.167.144.1

    Matches and counts indicate likelihood, not certainty, so confirm intent by reviewing full request context before applying permanent blocks.