jeffersongoncalves / laravel-favicon-proxy
A Laravel package that proxies and server-side caches website favicons (Google S2 by default) behind a same-origin route, so visitors' browsers never hit the third-party favicon service. Validates the upstream content-type, sends nosniff, negative-caches failures and falls back to a transparent pixe
Package info
github.com/jeffersongoncalves/laravel-favicon-proxy
pkg:composer/jeffersongoncalves/laravel-favicon-proxy
Fund package maintenance!
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.8
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/routing: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- spatie/laravel-package-tools: ^1.14
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.0
- orchestra/testbench: ^9.0|^10.0|^11.0
- pestphp/pest: ^3.7.4|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
This package is auto-updated.
Last update: 2026-06-23 11:52:40 UTC
README
Laravel Favicon Proxy
Proxy website favicons through your own host. Instead of pointing an external-link <img> straight at a third-party favicon service (a cross-origin connection on every page, leaking which links a visitor sees), this fetches the icon server-side once, caches the bytes, and serves them same-origin — the browser never talks to the upstream.
- Default upstream is Google's S2 favicon service; configurable.
- Allowlists raster image content-types (
image/png,image/jpeg,image/gif,image/webp,image/x-icon,image/vnd.microsoft.icon) and sendsX-Content-Type-Options: nosniff.image/svg+xmland any non-image response are rejected — an SVG served same-origin can carry<script>, so it would otherwise be a stored-XSS vector. - Caps the upstream response size (
max_bytes, default 100 KB) to guard against an oversized body. - Serialises concurrent misses for the same domain with an atomic cache lock (cache-stampede guard).
- Negative-caches failures, falls back to a transparent 1×1 pixel.
Installation
composer require jeffersongoncalves/laravel-favicon-proxy
The /favicon-proxy route is registered automatically. Point your icons at it:
<img src="{{ route('favicon-proxy', ['domain' => 'laravel.com']) }}" alt="" width="16" height="16">
Optionally publish the config:
php artisan vendor:publish --tag="favicon-proxy-config"
Configuration
| Key | Default | Description |
|---|---|---|
enabled |
true |
Register the proxy route. |
path |
favicon-proxy |
Route path. |
middleware |
['throttle:120,1'] |
Middleware on the route. |
endpoint |
https://www.google.com/s2/favicons |
Upstream favicon service. |
query |
domain |
Query parameter carrying the domain. |
size |
64 |
Requested icon size (px). |
timeout |
6 |
Upstream request timeout (s). |
max_bytes |
102400 |
Maximum upstream response size (bytes); larger responses are rejected. |
cache_prefix |
favicon |
Cache key prefix. |
cache_days |
30 |
How long a fetched icon is cached. |
negative_cache_hours |
6 |
How long a failure is negative-cached. |
Security
The proxy only fetches from the fixed endpoint host (the visitor controls only the domain query string, which the upstream service itself resolves), so it is not an open SSRF gateway. This guarantee depends entirely on favicon-proxy.endpoint being a trusted constant — never derive it from request input, a database value an end user can edit, or any other untrusted source, or you reintroduce SSRF. Keep the route behind the default throttle middleware (or your own) as well.
Testing
composer test
License
The MIT License (MIT). Please see License File for more information.