Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Http2Serverpush Laravel Package

jacobbennett/laravel-http2serverpush

A Laravel middleware package for HTTP/2 server push. It helps your app send Link preload headers for CSS, JS, and other assets so browsers can fetch them sooner, reducing latency and improving page load performance with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register

composer require jacobbennett/laravel-http2serverpush
  • On Laravel 5.5+ package discovery registers the service provider automatically. On older 5.x, add JacobBennett\HTTP2ServerPush\HTTP2ServerPushServiceProvider::class to config/app.php.
  • Publish the config (this is where you actually configure anything):
php artisan vendor:publish --provider="JacobBennett\HTTP2ServerPush\HTTP2ServerPushServiceProvider"

Register the middleware

Add the middleware to app/Http/Kernel.php. For a site-wide effect put it in the global $middleware stack, since assets are usually shared across every HTML page:

protected $middleware = [
    // ...
    \JacobBennett\HTTP2ServerPush\Middleware\HTTP2ServerPush::class,
];

If you only want pushes on specific routes, register it as a route middleware in $routeMiddleware with a key like 'http2push' and attach it to a group instead.

Where to look first

  1. config/http2serverpush.php — the assets array is the list of paths that get emitted as Link: <...>; rel=preload headers.
  2. app/Http/Kernel.php — confirm middleware position; it must run after the response is generated, which the global stack handles by default.
  3. Your web server / CDN config — this is the half of the feature the package cannot do for you. Without http2_push_preload on; in nginx, H2Push in Apache, or a CDN that honors Link: rel=preload, the headers are inert.

First use case

Put your compiled bundle and main stylesheet in the config:

// config/http2serverpush.php
return [
    'assets' => [
        '/js/app.js',
        '/css/app.css',
    ],
];

Then verify with curl:

curl -sI https://your-app.test/ | grep -i '^link'

You should see Link: </js/app.js>; rel=preload (and one per asset). If the headers are there but nothing is pushed, the problem is at the server/CDN layer, not in Laravel.

Implementation Patterns

Global middleware for the shell assets. The common pattern is a tiny, stable list — the JS bundle, the CSS bundle, and a webfont — pushed on every HTML response. Keep it to assets that are (a) on the critical path and (b) same-origin. Anything else is usually better left to normal browser discovery.

Route-scoped pushes for heavy sections. Register the middleware as a route middleware and attach it only where it pays off, e.g. an image-heavy dashboard or a marketing page with a hero image. This keeps API and JSON routes from carrying meaningless Link headers.

Route::middleware('http2push')->group(function () {
    Route::get('/pricing', 'PricingController@index');
});

Derive the asset list from the build manifest. Hardcoding hashed filenames breaks on every deploy. Build the push list from mix-manifest.json (Laravel Mix) or public/build/manifest.json (Vite) inside a service provider, then feed the resolved paths into the package's config at boot:

// AppServiceProvider::boot()
$manifest = json_decode(file_get_contents(public_path('mix-manifest.json')), true);
config(['http2serverpush.assets' => [
    $manifest['/js/app.js'],
    $manifest['/css/app.css'],
]]);

This is the single highest-value integration: it keeps pushes correct across cache-busting deploys without manual edits.

Push per-response from the controller. For pages where the pushed asset depends on the record being rendered (a product hero image, a chart library), collect the asset in the controller/response and let the middleware emit it. If the package version you have exposes a collector/facade, that is the intended extension point; otherwise a small Response macro or a view composer that appends Link headers works equally well:

$response = response()->view('product', compact('product'));
$response->headers->set('Link', '</img/'.$product->hero.'.jpg>; rel=preload; as=image', false);
return $response;

The false third argument to set() appends rather than overwrites, which matters when the middleware has already added its own Link header.

Layer with Early Hints at the edge. Modern deployments get more value from 103 Early Hints than from push. Cloudflare, Fastly, and nginx all accept the same Link: rel=preload header, so configuring this package is a prerequisite for either strategy — you can switch the edge behavior later without touching application code.

Testing. Add a feature test asserting the header is present so a config change doesn't silently disable it:

$this->get('/')
    ->assertHeader('Link');

Gotchas and Tips

The package does not push anything. It only writes Link headers. HTTP/2 server push was removed from Chrome in 106 and Firefox has disabled it; what remains useful is rel=preload, which browsers do honor. Treat this package as "preload header middleware" and expect the actual benefit to come from preload, not push, on modern clients.

You must enable it server-side. nginx needs http2_push_preload on; in the http2 server block; Apache needs H2Push on and H2PushResource; CDNs need their own toggle (Cloudflare, for example, has historically ignored origin push hints). If your curl -I shows the header but DevTools shows no push, this is why.

Terminating TLS at a load balancer changes the picture. HTTP/2 push only happens on the connection that serves the response — the edge, not the origin. If you push at the origin over HTTP/1.1 behind a proxy, you gain nothing. In containerized setups let the edge read the Link header and do the work.

Middleware ordering matters for Link headers you set yourself. The package appends its header during the response phase. If a controller sets Link with replace = true (the Laravel default for headers->set()), it wipes out the middleware's entries. Always append with false, or set your per-page headers in a middleware that runs after the package's.

Don't push cross-origin assets without crossorigin. A Link: <https://cdn.example.com/app.js>; rel=preload; as=script header on a page where the script tag has crossorigin will produce a second fetch, because the preload and the real request don't match. Either mark the preload crossorigin too or skip cross-origin assets entirely.

Watch for the double-fetch trap on already-cached assets. Browsers do not dedupe preloads against the HTTP cache reliably across versions, so pushing a large bundle that is usually cached can waste bandwidth. Keep pushes to assets that change per deploy (hashed bundles) or that are genuinely critical.

Config caching will bite you. If you set the asset list dynamically in a service provider, remember config:cache snapshots config at cache time. Either run php artisan config:clear after deploy or avoid mutating config in favor of a dedicated push collector bound in the container.

The package is unmaintained (last release 2021, Laravel 5 era). It works on modern Laravel because the API it touches — middleware and Link headers — is stable, but expect to fork or reimplement if you hit PHP 8.2+ deprecations. The entire behavior is roughly ten lines; a custom middleware is a legitimate alternative if you need control over ordering, per-response collectors, or nopush attributes.

Escape the URL in the header. URLs with commas or spaces break the Link header grammar. Wrap in angle brackets (the package does) and avoid pushing user-supplied paths without validation — header injection via asset names is a real risk if you build the list from request input.

Debugging checklist, in order: curl -sI for the header → browser DevTools Network panel filtered by "Other" for the preload → server access log to see whether the server issued a PUSH_PROMISE frame → edge/CDN documentation for whether they honor Link. Each step isolates a different layer, and most failed rollouts stop at the third.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings