adrianlaraperez88 / load-balancer
Enterprise Load Balancer and Reverse Proxy Gateway for Laravel
Package info
github.com/adrianlaraperez88/load-balancer
Type:project
pkg:composer/adrianlaraperez88/load-balancer
Requires
- php: ^8.3
- guzzlehttp/guzzle: ^7.8
- guzzlehttp/promises: ^2.0
- laravel/framework: ^12.0|^13.0
- laravel/sanctum: ^4.0
Requires (Dev)
- fakerphp/faker: ^1.24
- mockery/mockery: ^1.6
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-15 04:33:26 UTC
README
A turnkey, high-resilience standalone Load Balancer and Reverse Proxy application built on Laravel 12/13 and PHP 8.3. Specifically architected for high-throughput enterprise deployments featuring 1 Front Gateway Server (app03) + $N$ Backend Processing Servers, supporting both Windows Server + IIS 10 and Docker / Linux Containers with zero-trust security and manual MSSQL database provisioning.
Table of Contents
- Documentation Library (
docs/) - Key Highlights
- Architecture: 1 Front Gateway (app03) + N Backends
- Installation
- Deployment Option 1: Windows Server + IIS
- Deployment Option 2: Docker & Linux Containers
- Authentication & Secure Communications (Sanctum)
- Interactive Swagger UI & OpenAPI Specification
- Visual Cluster Web Admin Control Center
- Ready-to-Run cURL Commands
- Security & Zero-Trust Hardening
- Artisan CLI Operations
- Balancing Strategies & Sticky Sessions
- Configuration Reference (
.env) - Testing & Quality Assurance
Documentation Library (docs/)
Comprehensive, dedicated operational runbooks and architectural specifications are maintained in the docs/ directory:
| Guide | Description |
|---|---|
| Windows Server & IIS 10 Installation Guide | Step-by-step setup for Windows Server 2016/2019/2022 + IIS 10, FastCGI tuning, URL Rewrite 2.1, HTTP_AUTHORIZATION whitelisting, and NTFS permissions. |
| Shared Server Deployment & Coexistence Guide | Coexistence on a shared Windows/Linux server alongside existing CRM, ERP, and legacy sites using AppPool isolation, Host Name Bindings, and Valkey/DB namespacing. |
| Backend Zero-Downtime Migration Runbook | 4-phase rollout: Permissive mode (strict: false), canary verification, DNS cutover, and strict perimeter lockdown without breaking current integrations. |
| OWASP Top 10 Security Architecture | Complete defensive matrix: SSRF metadata shields, CRLF header injection stripping, atomic anti-replay nonces, and brute-force throttling. |
| Valkey Engine & Performance Architecture | Linux Foundation Valkey 8 integration, sub-millisecond response microcaching (X-Cache: HIT/MISS), keep-alive socket reuse, and 10-node scale benchmark results. |
| Visual Cluster Web Admin Control Center | Operational walkthrough for the dark-mode GUI at /admin/cluster, authentication lock screen, 1-click dynamic node draining, and cluster provisioning. |
Key Highlights
- Pure PHP Reverse Proxy: Pipes payloads, HTTP methods, headers, and responses without requiring IIS ARR (Application Request Routing) or custom Nginx C modules.
- Dual Deployment Ready: Optimized for Windows Server 2016/2019/2022 + IIS 10 as well as Docker / Kubernetes / Linux.
- DBA-Friendly MSSQL Support: Includes self-contained, idempotent T-SQL deployment and rollback scripts for environments where developers cannot run
php artisan migrate. - Zero-Trust Security: HMAC-SHA256 request signing ensures backends cannot be bypassed directly; Windows NTFS-specific traversal protections block alternate data streams and 8.3 short-name exploits.
- Fail-Open Circuit Breakers: Windowed error rates prevent transient blips from knocking out your cluster, while guaranteed minimums prevent thundering herd failure cascades.
- Extreme Scale Analytics: Asynchronous metric logging via
dispatchAfterResponse()with probabilistic sampling handles sustained bursts of traffic.
Architecture: 1 Front Gateway (app03) + N Backends
In this topology, public clients and browser single-page applications (SPAs) only communicate with a single ingress server (app03). The $N$ backend processing nodes (app01, app02, ..., appN) reside in private subnets, execute compute workloads, and stream responses back through app03 to the client.
┌────────────────────────────────────────────────────────┐
│ UI / Browser / Client │
└───────────────────────────┬────────────────────────────┘
│ (All requests hit app03 only)
▼
┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ FRONT GATEWAY SERVER (app03) │
│ │
│ - Public Ingress (e.g., https://app03.yourdomain.com) │
│ - Runs on Windows IIS 10 or Docker Nginx/PHP-FPM │
│ - Mode: Proxy (`LOAD_BALANCER_MODE=proxy`) │
│ - Dynamic Session / Token Forwarding (`Authorization: Bearer` passed intact) │
│ - HMAC Request Signing (`X-LoadBalancer-Signature`, `X-LoadBalancer-Timestamp`) │
│ - Dispatches via Strategy: Round Robin, Weighted, Adaptive Latency (P2C), or Rendezvous Sticky │
└───────────────┬──────────────────────────┬──────────────────────────┬──────────────────────────────────┘
│ │ │
(Private VPC/LAN) (Private VPC/LAN) (Private VPC/LAN)
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Backend app01 │ │ Backend app02 │ │ Backend app0N │
│ (10.0.0.11:8000)│ │ (10.0.0.12:8000)│ │ (10.0.0.1N:8000)│
│ IIS / Docker │ │ IIS / Docker │ │ IIS / Docker │
│ Compute/Process │ │ Compute/Process │ │ Compute/Process │
└──────────────────┘ └──────────────────┘ └──────────────────┘
Installation
Require the package via Composer:
composer require adrianlaraperez88/load-balancer
Turnkey 1-Command Setup Wizard
Run the interactive installation command to set up configuration, HMAC security secrets, database migrations, initial nodes, and admin tokens in under 10 seconds:
php artisan loadbalancer:install
Or for automated headless CI/CD deployments:
php artisan loadbalancer:install --no-interaction --mode=proxy --strategy=round_robin
This single command automatically:
- Publishes
config/load-balancer.php. - Generates a cryptographically strong 64-character HMAC secret key and appends
LOAD_BALANCER_HMAC_SECRETinto.env. - Runs database migrations for
upstream_nodes,ip_rules, andrequest_metrics. - Prompts to register your initial upstream backend nodes.
- Issues a dedicated admin Sanctum token for immediate API & Web GUI administration.
Gateway Reverse Proxy Route Macro
Register the reverse proxy route macro on your ingress gateway server (routes/web.php or routes/api.php):
use Illuminate\Support\Facades\Route; // Captures all incoming web/API paths and transparently proxies to backend nodes: Route::loadBalancer('{any?}');
Note: The
Route::loadBalancer()macro automatically strips stateful session, cookie encryption, and CSRF middleware to avoid intercepting or corrupting backend authentication headers.
Turnkey Backend Protection (1-Line Middleware)
On your backend processing servers (app01, app02, ..., appN), protect incoming routes so they only accept requests originating from the gateway using the pre-registered 'gateway.verify' middleware alias:
use Illuminate\Support\Facades\Route; // Backend servers reject direct requests (401 Unauthorized) and only accept HMAC-signed traffic from app03: Route::middleware('gateway.verify')->group(function () { Route::apiResource('/orders', OrderController::class); Route::get('/users/me', [UserController::class, 'me']); });
Deployment Option 1: Windows Server + IIS
This is the recommended configuration when your organization hosts on Windows Server (2016 / 2019 / 2022) with Internet Information Services (IIS 10) and SQL Server.
1. Web Server & FastCGI Configuration
A battle-tested IIS web.config is provided in config/iis/web.config. Copy it directly to the root of your Laravel public/ directory:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <defaultDocument> <files> <clear /> <add value="index.php" /> </files> </defaultDocument> <rewrite> <rules> <!-- Forward Authorization Header to PHP FastCGI --> <rule name="Pass Authorization Header" stopProcessing="false"> <match url=".*" /> <conditions> <add input="{HTTP_AUTHORIZATION}" pattern="^Bearer (.+)$" /> </conditions> <serverVariables> <set name="HTTP_AUTHORIZATION" value="{C:0}" /> </serverVariables> </rule> <!-- Standard Laravel Front Controller Rewrite --> <rule name="Laravel Front Controller" stopProcessing="true"> <match url="^" ignoreCase="false" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> </conditions> <action type="Rewrite" url="index.php" /> </rule> </rules> </rewrite> <!-- Security: Hide sensitive directories and files --> <security> <requestFiltering> <hiddenSegments> <add segment=".env" /> <add segment="storage" /> <add segment="resources" /> <add segment="composer.json" /> <add segment="composer.lock" /> </hiddenSegments> </requestFiltering> </security> </system.webServer> </configuration>
2. Passing Authorization Headers (Bearer Tokens)
IIS FastCGI strips the Authorization header by default. The rewrite rule above captures it and sets the HTTP_AUTHORIZATION server variable.
If you encounter HTTP Error 500.50 (URL Rewrite Module Error):
- Open IIS Manager.
- Select your site, double-click URL Rewrite.
- In the right Actions pane, click View Server Variables...
- Click Add... and type
HTTP_AUTHORIZATION.
3. Windows NTFS & Path Traversal Protections
Windows IIS uses NTFS, which includes special namespace characteristics that malicious clients can attempt to exploit. The built-in proxy engine automatically rejects attacks before upstream dispatch:
- Alternate Data Streams (
::$DATA): Blocked (HTTP 400 Bad Request). - 8.3 Short Names (
DIR~1): Blocked to prevent bypassing hidden segment rules (HTTP 400 Bad Request). - Trailing Dots and Spaces (
path./): Blocked to prevent NTFS path truncation bypasses (HTTP 400 Bad Request). - Path Traversal (
..,%2e%2e,\): Sanitized and blocked (HTTP 400 Bad Request).
4. MSSQL Database Setup (No Migration Privileges)
When corporate database policies prevent developers from executing php artisan migrate, production-grade idempotent T-SQL scripts are available in database/mssql/:
| Script | Purpose | Execution |
|---|---|---|
01_create_tables.sql |
Creates upstream_nodes, load_balancer_metrics, ip_rules with primary keys, indexes, and constraints |
Run by DBA via SSMS or sqlcmd |
02_seed_initial_cluster.sql |
Idempotently seeds initial cluster (app01, app02) and default routing rules |
Run after table creation |
03_rollback_tables.sql |
Safely drops all tables and constraints if rollback is required | Emergency teardown |
04_create_sanctum_tables.sql |
Creates users and personal_access_tokens tables for Laravel Sanctum authentication |
Run by DBA for API auth |
Executing via SQLCMD:
sqlcmd -S tcp:db-server.internal,1433 -d ProductionDB -U sa -P "YourStrongPassword!" -i database/mssql/01_create_tables.sql sqlcmd -S tcp:db-server.internal,1433 -d ProductionDB -U sa -P "YourStrongPassword!" -i database/mssql/02_seed_initial_cluster.sql sqlcmd -S tcp:db-server.internal,1433 -d ProductionDB -U sa -P "YourStrongPassword!" -i database/mssql/04_create_sanctum_tables.sql
Deployment Option 2: Docker & Linux Containers
For cloud deployments (AWS ECS, GCP Cloud Run, Azure Kubernetes Service, or Linux VMs), an enterprise container stack is provided.
1. Production Docker Architecture
Dockerfile(docker/Dockerfile): Multi-stage build based on PHP 8.3 CLI/FPM with Microsoft ODBC 18 drivers for SQL Server,pdo_sqlsrv,redis,opcache, and Nginx.supervisord(docker/supervisord.conf): Manages PHP-FPM, Nginx, and the background health check daemon concurrently inside the container.nginx.conf(docker/nginx.conf): Configured for high-throughput proxying with 16KB header buffers, 10MB client body limit, gzip compression, and security headers.
2. Quick Start with Docker Compose (Production)
A complete production stack with the load balancer gateway, Valkey in-memory cache, and backend connections is defined in docker-compose.prod.yml:
# Build and launch the containerized load balancer stack: docker compose -f docker-compose.prod.yml up -d --build # View gateway logs: docker compose -f docker-compose.prod.yml logs -f gateway
3. Local Multi-Node Test Cluster (docker-compose.test-cluster.yml)
We provide a complete simulated environment featuring 1 Front Gateway (app03_gateway on port 8080) + 3 Backend Processing Nodes (backend_app01, backend_app02, backend_app04) + Valkey Cache (valkey/valkey:8-alpine):
# Launch the 4-server cluster: docker compose -f docker-compose.test-cluster.yml up -d --build # Test reverse proxy round-robin through app03: curl http://localhost:8080/ curl http://localhost:8080/ # Test data writing and reading through the proxy: curl -X POST http://localhost:8080/api/items \ -H "Content-Type: application/json" \ -H "Authorization: Bearer test-user-token" \ -d '{"message": "Testing DB write through app03"}' # Read back stored data: curl http://localhost:8080/api/items
4. High-Concurrency Scale Benchmark (1 Gateway + 10 Backends)
A multi-node scale testing environment is configured in docker-compose.scale-test.yml and automated via docker/run_scale_benchmark.py:
- 1 Gateway (
app03_gateway): Public ingress on port8080, running PHP-FPM 8.3 (tuned to 64 dynamic worker pools) and Nginx. - 10 Backend Processing Nodes (
backend_app01tobackend_app10): Running on private Docker subnet with ports isolated. - 1 Valkey Cache (
cluster_valkey): Runningvalkey/valkey:8-alpinefor high-throughput node registry and atomic metrics caching.
How to Run the 10-Backend Benchmark & Graphic Generator:
# 1. Start the 1 Gateway + 10 Backend cluster docker compose -f docker-compose.scale-test.yml up -d # 2. Run the timeout validation suite and 10,000-request scale benchmark python docker/run_scale_benchmark.py
Performance Graphic Dashboard:
The test outputs a high-resolution 4-panel visual graphic: docker/performance_report_10nodes.png
Multi-Tier Benchmark Results (10,000 Requests):
| Concurrency Tier | Total Requests | Elapsed Time | Throughput (RPS) | Median Latency (p50) | 95th Percentile (p95) | Success Rate | Failed |
|---|---|---|---|---|---|---|---|
| 20 Workers | 500 req | 6.88 s | 72.7 RPS | 259.9 ms | 335.8 ms | 100.0% | 0 |
| 50 Workers | 1,500 req | 16.74 s | 89.6 RPS | 536.3 ms | 697.4 ms | 100.0% | 0 |
| 100 Workers | 3,000 req | 30.53 s | 98.3 RPS | 1,013.1 ms | 1,101.5 ms | 100.0% | 0 |
| 200 Workers | 5,000 req | 51.85 s | 96.4 RPS | 2,042.4 ms | 2,350.3 ms | 100.0% | 0 |
10,000 total requests processed across 10 backends with 0 dropped packets or errors (100.0% success rate).
Traffic Distribution Uniformity Across all 10 Backend Nodes:
app01 : 480 req ( 9.6%) #########
app02 : 509 req (10.2%) ##########
app03_compute : 461 req ( 9.2%) #########
app04 : 487 req ( 9.7%) #########
app05 : 603 req (12.1%) ############
app06 : 522 req (10.4%) ##########
app07 : 489 req ( 9.8%) #########
app08 : 477 req ( 9.5%) #########
app09 : 474 req ( 9.5%) #########
app10 : 498 req (10.0%) ##########
Round-robin distribution achieves an optimal ~10% balance per server across all 10 nodes under heavy concurrent load.
Timeout Validation Results (1ms to 12s):
| Injected Delay | Elapsed Time | Returned Status | Expected | Match | Behavior |
|---|---|---|---|---|---|
| 1 ms | 0.41 s | 200 OK |
200 |
PASS | Instant response |
| 50 ms | 0.30 s | 200 OK |
200 |
PASS | Instant response |
| 250 ms | 0.46 s | 200 OK |
200 |
PASS | Normal response |
| 1 sec | 1.23 s | 200 OK |
200 |
PASS | Normal response |
| 3 sec | 3.25 s | 200 OK |
200 |
PASS | Normal response |
| 6 sec | 6.25 s | 200 OK |
200 |
PASS | Normal response |
| 8 sec | 8.27 s | 200 OK |
200 |
PASS | Within 10s gateway timeout |
| 12 sec | 30.32 s | 502 / 504 |
[502, 504] |
PASS | Timeout triggered! Gateway retried 3 healthy nodes, then safely aborted to protect client. |
100k Throughput & 10-Minute Projection:
- At 98.3 sustained RPS, a single container gateway processes 58,980 requests in 10 minutes (600s).
- Reaching 100k requests in 10 minutes requires 167 RPS. Deploying 2 Gateway containers behind an ALB/DNS will comfortably deliver ~196 RPS (100k in 8.5 minutes) across the 10 backends.
5. Speed & Latency Optimizations
-
cURL Socket Keep-Alive Connection Reuse:
-
CURLOPT_TCP_KEEPALIVE => 1,CURLOPT_TCP_KEEPIDLE => 120,CURLOPT_TCP_KEEPINTVL => 60. - Eliminates TCP three-way handshake (
SYN/SYN-ACK/ACK) latency on every proxy forward.
-
-
PHP-FPM Dynamic High-Concurrency Pool:
- Default PHP-FPM settings throttle at 5 workers (
pm.max_children = 5), leading to queue starvation. - Tuned in Dockerfile & FPM configs:
pm = dynamic pm.max_children = 64 pm.start_servers = 16 pm.min_spare_servers = 8 pm.max_spare_servers = 32 pm.max_requests = 2000
- Default PHP-FPM settings throttle at 5 workers (
-
Nginx FastCGI Buffer Tuning:
-
fastcgi_buffer_size 32k;andfastcgi_buffers 16 16k;prevent disk-buffering bottlenecks during large response transfers. -
keepalive_requests 10000;andkeepalive_timeout 65;optimize persistent client connections.
-
-
Sub-Millisecond Node Registry Caching:
- Upstream node list is cached in Redis / APCu for 60 seconds.
- Atomic cache clearing occurs instantly on any REST or Artisan node modification (
NodeManagementService::clearNodeCache()).
-
Gateway Response Microcaching (
X-Cache: HIT / MISS):- For read-heavy endpoints (
GET,HEAD), the ingress gateway can microcache upstream responses in Redis or local memory for 1 to 60 seconds. - Sub-millisecond latency ($< 1\text{ ms}$) without hitting backend compute or network sockets.
- Safe by default: Requests with
Authorizationheaders, user session cookies, or mutating verbs (POST,PUT,DELETE,PATCH) automatically bypass the cache and pass through directly to backends. - Enabled via
.env:LOAD_BALANCER_CACHE_ENABLED=true LOAD_BALANCER_CACHE_TTL=5 LOAD_BALANCER_CACHE_STORE=redis
- Inspection: Responses return
X-Cache: HITorX-Cache: MISSheaders.
- For read-heavy endpoints (
Authentication & Secure Communications (Sanctum)
To protect communications across 1 Front Gateway (app03) + $N$ Backend Processing Servers, the application implements Laravel Sanctum token-based authentication paired with HMAC-SHA256 request signing:
- Client Authenticates: Client sends credentials to
POST /api/auth/loginand receives a Sanctumaccess_token(plainTextToken). - Stateless Gateway Ingress (
app03): For subsequent API calls, the client sendsAuthorization: Bearer <token>. Theapp03gateway:- Forwards the
Authorizationheader intact. - Signs the request with an HMAC-SHA256 hash (
X-LoadBalancer-Signature) and timestamp (X-LoadBalancer-Timestamp).
- Forwards the
- Backend Node Verification:
VerifyLoadBalancerSignatureverifies the HMAC signature, rejecting any requests that bypassapp03.auth:sanctumvalidates the Bearer token against the central database (personal_access_tokenstable in SQL Server / SQLite / MySQL).
Authentication Endpoints
| Method | Endpoint | Protection | Description |
|---|---|---|---|
POST |
/api/auth/login |
Public | Authenticates credentials and returns a Sanctum access_token |
GET |
/api/auth/me |
auth:sanctum |
Returns the authenticated user's profile |
POST |
/api/auth/workload |
auth:sanctum |
Executes a secure business workload requiring authentication |
POST |
/api/auth/logout |
auth:sanctum |
Revokes the current personal_access_token |
Testing Sanctum Authentication:
# 1. Login to obtain a Bearer token curl -X POST http://localhost:8080/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email": "alice@enterprise.com", "password": "SecretPass123!"}' # 2. Call protected workload endpoint via gateway curl -X POST http://localhost:8080/api/auth/workload \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <token_here>" \ -d '{"task": "process_batch", "batch_id": 1001}' # 3. Logout (revokes token in DB) curl -X POST http://localhost:8080/api/auth/logout \ -H "Authorization: Bearer <token_here>"
Interactive Swagger UI & OpenAPI Specification
The gateway includes an interactive, browser-based Swagger UI (OpenAPI 3.0.3) document explorer for visualizing and testing all endpoints:
- Swagger UI URL: http://localhost:8080/api/docs
- Raw OpenAPI JSON Spec: http://localhost:8080/api/docs/openapi.json
# View Swagger UI from CLI curl -i http://localhost:8080/api/docs # Download the complete OpenAPI 3.0 specification curl -s http://localhost:8080/api/docs/openapi.json | jq .
Visual Cluster Web Admin Control Center
A modern, responsive, dark-mode single-page administrative dashboard is built directly into the gateway. It allows DevOps and engineering leads to monitor and control the cluster in real time without touching the command line or database:
- Web Admin Dashboard URL: http://localhost:8080/admin/cluster
- Zero-Dependency Architecture: Built using pure vanilla modern HTML5, CSS3 glassmorphism, and asynchronous vanilla JavaScript.
Key Capabilities:
- Live Cluster KPI Metrics: Live counters for Total Registered Nodes, Active Healthy Nodes, Drained / Maintenance Nodes, and Gateway Status.
- Instant Traffic Draining: 1-click Drain / Maintain toggle removes a backend node from the proxy rotation instantly (atomic cache invalidation). Click again to return to Active status.
- Dynamic Node Provisioning: Click "+ Add Upstream Node" to register a new backend container/server (
name,URL,weight) with real-time modal validation. - Permanent Node Deletion: Remove decommissioned servers from the cluster database with confirmation safeguards.
- Zero-Trust Administrative Protection: Locked down by default with an "Authentication Required" lock screen. Requires pasting your configured
LOAD_BALANCER_METRICS_TOKEN(Bearer token) and clicking Unlock to view KPIs, list nodes, or execute administrative actions. Unauthorized requests are strictly rejected with HTTP 401. Active tokens are securely persisted in browsersessionStoragefor convenience.
Ready-to-Run cURL Commands
1. Health & Status Probe
curl -i http://localhost:8080/up
2. Laravel Sanctum Authentication Flow
# A. Authenticate and receive a Bearer token curl -s -X POST http://localhost:8080/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email": "alice@enterprise.com", "password": "SecretPass123!"}' | jq . # Save token into environment variable: TOKEN="<paste_access_token_here>" # B. Call authenticated user profile curl -s -X GET http://localhost:8080/api/auth/me \ -H "Authorization: Bearer $TOKEN" | jq . # C. Execute secure workload through the gateway curl -s -X POST http://localhost:8080/api/auth/workload \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"task": "process_batch", "batch_id": 1054}' | jq . # D. Revoke token on logout curl -s -X POST http://localhost:8080/api/auth/logout \ -H "Authorization: Bearer $TOKEN" | jq .
3. Cluster Node Management API
# A. List all active upstream nodes in the cluster curl -s -X GET http://localhost:8080/api/load-balancer/nodes \ -H "X-Metrics-Secret: admin-cluster-secret-token" | jq . # B. Register a new backend processing node curl -s -X POST http://localhost:8080/api/load-balancer/nodes \ -H "X-Metrics-Secret: admin-cluster-secret-token" \ -H "Content-Type: application/json" \ -d '{"name": "backend_app06", "url": "http://10.0.0.16:8000", "weight": 5, "is_active": true}' | jq . # C. Inspect node details by ID curl -s -X GET http://localhost:8080/api/load-balancer/nodes/1 \ -H "X-Metrics-Secret: admin-cluster-secret-token" | jq . # D. Toggle node maintenance mode (drain/reactivate traffic instantly) curl -s -X PATCH http://localhost:8080/api/load-balancer/nodes/1/toggle \ -H "X-Metrics-Secret: admin-cluster-secret-token" | jq . # E. Remove node from the database curl -s -X DELETE http://localhost:8080/api/load-balancer/nodes/1 \ -H "X-Metrics-Secret: admin-cluster-secret-token" | jq .
4. IP Affinity & Firewall Rules API
# A. List all active IP rules curl -s -X GET http://localhost:8080/api/load-balancer/ip-rules \ -H "X-Metrics-Secret: admin-cluster-secret-token" | jq . # B. Bind an IP to a designated target node (Sticky Routing) curl -s -X POST http://localhost:8080/api/load-balancer/ip-rules \ -H "X-Metrics-Secret: admin-cluster-secret-token" \ -H "Content-Type: application/json" \ -d '{"ip_address": "192.168.1.100", "action": "route", "target_node_id": 2}' | jq . # C. Delete an IP rule curl -s -X DELETE http://localhost:8080/api/load-balancer/ip-rules/1 \ -H "X-Metrics-Secret: admin-cluster-secret-token" | jq .
5. Cluster Observability & Latency Metrics
# Retrieve real-time request counts, status codes, and latency percentiles curl -s -X GET http://localhost:8080/api/load-balancer/metrics \ -H "X-Metrics-Secret: admin-cluster-secret-token" | jq .
Security & Zero-Trust Hardening
1. HMAC-SHA256 Request Signing & Nonce Deep-Dive
To ensure clients cannot bypass the app03 gateway and send requests directly to backend nodes, all proxied requests are cryptographically signed with HMAC-SHA256.
Payload Canonical String:
"{METHOD}|{PATH}|{TIMESTAMP}|{REQUEST_ID}"
X-Request-Id(Cryptographic Nonce & Tracing ID): A unique UUIDv4 generated for every request. Ensures two identical requests arriving in the same second never share the same hash, eliminating collision and rainbow table vulnerabilities.X-LB-Timestamp(Replay Window): Unix timestamp validated against a 300-second expiration tolerance.X-LB-Signature:hash_hmac('sha256', payload, secret).
Configuration:
In your .env (on both Gateway and Backends):
LOAD_BALANCER_SIGNATURE_ENABLED=true LOAD_BALANCER_SIGNATURE_SECRET="your-ultra-secure-shared-secret-key-64-bytes" LOAD_BALANCER_SIGNATURE_TOLERANCE=300
Verification Middleware on Backend Nodes (app01 to appN):
In bootstrap/app.php:
use Isg\LoadBalancer\Http\Middleware\VerifyLoadBalancerSignature; $middleware->api(append: [ VerifyLoadBalancerSignature::class, ]);
Or simply use the route alias: Route::middleware('gateway.verify').
Zero-Downtime HMAC Key Rotation
In enterprise environments, security policies require periodic secret rotation. To rotate keys without dropping inflight requests or requiring simultaneous downtime across all servers:
- Configure
active_key_id, key mappings, andgrace_secretsinconfig/load-balancer.php:'signature' => [ 'enabled' => env('LOAD_BALANCER_SIGNATURE_ENABLED', true), 'active_key_id' => env('LOAD_BALANCER_ACTIVE_KEY_ID', 'v2'), 'keys' => [ 'v1' => env('LOAD_BALANCER_KEY_V1'), 'v2' => env('LOAD_BALANCER_KEY_V2'), ], 'grace_secrets' => array_filter(explode(',', (string) env('LOAD_BALANCER_GRACE_SECRETS', ''))), ],
- The gateway automatically tags requests with header
X-LB-Key-Id: v2. - Backend servers verify against the active key ID (
v2), falling back to any configured grace keys (LOAD_BALANCER_GRACE_SECRETS), providing seamless, zero-downtime rotation.
Hands-On cURL Demonstration:
A. Bypassing the Gateway (Rejected):
curl -i http://backend_app01:8000/api/workload # Output: HTTP/1.1 401 Unauthorized # {"error":"Unauthorized","message":"Missing required load balancer signature headers."}
B. Manually Calculating & Sending Valid Signature:
# In Bash: SECRET="your-ultra-secure-shared-secret-key-64-bytes" TIMESTAMP=$(date +%s) REQ_ID="req-$(cat /proc/sys/kernel/random/uuid)" PAYLOAD="GET|api/workload|${TIMESTAMP}|${REQ_ID}" SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}') # Send with signature: curl -i http://backend_app01:8000/api/workload \ -H "X-Request-Id: $REQ_ID" \ -H "X-LB-Timestamp: $TIMESTAMP" \ -H "X-LB-Signature: $SIGNATURE" # Output: HTTP/1.1 200 OK
C. Path Tampering (Detected & Rejected):
# Attacker alters the path to /api/admin without knowing the secret curl -i http://backend_app01:8000/api/admin \ -H "X-Request-Id: $REQ_ID" \ -H "X-LB-Timestamp: $TIMESTAMP" \ -H "X-LB-Signature: $SIGNATURE" # Output: HTTP/1.1 403 Forbidden # {"error":"Forbidden","message":"Invalid load balancer signature."}
2. RFC-Compliant Header Sanitization & Multi-Cookie Preservation
The reverse proxy cleanses all headers before forwarding upstream and before responding to the client:
- Stripped Hop-by-Hop Headers:
Connection,Keep-Alive,Proxy-Authenticate,Proxy-Authorization,TE,Trailers,Transfer-Encoding,Upgrade. - Client IP Forgery Prevention: Strips client-supplied
X-Forwarded-Host,X-Forwarded-Proto, andX-Forwarded-For, replacing them with verified client IP and gateway metadata. - Multiple
Set-CookieSupport: Preserves all separateSet-Cookieresponse headers returned by backend nodes (e.g., session cookies + remember-me cookies + tracking cookies) without overwriting or collapsing them. - Bearer Tokens: The
Authorizationheader is securely forwarded untouched.
3. Sliding-Window Circuit Breaker & Fail-Open Protection
Traditional circuit breakers that trip on a single error cause cascading outages. This package implements a sliding-window failure rate breaker:
- Tracks total requests and failures over a 60-second window.
- Trips to
OPENstatus only when the error rate exceeds the threshold (default: 50% failures across a minimum of 5 requests). - Fail-Open Protection: If all backend nodes trip simultaneously, the breaker fails open rather than serving 500 errors to 100% of your users.
- Half-Open Probing: Periodically lets a canary probe through after the cool-off period (default: 30 seconds) to detect upstream recovery automatically.
4. Request Body Size Limits & Memory-Efficient Streaming
- Configurable Body Limit: Rejects oversized payloads immediately with
HTTP 413 Content Too Large(default: 10MB viaLOAD_BALANCER_MAX_BODY_SIZE=10485760). - Response Streaming: Responses are streamed directly via
response()->stream()using low memory buffers, preventing memory exhaustion on large downloads or long-polling responses.
5. Authenticated Observability & Metrics
The /api/load-balancer/metrics endpoint provides JSON telemetry (cluster status, total requests, average latency).
By default, this endpoint returns 404 Not Found to prevent information disclosure. When enabled, it is protected by authentication and IP allowlisting:
LOAD_BALANCER_METRICS_ENABLED=true LOAD_BALANCER_METRICS_TOKEN="your-metrics-bearer-token" LOAD_BALANCER_METRICS_IP_ALLOWLIST="10.0.0.5,192.168.1.50"
Artisan CLI Operations
Node Management (loadbalancer:node)
Manage cluster nodes dynamically without database access:
# Add a new backend node (validates URL scheme and syntax): php artisan loadbalancer:node add --name=app01 --url=http://10.0.0.11:8000 --weight=1 # Add a primary node in an active-passive setup: php artisan loadbalancer:node add --name=app02 --url=http://10.0.0.12:8000 --weight=2 --primary # List all nodes with current health and weights: php artisan loadbalancer:node list # Toggle a node active/inactive for scheduled maintenance: php artisan loadbalancer:node toggle 1 # Remove a node from the cluster: php artisan loadbalancer:node remove 1
Concurrent Health Checks (loadbalancer:health-check)
Executes non-blocking, parallel health checks across all cluster nodes using Guzzle HTTP pools:
# Run manual health check:
php artisan loadbalancer:health-check --timeout=5 --consecutive=3
Schedule it in your routes/console.php (Laravel 11/12) or app/Console/Kernel.php (Laravel 10):
use Illuminate\Support\Facades\Schedule; Schedule::command('loadbalancer:health-check --timeout=3 --consecutive=2')->everyMinute();
Telemetry Retention (loadbalancer:prune-metrics)
Prunes historical telemetry rows in safe, chunked transactions to avoid locking SQL Server or MySQL tables:
# Delete metric records older than 7 days in chunks of 1000:
php artisan loadbalancer:prune-metrics --days=7 --chunk=1000
Schedule nightly in your console routes:
Schedule::command('loadbalancer:prune-metrics --days=14')->daily();
Balancing Strategies & Sticky Sessions
Supported Strategies
Set your strategy in .env via LOAD_BALANCER_DEFAULT_STRATEGY:
| Strategy Key | Description | Best For |
|---|---|---|
round_robin |
Even sequential distribution across all healthy nodes | Uniform backend servers |
weighted_random |
Distributes proportionally according to each node's weight column |
Heterogeneous server capacities |
active_passive |
Routes 100% of traffic to is_primary=true nodes; fails over to secondaries |
Disaster recovery setups |
adaptive_latency |
Power-of-Two-Choices (P2C) picking the lowest EMA latency node | Real-time dynamic workloads |
least_redirects |
Routes to the node with the lowest 24-hour total request tally | Redirect mode |
random |
Arbitrary random distribution | Development and testing |
Rendezvous (HRW) Consistent Hashing
When LOAD_BALANCER_STICKY_SESSIONS=true is enabled, the proxy uses Highest Random Weight (HRW) / Rendezvous Hashing rather than simple modulo hashing.
- The client IP address is hashed against each active node's identifier (
md5("$ip-$nodeId")). - The node yielding the highest weight receives the request.
- Advantage: When a backend server is added or removed, only $1/N$ of keys are remapped, preserving cache warmth across the remaining $N-1$ servers.
Power-of-Two-Choices (P2C) Adaptive Latency
The adaptive_latency strategy uses Power-of-Two-Choices (P2C):
- Samples two healthy nodes at random.
- Compares their Exponential Moving Average (EMA) response latency.
- Selects the node with the lower latency.
This prevents the classic "thundering herd" oscillation where all requests rush to a single momentarily fast server and overload it.
Custom Strategy Implementation
Create a custom class implementing LoadBalancingStrategy:
namespace App\LoadBalancing; use Isg\LoadBalancer\Contracts\LoadBalancingStrategy; use Illuminate\Support\Collection; use Illuminate\Http\Request; use Isg\LoadBalancer\Models\UpstreamNode; class RegionalAffinityStrategy implements LoadBalancingStrategy { public function selectNode(Collection $nodes, ?Request $request = null): ?UpstreamNode { $region = $request?->header('CF-IPCountry', 'US'); return $nodes->firstWhere('metadata.region', $region) ?? $nodes->first(); } }
Register it in config/load-balancer.php:
'strategies' => [ // ... 'regional' => \App\LoadBalancing\RegionalAffinityStrategy::class, ],
Configuration Reference (.env)
| Variable | Type | Default | Description |
|---|---|---|---|
LOAD_BALANCER_MODE |
string | proxy |
proxy (pipes payloads through gateway) or redirect (issues HTTP 302) |
LOAD_BALANCER_DEFAULT_STRATEGY |
string | round_robin |
Default routing strategy key |
LOAD_BALANCER_TIMEOUT |
int | 30 |
Upstream read/execution timeout in seconds |
LOAD_BALANCER_CONNECT_TIMEOUT |
int | 5 |
Upstream socket connection timeout in seconds |
LOAD_BALANCER_TLS_VERIFY |
bool | true |
Verify upstream SSL/TLS certificates (set false for self-signed LAN certs) |
LOAD_BALANCER_FORCE_HTTPS |
bool | false |
Force https:// schemes when querying upstreams in production |
LOAD_BALANCER_STICKY_SESSIONS |
bool | false |
Enable Rendezvous consistent IP hashing |
LOAD_BALANCER_STREAM_RESPONSES |
bool | true |
Stream backend responses with low memory footprint |
LOAD_BALANCER_MAX_BODY_SIZE |
int | 10485760 |
Maximum request body size in bytes (10MB). Rejects with 413 if exceeded |
LOAD_BALANCER_LOGGING |
bool | true |
Enable asynchronous metrics recording |
LOAD_BALANCER_SAMPLE_RATE |
float | 1.0 |
Probability (0.01 - 1.0) to sample telemetry at high scale |
LOAD_BALANCER_CIRCUIT_BREAKER_ENABLED |
bool | true |
Enable sliding-window circuit breaker |
LOAD_BALANCER_CIRCUIT_BREAKER_FAIL_RATE |
float | 0.5 |
Failure rate threshold (50%) to trip breaker |
LOAD_BALANCER_CIRCUIT_BREAKER_MIN_REQ |
int | 5 |
Minimum request volume before breaker can trip |
LOAD_BALANCER_CIRCUIT_BREAKER_DECAY |
int | 30 |
Cool-off window in seconds before half-open canary probe |
LOAD_BALANCER_SIGN_REQUESTS |
bool | false |
Enable HMAC-SHA256 request signing for zero-trust |
LOAD_BALANCER_HMAC_SECRET |
string | null |
64-byte shared secret key for HMAC signature generation |
LOAD_BALANCER_WAF_ENABLED |
bool | true |
Enable internal IP rate limiter |
LOAD_BALANCER_WAF_LIMIT |
int | 500 |
Max requests per minute per IP |
LOAD_BALANCER_CACHE_ENABLED |
bool | false |
Enable sub-millisecond gateway response microcaching |
LOAD_BALANCER_CACHE_TTL |
int | 5 |
Microcache duration in seconds (1 to 60s) |
LOAD_BALANCER_CACHE_STORE |
string | null |
Cache repository store (valkey, redis, database, file) |
LOAD_BALANCER_ACTIVE_KEY_ID |
string | null |
Active key identifier tag (e.g. v2) attached via X-LB-Key-Id |
LOAD_BALANCER_GRACE_SECRETS |
string | null |
Comma-separated previous secrets accepted during zero-downtime key rotation |
LOAD_BALANCER_METRICS_ENABLED |
bool | false |
Enable /api/load-balancer/metrics endpoint |
LOAD_BALANCER_METRICS_TOKEN |
string | null |
Required Bearer token for accessing metrics |
LOAD_BALANCER_METRICS_IP_ALLOWLIST |
string | null |
Comma-separated list of allowed CIDRs / IPs for metrics |
REDIS_HOST |
string | 127.0.0.1 |
Valkey / Redis cluster service hostname |
REDIS_PORT |
int | 6379 |
Valkey / Redis cluster service port |
Testing & Quality Assurance
The package includes comprehensive unit and integration test coverage:
# Run PHPUnit test suite: php vendor/bin/phpunit # Or via Docker: docker exec app03_gateway vendor/bin/phpunit
All 59 unit and integration tests (190 assertions, 100% green) validate:
- OWASP A10 / SSRF protection: Cloud metadata link-local blocking (
169.254.169.254,metadata.google.internal). - OWASP A03 / CWE-113: Forwarded header CRLF injection and HTTP request splitting sanitization.
- OWASP A02 & A07: Atomic cryptographic nonce anti-replay protection on signed requests.
- OWASP A05: Security hardening headers (
X-Content-Type-Options: nosniff,X-Frame-Options: SAMEORIGIN). - OWASP A07: Brute-force login rate limiting on Sanctum authentication (
throttle:10,1). - Zero-trust header sanitization, IP proxy spoofing prevention, and multi-cookie preservation.
- Laravel Sanctum authentication, Bearer token verification, and token revocation on logout.
- Gateway response microcaching (
X-Cache: HIT / MISS) and authentication bypass. - Zero-downtime HMAC key rotation (
X-LB-Key-Id, active key, and grace-period secrets). - Turnkey installation CLI wizard (
loadbalancer:install) and'gateway.verify'middleware. - REST API cluster and IP affinity rule database management with validation.
- Sliding-window circuit breaker tripping and fail-open resilience.
- Windows NTFS path traversal defenses (alternate streams, 8.3 short names, trailing dots).
- Rendezvous consistent hashing balance and minimal disruption.
- Concurrent health check pooling and metric pruning.
- Protected metrics authorization and HMAC signature verification.
License
The MIT License (MIT). Please see License File for more information.