laravel-any-trusted-proxies maintained by myscode
Laravel Any Trusted Proxies
Easily configurable any popular, custom, or niche proxy providers for Laravel.
A Laravel package for dynamic loading of trusted proxy IPs from multiple CDN and proxy providers: Cloudflare, Fastly, AWS CloudFront, Gcore, Bunny CDN, CDNvideo, Megafon — and any custom provider you add.
Table of Contents
- Features
- Requirements
- Installation
- Quick Start
- Publishing Configuration
- Middleware Setup
- Configuration
- IP Address Formats
- Cache Safety
- Available Commands
- Scheduler Setup
- Architecture
- Adding a Custom Provider
- Testing
- License
Features
- Pick any providers — enable only the ones you need via a single
.envvariable - 7 built-in providers: Cloudflare, Fastly, AWS CloudFront, Gcore, Bunny CDN, CDNvideo, Megafon
- Custom provider support — add your own or niche providers via the
IpProviderinterface - HTTP retries via
Http::retrywith configurable attempts and delay - Safe cache refresh: if any provider fails, the existing cache is preserved — partial results are never saved
- Middleware that injects trusted proxy IPs at runtime — replaces the stock
TrustProxiesmiddleware - Artisan command for manual/scheduled cache refresh
- CIDR and plain IP support — transparently works with both formats
Requirements
| Dependency | Version |
|---|---|
| PHP | >= 8.1 |
| Laravel | 10.x or 11.x |
| Composer | >= 2.0 |
Installation
composer require myscode/laravel-any-trusted-proxies
The package uses Laravel auto-discovery — the ServiceProvider is registered automatically.
Quick Start
- Add to your
.envthe providers you want to enable:
TRUSTED_PROXIES=cloudflare,fastly
- Register the middleware in
bootstrap/app.php(Laravel 11+):
->withMiddleware(function (Middleware $middleware) {
$middleware->replace(
\Illuminate\Http\Middleware\TrustProxies::class,
\Myscode\AnyTrustedProxies\Http\Middleware\TrustProxies::class
);
})
- Done! The trusted proxy IPs will be fetched, cached, and applied automatically.
If TRUSTED_PROXIES is left empty, no providers are loaded — the middleware will have an empty proxy list.
Publishing Configuration
php artisan vendor:publish --tag=trusted-proxy-config
After publishing, the file will be placed at config/trusted-proxy.php. You can add custom providers, change TTL, or tweak retry settings.
Middleware Setup
The package provides its own middleware Myscode\AnyTrustedProxies\Http\Middleware\TrustProxies that replaces the stock Illuminate\Http\Middleware\TrustProxies.
Laravel 11+
->withMiddleware(function (Middleware $middleware) {
$middleware->replace(
\Illuminate\Http\Middleware\TrustProxies::class,
\Myscode\AnyTrustedProxies\Http\Middleware\TrustProxies::class
);
})
Laravel 10
In app/Http/Kernel.php:
protected $middleware = [
// ...
\Myscode\AnyTrustedProxies\Http\Middleware\TrustProxies::class,
];
Configuration
Environment Variables
All configuration can be done through .env:
# Comma-separated list of providers to enable (leave empty to disable all)
TRUSTED_PROXIES=cloudflare,fastly,aws_cloudfront
# Cache key (optional, default: trusted_proxy_ips)
TRUSTED_PROXY_CACHE_KEY=trusted_proxy_ips
# Cache TTL in seconds (optional, default: 604800 = 7 days)
TRUSTED_PROXY_TTL=604800
# HTTP retry attempts (optional, default: 3)
TRUSTED_PROXY_RETRY_TIMES=3
# HTTP retry delay in ms (optional, default: 200)
TRUSTED_PROXY_RETRY_SLEEP=200
Options Reference
| Option | Type | .env Variable |
Description | Default |
|---|---|---|---|---|
cache_key |
string |
TRUSTED_PROXY_CACHE_KEY |
Cache key for storing IPs | trusted_proxy_ips |
ttl |
int |
TRUSTED_PROXY_TTL |
Cache lifetime in seconds | 604800 (7 days) |
retry.times |
int |
TRUSTED_PROXY_RETRY_TIMES |
Number of HTTP retry attempts | 3 |
retry.sleep |
int |
TRUSTED_PROXY_RETRY_SLEEP |
Delay between retries (ms) | 200 |
enabled |
array |
TRUSTED_PROXIES |
Comma-separated provider names to enable | [] |
providers |
array |
— | Map of all available providers (name → class) | all built-in |
headers |
int |
— | Bitmask of trusted forwarded headers | HEADER_X_FORWARDED_ALL |
The headers value is a bitmask from Illuminate\Http\Request:
| Constant | Value | Description |
|---|---|---|
HEADER_X_FORWARDED_FOR |
0b00000010 |
Client IP |
HEADER_X_FORWARDED_HOST |
0b00000100 |
Host |
HEADER_X_FORWARDED_PORT |
0b00001000 |
Port |
HEADER_X_FORWARDED_PROTO |
0b00010000 |
Protocol (http/https) |
HEADER_X_FORWARDED_ALL |
0b11111110 |
All headers |
Available Providers
| Provider | .env Key |
Class | Response Type | Data Source |
|---|---|---|---|---|
| Cloudflare | cloudflare |
CloudflareIpProvider |
Plain text | cloudflare.com/ips-v4 + ips-v6 |
| Fastly | fastly |
FastlyIpProvider |
JSON | api.fastly.com/public-ip-list |
| AWS CloudFront | aws_cloudfront |
AwsCloudFrontIpProvider |
JSON | ip-ranges.amazonaws.com/ip-ranges.json |
| Gcore | gcore |
GcoreIpProvider |
JSON | api.gcore.com/cdn/public_ips_list |
| Bunny CDN | bunny |
BunnyIpProvider |
JSON | api.bunny.net/system/edgeserverlist |
| CDNvideo | cdnvideo |
CdnvideoIpProvider |
JSON | api.cdnvideo.ru/app/nodes/v2/ip2origin |
| Megafon | megafon |
MegafonIpProvider |
JSON | api.lk.cdn.megafon.ru/cdn/public_ips_list |
IP Address Formats
The package does not validate or transform IP addresses on its own — it passes them "as is" to Laravel's standard middleware. Verification happens at the framework level via Symfony's IpUtils::checkIp().
Both formats are supported:
| Format | Example | How it is checked |
|---|---|---|
| CIDR | 173.245.48.0/20 |
Subnet membership check |
| Plain IPv4 | 192.168.1.1 |
Exact match (equivalent to /32) |
| Plain IPv6 | 2400:cb00::1 |
Exact match (equivalent to /128) |
Cache Safety
- On
refresh/load, the list is cached only if all enabled providers succeed - If any provider fails, the existing cache is used (if available), so the application never loses its trusted proxies
- Partial results on failure are never saved — this prevents a scenario where a single failed provider removes its IPs from the trusted list
- All IPs are deduplicated across providers
Available Commands
any-trustedproxy:reload
Force-refreshes the trusted proxy list from all enabled providers:
php artisan any-trustedproxy:reload
Output:
Trusted proxies refreshed. Count: 42
Exit codes:
0(SUCCESS) — refresh completed successfully
Scheduler Setup
use Illuminate\Support\Facades\Schedule;
Schedule::command('any-trustedproxy:reload')->weekly();
// or: ->daily(), ->twiceDaily(), ->everySixHours()
Architecture
┌─────────────────────────────────────────────┐
│ .env │
│ TRUSTED_PROXIES=cloudflare,fastly │
└──────────────────┬──────────────────────────┘
▼
┌─────────────────────────────────────────────┐
│ ServiceProvider │
│ ┌───────────────────────────────────────┐ │
│ │ Read enabled[] from config │ │
│ │ Filter providers map by enabled │ │
│ │ Instantiate only selected providers │ │
│ └───────────────┬───────────────────────┘ │
└──────────────────┼──────────────────────────┘
▼
┌─────────────────────────────────────────────┐
│ HTTP Request │
└────────────────┬────────────────────────────┘
▼
┌─────────────────────────────────────────────┐
│ Middleware: TrustProxies │
│ ┌───────────────────────────────────────┐ │
│ │ $this->proxies = TrustedProxy::load()│ │
│ │ $this->headers = config(...) │ │
│ └───────────────┬───────────────────────┘ │
└──────────────────┼──────────────────────────┘
▼
┌─────────────────────────────────────────────┐
│ Facade: TrustedProxy │
│ ┌───────────────────────────────────────┐ │
│ │ IpCacheManager │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ Cache::get(cache_key) │ │ │
│ │ │ ├── hit → return cached │ │ │
│ │ │ └── miss → fetchFromProviders │ │ │
│ │ └──────────────┬──────────────────┘ │ │
│ └─────────────────┼─────────────────────┘ │
└────────────────────┼────────────────────────┘
▼
┌─────────────────────────────────────────────┐
│ IpProvider[] (only enabled) │
│ ┌──────────┐ ┌──────────┐ ┌────────────┐ │
│ │Cloudflare│ │ Fastly │ │ Gcore │ │
│ │ getIps() │ │ getIps() │ │ getIps() │ │
│ └────┬─────┘ └────┬─────┘ └─────┬──────┘ │
│ └─────────────┴─────────────┘ │
│ │ │
│ array_merge + │
│ array_unique │
│ ▼ │
│ Cache::put() │
└─────────────────────────────────────────────┘
Key Components
| Component | Purpose |
|---|---|
IpProvider (interface) |
Contract for all providers: getIps(): array |
FetchesWithRetry (trait) |
HTTP requests with retries: getJsonWithRetry() and getTextLinesWithRetry() |
IpCacheManager |
Orchestration: collects IPs from providers, caching, fail-safe logic |
TrustProxies (middleware) |
Replaces stock middleware — injects proxies at runtime |
TrustedProxy (facade) |
Convenient access to IpCacheManager |
TrustedProxyServiceProvider |
Laravel registration: singleton, TRUSTED_PROXIES filtering, command, config |
ReloadProxiesCommand |
Artisan command for forced cache refresh |
Adding a Custom Provider
Implement the IpProvider interface and optionally use the FetchesWithRetry trait:
<?php
namespace App\TrustedProxies;
use Myscode\AnyTrustedProxies\Contracts\IpProvider;
use Myscode\AnyTrustedProxies\Services\Concerns\FetchesWithRetry;
class MyCdnProvider implements IpProvider
{
use FetchesWithRetry;
public function getIps(): array
{
// Option 1: JSON API
$data = $this->getJsonWithRetry('https://api.mycdn.com/public-ips');
return collect($data['ip_ranges'] ?? [])
->pluck('subnet')
->filter()
->values()
->all();
// Option 2: Plain text (one IP per line)
// return $this->getTextLinesWithRetry('https://mycdn.com/ips.txt');
// Option 3: Static list
// return ['10.0.0.0/8', '172.16.0.0/12'];
}
}
Register it in config/trusted-proxy.php and .env:
// config/trusted-proxy.php
'providers' => [
// ...built-in providers...
'my_cdn' => \App\TrustedProxies\MyCdnProvider::class,
],
# .env
TRUSTED_PROXIES=cloudflare,fastly,my_cdn
Implementation Guidelines
getIps()must return a flat array of strings — CIDR (10.0.0.0/8) or plain IP (192.168.1.1)- Do not throw exceptions —
IpCacheManagercatches\Throwableand logs the error without breaking other providers - Use the
FetchesWithRetrytrait for automatic retries according to your retry settings - Filter out empty values — use
array_filteron the result
Testing
composer install
composer test
composer test-coverage # with coverage
Tests use Orchestra Testbench. HTTP requests are mocked via Http::fake().
License
MIT License — see LICENSE.