symfony/web-link
Symfony WebLink component helps manage link relationships between resources. Create and serialize HTTP Link headers for preload, prefetch, and resource hints (HTML5/Web standards), enabling better performance via HTTP/2 push and client hints.
Installation:
composer require symfony/web-link
No Laravel-specific dependencies—works with vanilla PHP.
First Use Case: Preload a critical CSS file for a route:
use Symfony\Component\WebLink\GenericLinkProvider;
use Symfony\Component\WebLink\HttpHeaderSerializer;
use Symfony\Component\WebLink\Link;
// In a Laravel middleware or controller:
$linkProvider = (new GenericLinkProvider())
->withLink(new Link('preload', '/css/app.css', [
'as' => 'style',
'crossorigin' => 'anonymous',
]));
return response()
->header('Link', (new HttpHeaderSerializer())->serialize($linkProvider->getLinks()));
Where to Look First:
Link class constants (e.g., Link::AS_STYLE, Link::AS_SCRIPT) for standardized attributes.HttpHeaderSerializer for header formatting.Inject Link headers globally for specific routes:
// app/Http/Middleware/PreloadAssets.php
public function handle(Request $request, Closure $next) {
$response = $next($request);
$linkProvider = (new GenericLinkProvider())
->withLink(new Link('preload', asset('css/main.css'), [
'as' => 'style',
'media' => 'print',
]));
return $response->header('Link', (new HttpHeaderSerializer())->serialize($linkProvider->getLinks()));
}
Register in app/Http/Kernel.php:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\PreloadAssets::class,
// ...
],
];
Use Laravel’s Route::middleware() or controller logic:
// routes/web.php
Route::get('/dashboard', function () {
$linkProvider = (new GenericLinkProvider())
->withLink(new Link('preload', asset('js/dashboard.js'), [
'as' => 'script',
'fetchpriority' => 'high',
]));
return response()
->header('Link', (new HttpHeaderSerializer())->serialize($linkProvider->getLinks()))
->view('dashboard');
});
Attach Link headers to API responses (e.g., HATEOAS):
// app/Http/Controllers/Api/PostController.php
public function show(Post $post) {
$linkProvider = (new GenericLinkProvider())
->withLink(new Link('related', route('posts.related', $post), ['title' => 'Related Posts']))
->withLink(new Link('next', route('posts.next', $post), ['title' => 'Next Page']));
return response()->json($post->toArray())
->header('Link', (new HttpHeaderSerializer())->serialize($linkProvider->getLinks()));
}
Generate <link> tags dynamically:
// app/Helpers/WebLinkHelper.php
function weblink_tags($rel, $href, $attributes = []) {
$link = new Link($rel, $href, $attributes);
return (new HtmlLinkSerializer())->serialize($link);
}
Usage in Blade:
{{ weblink_tags('preconnect', 'https://cdn.example.com', ['crossorigin']) }}
Use Link headers as push candidates (requires server config):
// app/Http/Middleware/PushAssets.php
public function handle($request, Closure $next) {
$response = $next($request);
if ($request->is('*')) {
$linkProvider = (new GenericLinkProvider())
->withLink(new Link('preload', asset('js/vendor.js'), ['as' => 'script']))
->withLink(new Link('preload', asset('fonts/inter.woff2'), ['as' => 'font']));
return $response->header('Link', (new HttpHeaderSerializer())->serialize($linkProvider->getLinks()));
}
return $response;
}
Nginx Example:
location / {
proxy_pass http://app;
proxy_set_header Link $http_link;
push_preload on;
}
Service Container Binding:
Bind GenericLinkProvider to reuse across requests:
// app/Providers/AppServiceProvider.php
public function register() {
$this->app->singleton(GenericLinkProvider::class, function () {
return new GenericLinkProvider();
});
}
Usage:
$this->app->make(GenericLinkProvider::class)
->withLink(new Link('prefetch', '/api/data'));
Caching Link Providers:
Cache static links (e.g., for /robots.txt or /sitemap.xml):
$linkProvider = Cache::remember('weblink.assets', now()->addHour(), function () {
return (new GenericLinkProvider())
->withLink(new Link('preload', asset('css/app.css')))
->withLink(new Link('preconnect', 'https://fonts.gstatic.com'));
});
Environment-Specific Links: Use config to toggle links per environment:
// config/weblink.php
'enabled' => env('WEBLINK_ENABLED', true),
'links' => [
'preload' => [
'css/app.css' => ['as' => 'style'],
'js/app.js' => ['as' => 'script'],
],
],
Middleware:
if (config('weblink.enabled')) {
$linkProvider = collect(config('weblink.links'))
->map(fn ($attrs, $href) => new Link('preload', $href, $attrs))
->reduce(fn ($provider, $link) => $provider->withLink($link), new GenericLinkProvider());
}
Testing Headers:
Use Laravel’s Testing\TestResponse to assert headers:
$response = $this->get('/dashboard');
$response->assertHeader('Link', 'preload="/css/app.css;as=style"');
Header Injection Timing:
finishRequest() event or ensure middleware runs early in the stack.Duplicate Links:
Link, causing malformed headers.GenericLinkProvider::getLinks() to merge links or deduplicate:
$provider = $provider->withLinks($newLinks)->getLinks()->unique();
Non-Standard Link Types:
rel values (e.g., rel="modulepreload") may not be supported in all browsers.HTTP/2 Server Push Limitations:
curl -v or Chrome DevTools’ "Network" tab (check "Push" column).Case Sensitivity:
Link header values are case-sensitive (e.g., preload vs Preload).Link::REL_PRELOAD:
new Link(Link::REL_PRELOAD, '/file.css', ['as' => Link::AS_STYLE]);
Cross-Origin Restrictions:
crossorigin="anonymous") may fail if the server lacks CORS headers.curl -I or browser dev tools.Inspect Headers:
curl -I http://example.com/dashboard.Validate Serialization:
HttpHeaderSerializer::serialize() to debug malformed headers:
$serializer = new HttpHeaderSerializer();
$links = $linkProvider->getLinks();
dd($serializer->serialize
How can I help you explore Laravel packages today?