2.35 million HTTP requests. What were the bots looking for on the server, and what did Fail2ban change?
A technical case study covering nearly 11 months of traffic on Apache2: from scans for .env files and webshells, through signatures of known CVEs, to the implementation of security measures and an assessment of their effectiveness.
Every server exposed to the Internet will, sooner or later, be scanned. Bots don’t need to know which application is running on port 443; they simply send a series of requests, check common paths, look for exposed files, and test for known vulnerabilities. A quick analysis of an access.log file is enough to reveal just how automated and widespread this phenomenon is.
What does the actual scanning of a public web server look like, what are these automated bots searching for, and how can such attacks be mitigated using Apache2, Fail2ban, and iptables?
The information and conclusions presented below are based on an analysis of logs from a production server, covering a period of nearly 11 months.What do server responses really tell us?
Web applications and websites function by receiving a request from a client's web browser, processing that request on the server, and returning a response. In cybersecurity analysis, we must pay attention to the requests sent to a server, how the server responds to them, and what it returns as a response.
Responses are divided into several categories:
- informational (100 – 199)
- successful (200 – 299)
- redirections (300 – 399)
- client-side errors (400 – 499)
- server-side errors (500 – 599)
The full specification can be found here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status
Between December 2025 and October 2026, the server returned over 1.45 million 404 responses—indicating an error when an attempt was made to access an invalid URL. From a security perspective, some of these responses may be benign; for instance, a browser might have attempted to load a missing favicon.ico file or a missing css/js file. Such occurrences generally do not constitute a security incident.
However, when a single source (IP address) checks paths such as /.env, /.git/config,
PHP files typically associated with webshells, and PHPUnit endpoints within a short period, it indicates a potential attack.
Most Frequently Targeted Vulnerabilities
The analyzed log was dominated by two types of attacks: searching for passwords and other access credentials, and scanning for WordPress-related targets.
The categories below overlap; therefore, they cannot be summed, as doing so would be misleading.
.env files and exposed secrets
The most common activity involved checking paths that, in a misconfigured deployment, could reveal database passwords, API keys, mail configurations, or cloud credentials. In the complete dataset, /.env appeared
16 545 times.
Other examples of such files are shown below.
/.env
/.env.local
/.env.production
/.env.bak
/api/.env
/backend/.env
/.aws/credentials
/secrets.json
/terraform.tfstate
This mechanism does not require an advanced exploit; sometimes, an incorrect DocumentRoot,
a leftover backup file, or misconfigured permissions are enough. The best protection is to store passwords/tokens
outside the public file tree or within server environment variables, and to restrict access at both the application and web server levels.
WordPress and forgotten installations
The statistics show 227 852 requests identified as related to WordPress. Bots searched for
installers, login pages, XML-RPC endpoints, configuration files, and plugin directories. They were not limited to the domain's root directory; they also scanned paths such as /old/, /blog/, /wordpress/, and /test/.code>.
The most frequently checked paths are listed below.
/wp-admin/install.php
/wp-login.php
/xmlrpc.php
/wp-config.php.bak
/wp-content/plugins/hellopress/wp_filemanager.php
/wordpress/wp-includes/wlwmanifest.xml
Within the full dataset, 6 323 requests were recorded for /wp-admin/install.php, and 3 902 for
/xmlrpc.php.
Searching for existing webshells
The logs show 27 167 matches for patterns associated with backdoors and webshells. Some bots do not look for software vulnerabilities; instead, they search for signs that someone else has already compromised the system and left behind tools for remote access—for example:
/shell.php
/wso.php
/alfa.php
/alfashell.php
/ioxi-o.php
/adminfuns.php
The mere presence of a filename does not confirm the existence of malware—especially if the log shows a 404 error. However, if such a file actually exists, you should verify its content, modification history, and whether it belongs to your legitimate deployment. It could be a remnant of a successful attack.
Exposed Git repositories
I have classified 20 413 requests as attempts to access Git resources. Files such as
/.git/config or /.git/HEAD should not be publicly accessible. If CI/CD mechanisms—for example—are misconfigured, these files could make it easier to reconstruct the source code and gain insight into the application's structure and security measures.
Signatures of specific vulnerabilities
Not every scan can be mapped to a specific CVE identifier. However, some requests correspond to characteristic attempts to exploit known historical vulnerabilities.
| Signature / CVE | What is being checked? | Interpretation |
|---|---|---|
| CVE-2017-9841 | eval-stdin.php in PHPUnit |
Risk of code execution if the vulnerable file is publicly accessible. |
| CVE-2024-4577 | auto_prepend_file=php://input |
PHP-CGI pattern; a specific CVE relates to particular Windows configurations. |
| CVE-2021-3129 | /_ignition/execute-solution |
Identification of vulnerable Laravel Ignition configurations. |
| CVE-2022-22947 | /actuator/gateway/routes |
Identification of Spring Cloud Gateway; not proof of a complete exploit. |
As part of the analysis, I categorized 16 989 requests related to PHPUnit and 1 843 related to PHP-CGI.
Practical analysis of Apache2 logs
Using the standard Apache combined log format, we can quickly extract requests, methods, and HTTP status codes. Example entry (synthetic, with a sample IP address):
xxx.xxx.xxx.xxx - - [19/May/2026:10:15:21 +0200] "GET /.env HTTP/1.1" 404 285 "-" "curl/8.0"
Most active months
Following the implementation of Fail2ban in July 2026, the number of suspicious requests reaching Apache dropped significantly. The full monthly data is provided below.
| Month | Total HTTP requests | Suspicious requests | Share |
|---|---|---|---|
| 2025-12* | 110 583 | 43 590 | 39.4% |
| 2026-01 | 163 915 | 57 917 | 35.3% |
| 2026-02 | 409 056 | 62 573 | 15.3% |
| 2026-03 | 322 254 | 84 460 | 26.2% |
| 2026-04 | 273 416 | 73 828 | 27.0% |
| 2026-05 | 261 759 | 85 796 | 32.8% |
| 2026-06 | 370 994 | 80 880 | 21.8% |
| 2026-07 / installation and configuration of FAIL2BAN | 109 697 | 18 827 | 17.2% |
| 2026-08 | 105 620 | 17 832 | 16.9% |
| 2026-09 | 126 257 | 26 228 | 20.8% |
| 2026-10* | 103 627 | 18 596 | 17.9% |
The monthly average from March to June was approximately 81.2 thousand suspicious requests. In July and August, these figures dropped to around 18 thousand. This is consistent with how firewall-level blocking works: blocked connections do not subsequently appear in the logs.Apache.
Protection: Fail2ban, Apache2, and iptables
Fail2ban is a useful security layer that analyzes logs, identifies patterns, and can block traffic sources either temporarily or permanently.
Example filter for sensitive files
File apache-sensitive-data.conf:
[Definition]
failregex = ^<HOST> \S+ \S+ \[[^\]]+\] "(?:GET|POST|HEAD) /(?:\.env(?:[.? /]|$)|\.git/(?:config|HEAD)(?:[? ]|$))[^\"]*" (?:200|403|404) \d+ .*$
ignoreregex =
This is an example of a narrow filter for the standard combined log format.
Example iptables jail configuration
File /apache-sensitive-data.local:
[apache-sensitive-probes]
enabled = true
filter = apache-sensitive-probes
logpath = /var/log/apache2/access.log
backend = auto
port = http,https
banaction = iptables-multiport
maxretry = 2
findtime = 600
bantime = 86400
Two matches within 10 minutes can trigger a 24-hour ban. Naturally, these thresholds should be adjusted based on traffic volume, monitoring sources, the presence of a reverse proxy, and the acceptable risk of false positives.
Conclusions from the analysis
What should be implemented alongside Fail2ban?
- Keep passwords and other access credentials outside the
DocumentRoot. Block public access to.envfiles, backups, repositories, and key files. - Do not expose testing tools. A production server should not provide access to unnecessary tools like PHPUnit, debuggers, or profilers.
- Regularly update dependencies. This applies especially to PHP, CMS platforms, plugins, and libraries.
Options -Indexes in the appropriate
context./robots.txt, /favicon.ico, or triggering standard 404 errors.
How to measure the effectiveness of your protection?
| Metric | What it tells us |
|---|---|
| Suspicious requests per day | The volume of traffic reaching Apache. |
| Unique sources | Whether traffic originates from a few scanners or many different addresses. |
| Fail2ban bans and re-bans | Whether rules are being triggered and if sources are returning. |
| 200 responses for sensitive paths | Which events require manual verification. |
| False positive rate | Whether the protection is negatively affecting legitimate users. |
A server does not need to be the target of a specific attacker to face daily attempts to exploit known vulnerabilities. Protection begins with observation: you need to know what is being scanned, what is responding, and which security measures are actually working.