Optimalizace obrázků v Laravelu: WebP, HEIC a responsivní picture

Jak v Laravelu automaticky převádím obrázky do WebP, řeším HEIC z iPhonu a servíruji responsivní picture s cachovanými rozměry.
Publikováno 17.08.2026

Optimalizace obrázků v Laravelu: WebP, HEIC a responsivní picture

Publikováno

· Autor: · Pod kapotou · 3 min čtení

Obsah článku

Obrázky jsou skoro vždy největší položka na stránce. Nepřevedené fotky z mobilu dokážou nafouknout galerii na desítky megabajtů a shodit Core Web Vitals. Ukážu, jak to řeším v Laravelu — automatický převod do WebP, konverze HEIC z iPhonu a responsivní <picture> s cachovanými rozměry.

Převod do WebP na pozadí

WebP je při stejné kvalitě zhruba o třetinu menší než JPEG. Nechci ale konverzi řešit ručně, tak si držím službu, která WebP variantu vygeneruje jednou a pak už jen vrací její URL.

use Illuminate\Support\Facades\Storage;
use Intervention\Image\ImageManager;

public function webpUrlFor(string $relative): ?string
{
    $target = preg_replace('/\.\w+$/', '.webp', $relative);

    if (! Storage::disk('public')->exists($target)) {
        $source = Storage::disk('public')->path($relative);
        $image = $this->manager->read($source);
        Storage::disk('public')->put($target, (string) $image->toWebp(quality: 82));
    }

    return Storage::url($target);
}

Kvalita 82 je rozumný kompromis — na fotkách okem nepoznáte rozdíl, ale soubor spadne výrazně dolů.

HEIC a HEVC z iPhonu

Fotky z iPhonu chodí jako HEIC, videa jako HEVC — formáty, které polovina prohlížečů nezobrazí. Při nahrání je proto převádím na univerzální JPEG (respektive H.264), aby je přehrál kdokoli. Detekci řeším podle přípony a MIME typu, samotný převod přes imagick, u videa přes ffmpeg ve frontě.

if (in_array($file->getClientOriginalExtension(), ['heic', 'heif'])) {
    $jpeg = $this->manager->read($file->getRealPath())->toJpeg(quality: 85);
    Storage::disk('public')->put($path.'.jpg', (string) $jpeg);
}

Responsivní picture s cachovanými rozměry

Aby se stránka neposkakovala při načítání (CLS), potřebuje <img> znát rozměry předem. Čtení rozměrů z disku u každého requestu by ale bylo drahé, tak si je cachuju.

$meta = Cache::remember('img:meta:'.md5($relative), now()->addHours(6), fn () => [
    'webp' => $optimizer->webpUrlFor($relative),
    'dim'  => $optimizer->dimensionsFor($relative),
]);

Výstup pak zabalím do jedné Blade komponenty, kterou používám všude — od výpisu projektů po hero obrázek článku:

<x-responsive-image
    :path="$news->image"
    :alt="$news->title"
    loading="lazy"
    sizes="(min-width: 992px) 720px, 100vw" />

Komponenta vyplní width, height, WebP <source> i aspect-ratio ve stylu. Prohlížeč si tak rezervuje místo dřív, než obrázek dorazí.

Shrnutí technických rozhodnutí

  • Generuj jednou, servíruj vždy. WebP i náhledy vznikají líně při prvním požadavku a pak se jen čtou.

  • Rozměry cachuj. Ušetří to čtení souboru u každého renderu a drží CLS na nule.

  • Lazy loading + fetchpriority. Hero obrázek eager a high, zbytek lazy.

Závěr

Optimalizace obrázků není jednorázová akce, ale vrstva, která běží automaticky při každém uploadu. Nejvíc se vyplatí u galerií — přesně to jsem řešil u svatebního fotokoutku mezi projekty. Svižný web pak lépe konvertuje a míň zatěžuje server — širší pohled na výkon rozebírám v článku o optimalizaci výkonu Laravel aplikace a data z galerie zpřístupníte přes REST API. Potřebujete zrychlit existující web? Napište mi.