DEV Community

Log Audit
Log Audit

Posted on

5 Security Patterns You Should Be Scanning For in Your Apache/Nginx Logs

Every web server generates logs. Most teams ignore them until something breaks. But your access logs are a goldmine of security intelligence — if you know what to look for.

After analyzing millions of log lines across production systems, here are 5 attack patterns that show up consistently. Each one is easy to detect, and catching them early can save you from a breach.


1. Path Traversal Attempts

What it looks like:

192.168.1.105 - - [24/Mar/2026:03:14:22 +0000] "GET /images/../../etc/passwd HTTP/1.1" 400 0
10.0.0.33 - - [24/Mar/2026:03:14:45 +0000] "GET /static/%2e%2e/%2e%2e/etc/shadow HTTP/1.1" 403 0
203.0.113.50 - - [24/Mar/2026:03:15:01 +0000] "GET /download?file=../../../proc/self/environ HTTP/1.1" 200 1245
Enter fullscreen mode Exit fullscreen mode

Why it matters: Attackers use ../ sequences (or URL-encoded variants like %2e%2e) to escape the web root and read system files. The third example is the scariest — it returned a 200, meaning the server actually served the file.

What to scan for:

  • Requests containing .. or %2e%2e in the URI
  • Any 200 responses to paths referencing /etc/, /proc/, or system directories
grep -iE "(\.\\./|%2e%2e|%252e)" /var/log/nginx/access.log
Enter fullscreen mode Exit fullscreen mode

2. Brute Force Detection (Repeated 401s)

What it looks like:

198.51.100.14 - admin [24/Mar/2026:04:00:01 +0000] "POST /admin/login HTTP/1.1" 401 112
198.51.100.14 - admin [24/Mar/2026:04:00:01 +0000] "POST /admin/login HTTP/1.1" 401 112
198.51.100.14 - admin [24/Mar/2026:04:00:02 +0000] "POST /admin/login HTTP/1.1" 401 112
198.51.100.14 - admin [24/Mar/2026:04:00:03 +0000] "POST /admin/login HTTP/1.1" 200 3842
Enter fullscreen mode Exit fullscreen mode

Why it matters: A burst of 401 responses from the same IP followed by a 200 is the textbook signature of a successful brute force attack.

What to scan for:

  • More than 5-10 failed auth attempts (401/403) from a single IP within a short window
  • A 200 response after a series of failures (indicates successful compromise)
awk \ == 401 {print \} /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
Enter fullscreen mode Exit fullscreen mode

3. XSS Attempts in Query Strings

What it looks like:

10.0.0.88 - - [24/Mar/2026:05:22:10 +0000] "GET /search?q=<script>alert(document.cookie)</script> HTTP/1.1" 200 4521
172.16.0.5 - - [24/Mar/2026:05:22:33 +0000] "GET /profile?name=%3Cimg%20src%3Dx%20onerror%3Dalert(1)%3E HTTP/1.1" 200 3200
Enter fullscreen mode Exit fullscreen mode

Why it matters: XSS payloads in URLs mean someone is actively testing whether your application sanitizes input. If those requests return 200, your app may be vulnerable.

What to scan for:

  • <script>, <svg, <img tags in query parameters
  • Event handlers like onerror=, onload=
  • URL-encoded versions of the above
grep -iE "(<script|%3Cscript|onerror=|onload=|javascript:)" /var/log/nginx/access.log
Enter fullscreen mode Exit fullscreen mode

4. Sensitive File Access (.env, .git, wp-admin probing)

What it looks like:

45.33.32.156 - - [24/Mar/2026:06:10:05 +0000] "GET /.env HTTP/1.1" 200 890
45.33.32.156 - - [24/Mar/2026:06:10:06 +0000] "GET /.git/config HTTP/1.1" 200 234
45.33.32.156 - - [24/Mar/2026:06:10:07 +0000] "GET /wp-admin/ HTTP/1.1" 302 0
45.33.32.156 - - [24/Mar/2026:06:10:08 +0000] "GET /phpinfo.php HTTP/1.1" 200 52314
Enter fullscreen mode Exit fullscreen mode

Why it matters: This is an automated scanner probing for exposed config files. A .env file returning 200 is a critical incident — it likely contains database credentials, API keys, and secrets.

What to scan for:

  • Requests for dotfiles: .env, .git/, .htaccess, .aws/credentials
  • Admin panel probing: /wp-admin, /phpmyadmin, /adminer
  • Any of the above returning a 200 status code
grep -iE "(\.env|\.git|wp-admin|phpinfo|\.aws|server-status)" /var/log/nginx/access.log
Enter fullscreen mode Exit fullscreen mode

5. API Keys and Tokens Leaked in URLs

What it looks like:

10.0.0.22 - - [24/Mar/2026:07:30:15 +0000] "GET /api/data?api_key=sk_live_4eC39HqLyjWDarjtT1zdp7dc HTTP/1.1" 200 1580
10.0.0.22 - - [24/Mar/2026:07:30:16 +0000] "GET /webhook?token=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx HTTP/1.1" 200 40
Enter fullscreen mode Exit fullscreen mode

Why it matters: This is not an external attack — it is your own application leaking secrets. API keys passed as query parameters get logged in plain text in access logs, browser history, and analytics tools.

What to scan for:

  • Query parameters named api_key, token, access_token, secret
  • Known key patterns: sk_live_, ghp_, AKIA (AWS), xoxb- (Slack)
grep -iE "(api_key=|token=|access_token=|secret=|sk_live_|ghp_|AKIA)" /var/log/nginx/access.log
Enter fullscreen mode Exit fullscreen mode

Immediate action: If you find leaked keys, rotate them immediately.


Automate It

Scanning for these patterns manually works, but it does not scale. You have to check every log rotation, across every server, every day.

I built LogAudit to automate exactly this — it scans your nginx/Apache logs against these patterns (and more), flags issues by severity, and gives you a compliance score. Free tier available if you want to try it.

But regardless of what tools you use — start scanning. These patterns are hitting your servers right now.

Top comments (0)