DEV Community

Cover image for Reading access logs: scans that end in 404, and the 200 responses worth a second look
Susumu Takahashi
Susumu Takahashi

Posted on Originally published at en.wpmm.jp

Reading access logs: scans that end in 404, and the 200 responses worth a second look

A public site receives requests every day from visitors that are just looking for something. Most of them ask for files that do not exist and end in a 404. Without a habit of reading the log, though, the one line that deserves attention is easy to miss among them. This article describes how to look at a WordPress access log in a read-only way, centered on separating routine probing from what to confirm. Reading logs does not make a site safe. It gives you more clues to notice things.

What an access log records

A web server records each request as a line. In the common "combined" format, a line looks roughly like this:

client - - [time] "METHOD path protocol" status bytes "referrer" "user-agent"
Enter fullscreen mode Exit fullscreen mode

Note: The status is the HTTP response code. 200 means the server returned the content; 404 means nothing was found. See the HTTP status code article for more.

Where logs live and how you download them depends on your hosting provider, so check its documentation first. Also, old logs are rotated away after a while, so confirm that the period you want to inspect still exists.

Routine probing mostly ends in 404

The first time you read a public site's log, you may be surprised by how many requests are for paths you never created. Names that look like configuration backups, diagnostic pages that print environment details, or entry points of admin tools: these are mechanical guesses at "files that might exist" and are not necessarily aimed at your site.

Most of them end in 404 because the file does not exist. So the presence of probing in the log is not, by itself, abnormal. Two things are worth keeping apart:

  • Probing arrives: routine for a public site. No reason to panic on this alone
  • Probing gets a 200: the requested thing may exist and its content may have been returned. This is what to confirm

Look at the overall distribution first

Before reading single lines, count the statuses. This is a read-only command (access.log stands for a downloaded log file):

awk '{print $9}' access.log | sort | uniq -c | sort -rn
Enter fullscreen mode Exit fullscreen mode

A typical shape is mostly 200, then 301/304, with a smaller share of 404, but it varies a lot by site. What matters is knowing your usual shape. A sudden rise in 404s or a visible share of 5xx is a reason to look closer.

Skim the paths that returned 404

Then list the 404 paths, most frequent first:

awk '$9==404 {print $7}' access.log | sort | uniq -c | sort -rn | head -30
Enter fullscreen mode Exit fullscreen mode

What to look for:

  • Names that should not exist on your site at all are most likely routine probing. Just skim them
  • URLs that used to exist on your site near the top suggest broken links or a missed redirect, which is an operational improvement
  • Many requests to one path in a short time are worth noting from a server-load point of view

The thing not to miss: a suspicious path that returned 200

Here is the main point. The 404 list records probes that missed. Probes that hit are recorded as 200. Count PHP requests that returned 200:

awk '$9==200 && $7 ~ /\.php/ {print $7}' access.log | sed 's/?.*//' | sort | uniq -c | sort -rn | head -30
Enter fullscreen mode Exit fullscreen mode

A normal WordPress site shows a limited set of PHP files (index.php, wp-login.php, wp-cron.php, xmlrpc.php, admin-ajax.php, and so on, depending on the setup). For any other PHP name returning 200, ask whether you can explain what that file is.

Two combinations deserve particular attention:

  • A PHP file under uploads returning 200: that directory is for images and similar files, so PHP running there is not what normal operation expects. Combine this with the "PHP in uploads" check from the compromise-check article and look at whether the file exists
  • A POST request to an unfamiliar PHP file returning 200: also worth confirming
awk '$6=="\"POST\"" && $9==200 {print $7}' access.log | sed 's/?.*//' | sort | uniq -c | sort -rn | head -30
Enter fullscreen mode Exit fullscreen mode

Neither finding means a site has been compromised. A plugin may ship its own PHP, or you may have forgotten a diagnostic file you placed yourself. The order of confirmation is: what is the file, who placed it and when, and is its content what you expect. If you cannot tell, record the current state and preserve it before deleting anything.

Why a 200 alone is not enough

Reading logs has limits:

  • Some setups return 200 for pages that do not exist. Depending on the theme, plugins, or server settings, a missing path may return an error-looking page with a 200 (a soft 404). Then a 200 line is not necessarily a hit. Open the path and see what actually comes back
  • Behind a CDN or reverse proxy, the client address may be the provider's. You may not be able to tell who the requester was
  • Absence from the log does not mean it did not happen. Retention, recorded fields, and what you can download are limited. Do not read "not in the log" as "nothing occurred"
  • A log records; it does not protect. Reading alone defends nothing. It matters when it leads to updates, setting reviews, and checks

A minimal habit

You do not need to read everything daily. One sustainable shape:

  1. At a regular interval such as weekly, count the status breakdown and compare it with your usual shape
  2. List PHP files that returned 200 and that you do not recognize
  3. For anything unclear, check whether the file exists, its modification time, and its content
  4. Leave a short note of what you checked and decided, so you can explain it next time

As the log level and rotation article describes, a log is valuable when it stays available to review later. Decide retention and review frequency to fit your own operation.

Summary

  • Probing requests to a public site are routine. Most end in 404 and are not abnormal in themselves
  • What to confirm is whether something probed actually exists and returned 200, especially PHP under uploads and POSTs to unfamiliar PHP files
  • A 200 line only suggests a possible hit, and soft-404 setups exist. Decide by the file's existence and content
  • Logs are material for noticing, not evidence of safety. Use them together with updates, checks, and setting reviews

Reading the access log is a status check before acting. Shrinking entry points, as in the attack surface checklist, and checking how those entry points look in the log complement each other.

Top comments (0)