flowd / phirewall
A PHP Firewall and rate limiter based on PSR-7 and PSR-15 middleware (safelists, blocklists, throttles, fail2ban)
Requires
- php: >=8.2
- psr/event-dispatcher: ^1.0
- psr/http-factory: ^1.1
- psr/http-message: ^1.1 || ^2.0
- psr/http-server-handler: ^1.0
- psr/http-server-middleware: ^1.0
- psr/simple-cache: ^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.89
- infection/infection: ^0.29
- nyholm/psr7: ^1.8
- phpstan/phpstan: ^1.12
- phpunit/phpunit: ^11.5
- rector/rector: ^1.2
Suggests
- ext-apcu: Optional: use ApcuCache for fast in-process counters (enable with apc.enable_cli=1 for CLI)
- predis/predis: Optional: use RedisCache for distributed counters
This package is not auto-updated.
Last update: 2026-03-23 22:48:17 UTC
README
Protect your PHP application from brute force, DDoS, SQL injection, XSS, and bot attacks with a single middleware.
Phirewall is a PSR-15 middleware that provides comprehensive application-layer protection. It's lightweight, framework-agnostic, and easy to configure.
Why Phirewall?
- Simple Setup - Add protection in minutes with sensible defaults
- Multiple Attack Vectors - Rate limiting, brute force protection, OWASP rules, bot detection
- Framework Agnostic - Works with any PSR-15 compatible framework (Laravel, Symfony, Slim, Mezzio, etc.)
- Production Ready - Redis support for multi-server deployments
- Observable - PSR-14 events for logging, metrics, and alerting
Quick Start
composer require flowd/phirewall
use Flowd\Phirewall\Config; use Flowd\Phirewall\Middleware; use Flowd\Phirewall\KeyExtractors; use Flowd\Phirewall\Store\InMemoryCache; // Create the firewall $config = new Config(new InMemoryCache()); // Allow health checks to bypass all rules $config->safelist('health', fn($req) => $req->getUri()->getPath() === '/health'); // Block common scanner paths $config->blocklist('scanners', fn($req) => str_starts_with($req->getUri()->getPath(), '/wp-admin')); // Rate limit: 100 requests per minute per IP $config->throttle('api', limit: 100, period: 60, key: KeyExtractors::ip()); // Ban IP after 5 failed logins in 5 minutes $config->fail2ban('login', threshold: 5, period: 300, ban: 3600, filter: fn($req) => $req->getHeaderLine('X-Login-Failed') === '1', key: KeyExtractors::ip() ); // Add to your middleware stack $middleware = new Middleware($config);
Add the middleware to your PSR-15 pipeline. All requests will be evaluated against your rules before reaching your application.
Try It Now
Run one of the included examples to see Phirewall in action:
# Basic setup demo php examples/01-basic-setup.php # See brute force protection php examples/02-brute-force-protection.php # Test SQL injection blocking php examples/04-sql-injection-blocking.php # Full production setup php examples/08-comprehensive-protection.php
Examples
The examples/ folder contains runnable examples:
| # | Example | Description |
|---|---|---|
| 01 | basic-setup | Minimal configuration to get started |
| 02 | brute-force-protection | Fail2Ban-style login protection |
| 03 | api-rate-limiting | Tiered rate limits for APIs |
| 04 | sql-injection-blocking | OWASP-style SQLi detection |
| 05 | xss-prevention | Cross-Site Scripting protection |
| 06 | bot-detection | Scanner and malicious bot blocking |
| 07 | ip-blocklist | File-backed IP/CIDR blocklists |
| 08 | comprehensive-protection | Production-ready multi-layer setup |
| 09 | observability-monolog | Event logging with Monolog |
| 10 | observability-opentelemetry | Distributed tracing with OpenTelemetry |
| 11 | redis-storage | Redis backend for multi-server deployments |
| 12 | apache-htaccess | Apache .htaccess IP blocking |
| 13 | benchmarks | Storage backend performance comparison |
| 14 | owasp-crs-files | Loading OWASP CRS rules from files |
| 15 | in-memory-pattern-backend | Configuration-based CIDR/IP blocklists |
| 16 | allow2ban | Hard volume cap with auto-ban |
| 17 | known-scanners | Block known attack tools and vulnerability scanners |
| 18 | trusted-bots | Trusted bot verification via reverse DNS |
| 19 | header-analysis | Suspicious headers detection |
| 20 | rule-benchmarks | Firewall rule performance benchmarks |
| 21 | sliding-window | Sliding window rate limiting |
| 22 | multi-throttle | Multi-window burst + sustained rate limiting |
| 23 | dynamic-limits | Role-based dynamic throttle limits |
| 24 | pdo-storage | PdoCache with SQLite, MySQL, PostgreSQL |
| 25 | track-threshold | Track with optional threshold and thresholdReached flag |
Features
Protection Layers
| Feature | Description |
|---|---|
| Safelists | Bypass all checks for trusted requests (health checks, internal IPs) |
| Blocklists | Immediately deny suspicious requests (403) |
| Throttling | Fixed and sliding window rate limiting by IP, user, API key, or custom key (429) with dynamic limits and multiThrottle |
| Fail2Ban | Auto-ban after repeated failures |
| Allow2Ban | Hard volume cap -- ban after too many total requests |
| Track with Threshold | Passive counting with optional alert threshold |
| OWASP CRS | SQL injection, XSS, and PHP injection detection |
| Pattern Backends | File/Redis-backed blocklists with IP, CIDR, path, and header patterns |
Observability
- PSR-14 Events -
SafelistMatched,BlocklistMatched,ThrottleExceeded,Fail2BanBanned,TrackHit,FirewallError - Fail-Open by Default - Cache outages don't take down the application; errors dispatched via PSR-14
- Diagnostics Counters - Per-rule statistics for monitoring
- Standard Headers -
X-RateLimit-*,Retry-After,X-Phirewall-*
Storage Backends
| Backend | Use Case |
|---|---|
InMemoryCache |
Development, testing, single requests |
ApcuCache |
Single-server production |
RedisCache |
Multi-server production |
PdoCache |
SQL-backed persistence (MySQL, PostgreSQL, SQLite) |
Documentation
For detailed documentation, see the docs/ folder:
- Getting Started - Step-by-step setup guide
- Common Attacks - Protection recipes for 10+ attack types
- Configuration - Complete API reference
- Storage Backends - Redis, APCu, PDO, and custom backends
- Pattern Backends - IP, CIDR, path, and header blocklists
- OWASP CRS - Loading and customizing rules
- Observability - Events, logging, and monitoring
- Infrastructure Adapters - Apache .htaccess integration
Installation
composer require flowd/phirewall
Optional Dependencies
# For Redis-backed distributed counters (multi-server) composer require predis/predis # For Monolog logging integration composer require monolog/monolog
APCu: Enable the PHP extension and set apc.enable_cli=1 for CLI testing.
Response Headers
When a request is blocked:
| Header | Description |
|---|---|
X-Phirewall |
Block type: blocklist, throttle, fail2ban |
X-Phirewall-Matched |
Rule name that triggered |
Retry-After |
Seconds until rate limit resets (429 only) |
Enable $config->enableRateLimitHeaders() for standard X-RateLimit-* headers.
Client IP Behind Proxies
When behind load balancers or CDNs, use TrustedProxyResolver:
use Flowd\Phirewall\Http\TrustedProxyResolver; use Flowd\Phirewall\KeyExtractors; $resolver = new TrustedProxyResolver([ '10.0.0.0/8', // Internal network '172.16.0.0/12', // Docker ]); $config->throttle('api', limit: 100, period: 60, key: KeyExtractors::clientIp($resolver) );
Custom Responses
Customize blocked responses while keeping standard headers:
$config->blocklistedResponse(function (string $rule, string $type, $req) { return new Response(403, ['Content-Type' => 'application/json'], json_encode(['error' => 'Blocked', 'rule' => $rule]) ); }); $config->throttledResponse(function (string $rule, int $retryAfter, $req) { return new Response(429, ['Content-Type' => 'application/json'], json_encode(['error' => 'Rate limited', 'retry_after' => $retryAfter]) ); });
OWASP Core Rule Set
Load OWASP-style rules for SQL injection, XSS, and more:
use Flowd\Phirewall\Owasp\SecRuleLoader; $rules = SecRuleLoader::fromString(<<<'CRS' SecRule ARGS "@rx (?i)\bunion\b.*\bselect\b" "id:942100,phase:2,deny,msg:'SQLi'" SecRule ARGS "@rx (?i)<script[^>]*>" "id:941100,phase:2,deny,msg:'XSS'" CRS); $config->owaspBlocklist('owasp', $rules);
Or load from files:
$rules = SecRuleLoader::fromDirectory('/path/to/crs-rules');
See 04-sql-injection-blocking.php and 05-xss-prevention.php for complete examples.
Real-World Recipes
API Rate Limiting
// Global limit $config->throttle('global', limit: 1000, period: 60, key: KeyExtractors::ip()); // Burst + sustained rate limiting with multiThrottle $config->throttles->multi('api', [ 1 => 5, // 5 req/s burst 60 => 200, // 200 req/min sustained ], KeyExtractors::ip()); // Dynamic limits based on user role $config->throttles->add('user', fn($req) => $req->getHeaderLine('X-Plan') === 'pro' ? 5000 : 100, 60, KeyExtractors::header('X-User-Id') );
Login Protection
// Throttle login attempts $config->throttle('login', limit: 10, period: 60, key: function($req) { return $req->getUri()->getPath() === '/login' ? $req->getServerParams()['REMOTE_ADDR'] : null; }); // Ban after failures $config->fail2ban('login-ban', threshold: 5, period: 300, ban: 3600, filter: fn($req) => $req->getHeaderLine('X-Login-Failed') === '1', key: KeyExtractors::ip() );
Bot Detection
$scanners = ['sqlmap', 'nikto', 'nmap', 'burp', 'dirbuster']; $config->blocklist('scanners', function($req) use ($scanners) { $ua = strtolower($req->getHeaderLine('User-Agent')); foreach ($scanners as $scanner) { if (str_contains($ua, $scanner)) return true; } return false; });
Development
# Run tests composer test # Run PdoCache tests against SQLite, MySQL, and PostgreSQL (requires Docker) composer test:database # Or directly: ./bin/test-databases.sh --keep (keeps containers running) # Run performance benchmarks only (no coverage, Xdebug disabled) XDEBUG_MODE=off PHIREWALL_RUN_BENCHMARKS=1 vendor/bin/phpunit --group performance --no-coverage # Fix code style composer fix # Mutation testing composer test:mutation
Sponsors
This project received funding from TYPO3 Association through its Community Budget program.
License
Dual licensed under LGPL-3.0-or-later and proprietary. See LICENSE for details.