jean-beru/fos-http-cache-cloudfront
CloudFront proxy implementation for FOSHttpCache. Create an AWS CloudFront client, instantiate the proxy with your distribution ID, purge single URLs or wildcard patterns, then call flush(). Supports custom caller reference generators to avoid duplicate requests.
Install Dependencies:
composer require jean-beru/fos-http-cache-cloudfront:^1.3 async-aws/cloudfront
Since 1.3.0:
friendsofsymfony/http-cache2.x is no longer supported. You must be onfriendsofsymfony/http-cache:^3.0(and typicallyFOSHttpCacheBundle3.x). If your app is pinned to 2.x, stay onjean-beru/fos-http-cache-cloudfront:1.2.*until you migrate.
Configure AWS SDK:
Ensure AWS credentials are available via environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) or Laravel’s AWS config (config/aws.php).
Initialize the Proxy:
use Aws\CloudFront\CloudFrontClient;
use JeanBeru\HttpCacheCloudFront\Proxy\CloudFront;
$client = new CloudFrontClient([
'region' => env('AWS_REGION'),
'version' => 'latest',
]);
$proxy = new CloudFront(
client: $client,
options: [
'distribution_id' => env('CLOUDFRONT_DISTRIBUTION_ID'),
]
);
First Use Case: Purge a single URL (e.g., after content update):
$proxy->purge('/home')->flush();
purge(string $url) signature).UniqIdCallerReferenceGenerator) to customize deduplication logic.CallerReference serialization, purge() typing, dependency drops).tests/ for edge cases (e.g., invalidation failures, rate limiting).string in 1.3.0):
$proxy->purge('/path/to/resource')->flush();
$proxy->purge('/assets/*')->purge('/images/*')->flush();
$proxy->purge((string) route('product.show', $id))->flush();
Pre-load critical paths during deployment:
$proxy->warmup('/home')->warmup('/products')->flush();
Trigger purges on model updates:
// app/Providers/EventServiceProvider.php
public function boot()
{
ProductUpdated::listen(function ($product) {
app('cloudfront.proxy')->purge(route('product.show', $product))->flush();
});
}
Combine with Laravel’s cache drivers:
// Cache in Redis, invalidate in CloudFront
Cache::put('key', $data, now()->addMinutes(5));
app('cloudfront.proxy')->purge('/cached-data')->flush();
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->singleton('cloudfront.proxy', function ($app) {
$client = new CloudFrontClient($app['config']['aws.cloudfront']);
return new CloudFront($client, [
'distribution_id' => $app['config']['services.cloudfront.distribution_id'],
]);
});
}
Create a CloudFront facade for cleaner syntax:
// app/Facades/CloudFront.php
public static function purge(string $path): self
{
return static::getFacadeRoot()->purge($path);
}
Add an Artisan command for manual purges:
// app/Console/Commands/PurgeCloudFront.php
public function handle()
{
$proxy = app('cloudfront.proxy');
$proxy->purge($this->argument('path'))->flush();
$this->info('Purged: '.$this->argument('path'));
}
Upgrading from 1.2.x to 1.3.0 (Breaking Dependency Drop):
friendsofsymfony/http-cache 2.x. Composer will fail to resolve if your app still requires it.friendsofsymfony/http-cache:^3.0 and the matching bundle before bumping this package, or pin jean-beru/fos-http-cache-cloudfront:1.2.* until ready.CallerReference Must Be a String:
CallerReference value could be sent to createInvalidation in a non-string form, producing SDK validation errors/odd payloads.generate() must return string — cast early if you build it from an int/ULID/UUID object:
public function generate(): string
{
return (string) Str::ulid();
}
purge() Parameter Typing:
$url parameter of CloudFront::purge() as string. UrlGenerator output, Url value objects, or route() results passed through loosely may now raise a TypeError.(string) route(...)) and check for subclass overrides that widen the signature.AWS SDK Conflicts:
async-aws/cloudfront with aws/aws-sdk-php (v2) may cause autoloading errors.composer.json:
"require": {
"async-aws/cloudfront": "^3.0"
},
"conflict": {
"aws/aws-sdk-php": "*"
}
Duplicate Invalidation Errors:
FOS\HttpCache\Exception\ProxyResponseException if the same caller_reference is reused.CallerReferenceGenerator (e.g., DateCallerReferenceGenerator) for time-based uniqueness:
$proxy = new CloudFront($client, [
'distribution_id' => 'XYZ123',
'caller_reference_generator' => new DateCallerReferenceGenerator('YmdHis'),
]);
CloudFront Distribution Mismatch:
distribution_id in config and use environment-specific values.TTL Misconfiguration:
DefaultCacheBehavior in CloudFront or use Cache-Control headers to override TTLs.Rate Limiting:
$proxy->purge('/path1')->purge('/path2')->flush();
$client = new CloudFrontClient([
'debug' => true,
'logger' => new \Aws\Handler\Log\LogHandler(new \Monolog\Logger('CloudFront')),
]);
InvalidationList in the CloudFront console for failures.TypeError/validation error here usually points to a custom generator returning a non-string.$mock = Mockery::mock(CloudFrontClient::class);
$mock->shouldReceive('createInvalidation')->andReturn(new Invalidation(['status' => 'InProgress']));
Custom Caller Reference:
Implement CallerReferenceGenerator for business-specific deduplication (return type is string, enforced by 1.3.0):
class OrderIdCallerReferenceGenerator implements CallerReferenceGenerator
{
public function generate(): string
{
return 'order-'.Order::latest()->first()->id;
}
}
Pre/Post-Flush Hooks:
Extend the CloudFront class to add callbacks:
class ExtendedCloudFront extends CloudFront
{
public function flush(): self
{
$this->logPurgeRequest();
parent::flush();
$this->notifySlack();
return $this;
}
}
Multi-Distribution Support: Override the proxy to handle multiple distributions:
class MultiDistributionProxy
{
public function __construct(private array $proxies) {}
public function purge(string $path): self
{
foreach ($this->proxies as $proxy) {
$proxy->purge($path);
}
return $this;
}
}
AWS Region:
Ensure the AWS SDK region matches your CloudFront distribution’s edge location (e.g., us-east-1 for global distributions).
IAM Permissions: The IAM user/role must have:
{
"Effect": "Allow",
"Action": [
"cloudfront:CreateInvalidation",
"cloudfront:GetInvalidation"
],
"Resource": "arn:aws:cloudfront::DISTRIBUTION_ID"
}
Path Patterns:
CloudFront invalidation paths must match the distribution’s CacheBehaviors. For example:
/assets/* works if the behavior is configured for assets.*./api/* requires a separate behavior for API paths.Environment Variables:
Avoid hardcoding credentials. Use Laravel’s .env:
AWS_ACCESS_KEY_ID=your_key
AWS_SECRET_ACCESS_KEY=your_secret
CLOUDFRONT_DISTRIBUTION_ID=XYZ123
How can I help you explore Laravel packages today?