ct accordingly.
Introducing xZeroProtect
xZeroProtect is a lightweight, file-based PHP 8 firewall with zero external dependencies. No Redis, no MySQL, no external services β everything is stored on disk.

Install it via Composer:
composer require webrium/xzeroprotect
Enter fullscreen mode Exit fullscreen mode
The simplest possible integration β two lines at the top of your index.php:
<?php
require 'vendor/autoload.php';
use Webrium\XZeroProtect\XZeroProtect;
XZeroProtect::init()->run();
Enter fullscreen mode Exit fullscreen mode
That's it. Default rules activate immediately.
What It Actually Does
1. Blocks Known Scanner User Agents
The library maintains a list of User-Agent signatures associated with scanning tools and attack frameworks: sqlmap, nikto, nessus, acunetix, masscan, dirbuster, gobuster, feroxbuster, wfuzz, ffuf, hydra, metasploit, and many others. Requests from these tools are blocked before they get anywhere near your application logic.
You can extend or trim this list:
$firewall->patterns->addAgent('custom-bad-bot');
$firewall->patterns->removeAgent('curl'); // if your API clients use curl legitimately
Enter fullscreen mode Exit fullscreen mode
2. Blocks Sensitive Path Probes
Scanners routinely probe paths that don't exist in modern PHP apps β and shouldn't. xZeroProtect blocks requests targeting:
- CMS admin panels:
/wp-admin, /wp-login, /administrator
- Config files:
.env, .git, .htpasswd, web.config
- Database tools:
/phpmyadmin, /adminer, /dbadmin
- Dangerous files:
.sql, .bak, .backup, dump.sql
- Web shells:
shell.php, c99.php, r57.php
- Path traversal:
../, ..%2f, %2e%2e
If you're running a fully-routed PHP app with no .php files in URLs, you can add that as a rule too:
$firewall->patterns->addPath('.php');
Enter fullscreen mode Exit fullscreen mode
3. Scans Payloads for Attack Signatures
The payload scanner inspects GET parameters, POST body, raw input, and cookies using compiled regular expressions. It detects:
- SQL injection variants:
UNION SELECT, DROP TABLE, SLEEP(), BENCHMARK()
- XSS:
<script tags, inline event handlers, javascript: protocol
- Path traversal:
../
- PHP code injection:
system(), exec(), eval(base64_decode(...))
- LFI:
/etc/passwd, /etc/shadow
- Remote file inclusion
- Command injection via shell metacharacters
4. Rate Limiting Without Redis
The rate limiter uses a sliding-window counter stored per-IP on disk. No Redis or Memcached required.
$firewall = XZeroProtect::init([
'rate_limit' => [
'max_requests' => 60,
'per_seconds' => 60,
],
]);
Enter fullscreen mode Exit fullscreen mode
When a client exceeds the limit, they accumulate violations. After enough violations, they get temporarily banned. After enough bans, permanently.
5. Doesn't Block Legitimate Crawlers
This is important: Google, Bing, and other legitimate search engines should not get caught in your firewall. xZeroProtect handles this with double-DNS verification for major crawlers:
- Resolve the visitor's IP to a hostname via reverse DNS
- Confirm the hostname ends with the expected suffix (e.g.
.googlebot.com)
- Re-resolve that hostname back to an IP
- Confirm the re-resolved IP matches the original
Anyone can fake a User-Agent string. DNS cannot be faked. This is the same verification method recommended by Google and Bing in their official documentation.
Learning Mode: Test Before You Block
Before switching to production, you can run in learning mode. All threats are detected and logged, but nothing is blocked. This lets you review what would have been caught and tune your configuration accordingly.
// During tuning
XZeroProtect::init(['mode' => 'learning'])->run();
// Once satisfied
XZeroProtect::init(['mode' => 'production'])->run();
Enter fullscreen mode Exit fullscreen mode
This is genuinely useful. Running learning mode for a few days on a real server will show you the volume and variety of automated probes hitting your application β and help you avoid accidentally blocking legitimate traffic.
Apache Integration for Zero-PHP-Overhead Blocking
For permanently banned IPs, you can optionally write them into .htaccess so Apache rejects the connection before PHP even starts:
$firewall = XZeroProtect::init([
'apache_blocking' => true,
'htaccess_path' => __DIR__ . '/.htaccess',
]);
Enter fullscreen mode Exit fullscreen mode
This is particularly useful for confirmed attackers who you never want to reach your application layer again.
Custom Rules
If the built-in modules don't cover a specific case, you can register your own:
use Webrium\XZeroProtect\RuleResult;
$firewall->rules->add('no-php-extension', function ($request) {
if (str_ends_with($request->path(), '.php')) {
return RuleResult::block('PHP extension not valid on this server');
}
return RuleResult::pass();
});
Enter fullscreen mode Exit fullscreen mode
Rules can block, log without blocking, or pass. They run in priority order and can be enabled, disabled, or removed at runtime.
What xZeroProtect Is Not
To be clear about expectations: this is not a commercial WAF. It won't stop a sophisticated, targeted attack from a determined adversary. It doesn't do deep packet inspection, it doesn't have a threat intelligence feed, and it doesn't update its rule set automatically.
What it does is significantly reduce the automated noise that hits most PHP applications β the opportunistic scanners, the script-kiddie tools, the mass-probing bots β with a setup that takes minutes and has no infrastructure dependencies.
For many projects, especially those on shared hosting or small VPS instances where you don't have the option to configure Nginx rate limiting or cloud WAF rules, this is a practical and useful layer of defense.
Getting Started
composer require webrium/xzeroprotect
Enter fullscreen mode Exit fullscreen mode
Full documentation and source: github.com/webrium/xzeroprotect
Start in learning mode, review your logs, tune your configuration, then flip to production.
Have feedback or found an edge case? Issues and PRs are welcome on GitHub.