Настройка WebP и оптимизация изображений на сайте в 2026

В 2026 году оптимизация изображений стала критически важной для успеха любого сайта. За последние два года я заметил, что клиенты всё чаще жалуются на медленную загрузку страниц, и в 80% случаев проблема именно в неоптимизированных картинках.

Формат WebP: почему это стандарт 2026 года

Честно говоря, ещё в 2021 году я относился к WebP скептически. Думал — очередной эксперимент Google, который так и останется нишевым решением. Но сейчас поддержка WebP есть в 97% браузеров, и отказываться от него просто глупо.

На практике WebP даёт сжатие на 25-35% лучше, чем JPEG, при том же качестве изображения. У меня был клиент с интернет-магазином обуви — после перехода на WebP размер страницы каталога уменьшился с 3.2 МБ до 2.1 МБ. PageSpeed Insights показал рост с 45 до 78 баллов на мобильных устройствах.

WebP поддерживает как lossy (с потерями), так и lossless (без потерь) сжатие. Плюс альфа-канал для прозрачности и анимацию — по сути, один формат заменяет JPEG, PNG и GIF. Это существенно упрощает работу с изображениями на сайте.

💡
Совет из практики: Не конвертируйте в WebP изображения размером меньше 10 КБ. Для мелких иконок и кнопок PNG часто даёт лучший результат, а экономия получается копеечная.

Настройка WebP в Nginx

Самый правильный способ внедрения WebP — настройка на уровне веб-сервера. Я обычно использую Nginx с модулем content negotiation. Вот конфигурация, которую применяю на большинстве проектов:

server {
    listen 80;
    server_name example.com;
    root /var/www/html;
    
    # Проверяем поддержку WebP браузером
    location ~* \.(jpe?g|png)$ {
        set $webp_suffix "";
        if ($http_accept ~* "image/webp") {
            set $webp_suffix ".webp";
        }
        
        # Пробуем найти WebP версию
        try_files $uri$webp_suffix $uri =404;
        
        # Устанавливаем правильный MIME-type
        location ~ \.webp$ {
            add_header Vary Accept;
            add_header Cache-Control "public, max-age=31536000";
        }
    }
    
    # Кэширование для всех изображений
    location ~* \.(jpg|jpeg|png|gif|webp|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header Vary Accept;
    }
}

Эта конфигурация автоматически отдаёт WebP-версию изображения, если браузер её поддерживает, и оригинал — если не поддерживает. Главное — не забыть создать WebP-версии всех изображений с тем же именем плюс расширение .webp.

А вот более продвинутая версия с обработкой ошибок:

location ~* \.(jpe?g|png)$ {
    set $webp_suffix "";
    set $webp_uri $uri;
    
    if ($http_accept ~* "image/webp") {
        set $webp_suffix ".webp";
        set $webp_uri "${uri}.webp";
    }
    
    # Сначала пробуем WebP
    try_files $webp_uri $uri =404;
    
    # Логирование для отладки
    access_log /var/log/nginx/webp_access.log combined;
}

WebP в Apache через .htaccess

Если у вас Apache (что встречается на shared-хостингах), настройка делается через .htaccess. Честно говоря, это менее элегантное решение, но работает:

RewriteEngine On

# Проверяем поддержку WebP
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} \.(jpe?g|png)$
RewriteCond %{REQUEST_FILENAME}\.webp -f
RewriteRule ^(.+)\.(jpe?g|png)$ $1.$2.webp [T=image/webp,E=accept:1,L]

# Устанавливаем правильные заголовки

    Header append Vary Accept env=REDIRECT_accept


# Добавляем MIME-type для WebP

    AddType image/webp .webp


# Кэширование изображений

    ExpiresActive on
    ExpiresByType image/webp "access plus 1 year"
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"

У меня был проект на WordPress, где хостинг не поддерживал Nginx. Эта .htaccess конфигурация сработала отлично — скорость загрузки главной страницы увеличилась на 40%.

⚠️
Внимание: Обязательно тестируйте .htaccess правила на staging-окружении. Неправильная конфигурация может сломать отображение всех изображений на сайте.

Конвертация изображений в WebP

Массовая конвертация — это отдельная головная боль. Я перепробовал кучу инструментов и остановился на нескольких проверенных решениях.

Для разовой конвертации использую cwebp из пакета webp-tools:

# Установка в Ubuntu/Debian
sudo apt install webp

# Конвертация одного файла
cwebp -q 80 input.jpg -o output.webp

# Массовая конвертация всех JPEG в папке
for file in *.jpg; do
    cwebp -q 80 "$file" -o "${file%.jpg}.webp"
done

# Для PNG с альфа-каналом
cwebp -q 80 -lossless input.png -o output.webp

Качество 80 — мой стандартный выбор. Оно даёт хороший баланс между размером файла и качеством изображения. Для фотографий товаров в интернет-магазинах иногда поднимаю до 85-90, для декоративных элементов могу опустить до 70.

На PHP проектах часто использую библиотеку Intervention Image:

 'imagick']);

function convertToWebP($inputPath, $outputPath, $quality = 80) {
    try {
        $image = Image::make($inputPath);
        
        // Изменяем размер если нужно
        if ($image->width() > 1920) {
            $image->resize(1920, null, function ($constraint) {
                $constraint->aspectRatio();
                $constraint->upsize();
            });
        }
        
        // Сохраняем в WebP
        $image->encode('webp', $quality)->save($outputPath);
        
        return true;
    } catch (Exception $e) {
        error_log("WebP conversion failed: " . $e->getMessage());
        return false;
    }
}

// Пример использования
$inputDir = '/var/www/html/images/';
$files = glob($inputDir . '*.{jpg,jpeg,png}', GLOB_BRACE);

foreach ($files as $file) {
    $webpFile = pathinfo($file, PATHINFO_DIRNAME) . '/' . 
                pathinfo($file, PATHINFO_FILENAME) . '.webp';
    
    if (!file_exists($webpFile)) {
        convertToWebP($file, $webpFile, 80);
        echo "Converted: " . basename($file) . " -> " . basename($webpFile) . "\n";
    }
}

Этот скрипт я запускаю через cron раз в день — он автоматически конвертирует новые изображения в WebP. Удобно для сайтов, где контент-менеджеры регулярно загружают фотографии.

Оптимизация размеров изображений

WebP — это только половина дела. Размер изображений не менее важен. В 2026 году я придерживаюсь следующих стандартов:

Был случай — клиент загружал фотографии товаров прямо с камеры, по 8-12 МБ каждая. Главная страница весила 45 МБ! После оптимизации размеров и конвертации в WebP вес снизился до 2.8 МБ, а время загрузки — с 18 секунд до 3.2 секунды.

Для автоматического ресайза на PHP использую такую функцию:

function optimizeImage($sourcePath, $targetPath, $maxWidth = 1920, $quality = 80) {
    $imageInfo = getimagesize($sourcePath);
    if (!$imageInfo) return false;
    
    $sourceWidth = $imageInfo[0];
    $sourceHeight = $imageInfo[1];
    $mimeType = $imageInfo['mime'];
    
    // Вычисляем новые размеры
    if ($sourceWidth > $maxWidth) {
        $targetWidth = $maxWidth;
        $targetHeight = round(($sourceHeight * $maxWidth) / $sourceWidth);
    } else {
        $targetWidth = $sourceWidth;
        $targetHeight = $sourceHeight;
    }
    
    // Создаём исходное изображение
    switch ($mimeType) {
        case 'image/jpeg':
            $sourceImage = imagecreatefromjpeg($sourcePath);
            break;
        case 'image/png':
            $sourceImage = imagecreatefrompng($sourcePath);
            break;
        case 'image/gif':
            $sourceImage = imagecreatefromgif($sourcePath);
            break;
        default:
            return false;
    }
    
    // Создаём целевое изображение
    $targetImage = imagecreatetruecolor($targetWidth, $targetHeight);
    
    // Сохраняем прозрачность для PNG
    if ($mimeType == 'image/png') {
        imagealphablending($targetImage, false);
        imagesavealpha($targetImage, true);
        $transparent = imagecolorallocatealpha($targetImage, 255, 255, 255, 127);
        imagefill($targetImage, 0, 0, $transparent);
    }
    
    // Ресемплируем
    imagecopyresampled(
        $targetImage, $sourceImage,
        0, 0, 0, 0,
        $targetWidth, $targetHeight,
        $sourceWidth, $sourceHeight
    );
    
    // Сохраняем
    $result = false;
    if (pathinfo($targetPath, PATHINFO_EXTENSION) == 'webp') {
        $result = imagewebp($targetImage, $targetPath, $quality);
    } else {
        $result = imagejpeg($targetImage, $targetPath, $quality);
    }
    
    // Освобождаем память
    imagedestroy($sourceImage);
    imagedestroy($targetImage);
    
    return $result;
}
ℹ️
Полезно знать: Современные браузеры кэшируют изображения очень агрессивно. После оптимизации картинок обязательно проверьте результат в режиме инкогнито или с принудительным обновлением (Ctrl+F5).

Ленивая загрузка изображений

Lazy loading в 2026 году — это уже стандарт, а не дополнительная фича. Браузеры нативно поддерживают атрибут loading="lazy", но я всё равно рекомендую использовать JavaScript-библиотеки для большего контроля.

Нативный lazy loading работает просто:

<img src="image.webp" alt="Описание" loading="lazy" width="800" height="600">

Но у него есть ограничения — нет контроля над порогом загрузки и обработки ошибок. Поэтому на серьёзных проектах я использую Intersection Observer API:

class LazyImageLoader {
    constructor(options = {}) {
        this.options = {
            rootMargin: options.rootMargin || '50px',
            threshold: options.threshold || 0.01,
            ...options
        };
        
        this.images = document.querySelectorAll('img[data-src]');
        this.imageObserver = null;
        
        this.init();
    }
    
    init() {
        if (!('IntersectionObserver' in window)) {
            this.loadAllImages();
            return;
        }
        
        this.imageObserver = new IntersectionObserver(
            this.onIntersection.bind(this),
            this.options
        );
        
        this.images.forEach(img => {
            this.imageObserver.observe(img);
        });
    }
    
    onIntersection(entries) {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                this.loadImage(entry.target);
                this.imageObserver.unobserve(entry.target);
            }
        });
    }
    
    loadImage(img) {
        const src = img.dataset.src;
        const srcset = img.dataset.srcset;
        
        // Показываем placeholder во время загрузки
        img.style.filter = 'blur(5px)';
        img.style.transition = 'filter 0.3s';
        
        const imageLoader = new Image();
        
        imageLoader.onload = () => {
            img.src = src;
            if (srcset) img.srcset = srcset;
            
            img.style.filter = 'none';
            img.classList.add('loaded');
        };
        
        imageLoader.onerror = () => {
            img.src = '/images/placeholder-error.png';
            img.classList.add('error');
        };
        
        imageLoader.src = src;
    }
    
    loadAllImages() {
        this.images.forEach(img => {
            img.src = img.dataset.src;
            if (img.dataset.srcset) {
                img.srcset = img.dataset.srcset;
            }
        });
    }
}

// Инициализация
document.addEventListener('DOMContentLoaded', () => {
    new LazyImageLoader({
        rootMargin: '100px',
        threshold: 0.1
    });
});

HTML для такой загрузки выглядит так:

<img 
    src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 800 600'%3E%3C/svg%3E"
    data-src="image.webp"
    data-srcset="image-400.webp 400w, image-800.webp 800w, image-1200.webp 1200w"
    alt="Описание изображения"
    width="800" 
    height="600"
    class="lazy-image"
>

У одного клиента после внедрения lazy loading время загрузки главной страницы уменьшилось с 4.2 до 1.8 секунды. На странице было 60+ изображений товаров, и загружались только первые 8-10 в видимой области.

Адаптивные изображения с srcset

В мобильном трафике нет смысла загружать изображения в Full HD разрешении. Атрибут srcset позволяет браузеру выбрать оптимальный размер картинки в зависимости от размера экрана и плотности пикселей.

Вот как я настраиваю responsive images на адаптивных сайтах:

<picture>
    <source 
        media="(max-width: 768px)" 
        srcset="mobile-400.webp 1x, mobile-800.webp 2x"
        type="image/webp"
    >
    <source 
        media="(max-width: 1200px)" 
        srcset="tablet-800.webp 1x, tablet-1200.webp 2x"
        type="image/webp"
    >
    <source 
        srcset="desktop-1200.webp 1x, desktop-1920.webp 2x"
        type="image/webp"
    >
    
    <!-- Fallback для старых браузеров -->
    <img 
        src="desktop-1200.jpg" 
        alt="Описание изображения"
        loading="lazy"
    >
</picture>

А вот более простой вариант с srcset:

<img 
    src="image-800.webp"
    srcset="image-400.webp 400w, 
            image-800.webp 800w, 
            image-1200.webp 1200w,
            image-1920.webp 1920w"
    sizes="(max-width: 768px) 100vw, 
           (max-width: 1200px) 50vw, 
           33vw"
    alt="Описание изображения"
    loading="lazy"
>

Для автоматической генерации разных размеров использую такой PHP-скрипт:

function generateResponsiveImages($sourcePath, $outputDir, $basename) {
    $sizes = [
        'mobile' => [400, 600],
        'tablet' => [800, 1000], 
        'desktop' => [1200, 1600],
        'large' => [1920, 2400]
    ];
    
    $results = [];
    
    foreach ($sizes as $name => $dimensions) {
        foreach ($dimensions as $width) {
            // JPEG версия
            $jpegPath = $outputDir . $basename . '-' . $width . '.jpg';
            if (optimizeImage($sourcePath, $jpegPath, $width, 85)) {
                $results[$name]['jpeg'][] = $jpegPath;
            }
            
            // WebP версия
            $webpPath = $outputDir . $basename . '-' . $width . '.webp';
            if (optimizeImage($sourcePath, $webpPath, $width, 80)) {
                $results[$name]['webp'][] = $webpPath;
            }
        }
    }
    
    return $results;
}

function generatePictureTag($images, $alt, $basename) {
    $html = "\n";
    
    // WebP sources
    foreach ($images as $device => $formats) {
        if (isset($formats['webp']) && count($formats['webp']) > 0) {
            $srcset = [];
            foreach ($formats['webp'] as $path) {
                $width = basename($path, '.webp');
                $width = substr($width, strrpos($width, '-') + 1);
                $srcset[] = $path . ' ' . $width . 'w';
            }
            
            $media = '';
            switch ($device) {
                case 'mobile':
                    $media = ' media="(max-width: 768px)"';
                    break;
                case 'tablet':
                    $media = ' media="(max-width: 1200px)"';
                    break;
            }
            
            $html .= '    ' . "\n";
        }
    }
    
    // Fallback JPEG
    if (isset($images['desktop']['jpeg'][0])) {
        $html .= '    ' . htmlspecialchars($alt) . '' . "\n";
    }
    
    $html .= "";
    
    return $html;
}
💡
Из опыта: Не создавайте слишком много вариантов размеров. 3-4 размера вполне достаточно. Больше — это лишняя нагрузка на сервер и усложнение деплоя.

CDN и облачная оптимизация изображений

Для крупных проектов ручная оптимизация изображений становится неэффективной. Я всё чаще использую облачные сервисы для автоматической обработки картинок.

Cloudinary — мой фаворит среди таких сервисов. Вот как я его настраиваю:

// Конфигурация Cloudinary
const cloudinary = require('cloudinary').v2;

cloudinary.config({
    cloud_name: 'your-cloud-name',
    api_key: 'your-api-key',
    api_secret: 'your-api-secret'
});

// Функция для генерации оптимизированного URL
function getOptimizedImageUrl(publicId, options = {}) {
    const defaultOptions = {
        fetch_format: 'auto',  // Автоматический выбор формата (WebP если поддерживается)
        quality: 'auto:good',  // Автоматическая оптимизация качества
        width: 'auto',         // Автоматический размер на основе DPR
        dpr: 'auto',          // Автоматическая плотность пикселей
        crop: 'fill',
        gravity: 'auto'       // Умная обрезка
    };
    
    return cloudinary.url(publicId, { ...defaultOptions, ...options });
}

// Примеры использования
const mobileImageUrl = getOptimizedImageUrl('sample-image', {
    width: 400,
    height: 300
});

const desktopImageUrl = getOptimizedImageUrl('sample-image', {
    width: 1200,
    height: 800,
    quality: 'auto:best'
});

А если бюджет не позволяет Cloudinary, настраиваю собственное решение с ImageMagick на сервере:

class ImageOptimizer {
    private $cacheDir;
    private $maxAge = 31536000; // 1 год
    
    public function __construct($cacheDir = '/var/www/cache/images/') {
        $this->cacheDir = $cacheDir;
        if (!is_dir($this->cacheDir)) {
            mkdir($this->cacheDir, 0755, true);
        }
    }
    
    public function optimize($imagePath, $width = null, $height = null, $quality = 80, $format = 'webp') {
        // Генерируем ключ для кэша
        $cacheKey = md5($imagePath . $width . $height . $quality . $format);
        $cachedPath = $this->cacheDir . $cacheKey . '.' . $format;
        
        // Проверяем кэш
        if (file_exists($cachedPath) && 
            filemtime($cachedPath) > filemtime($imagePath)) {
            return $cachedPath;
        }
        
        // Оптимизируем изображение
        $imagick = new Imagick($imagePath);
        
        // Изменяем размер если нужно
        if ($width || $height) {
            $imagick->resizeImage(
                $width ?: 0, 
                $height ?: 0, 
                Imagick::FILTER_LANCZOS, 
                1, 
                !($width && $height) // Сохраняем пропорции если задан только один размер
            );
        }
        
        // Устанавливаем формат и качество
        $imagick->setImageFormat($format);
        if (in_array($format, ['jpg', 'jpeg', 'webp'])) {
            $imagick->setImageCompressionQuality($quality);
        }
        
        // Удаляем EXIF данные
        $imagick->stripImage();
        
        // Сохраняем
        $imagick->writeImage($cachedPath);
        $imagick->destroy();
        
        return $cachedPath;
    }
    
    public function serve($imagePath, $params = []) {
        $width = isset($params['w']) ? (int)$params['w'] : null;
        $height = isset($params['h']) ? (int)$params['h'] : null;
        $quality = isset($params['q']) ? (int)$params['q'] : 80;
        $format = isset($params['f']) ? $params['f'] : 'webp';
        
        // Проверяем поддержку WebP
        if ($format === 'webp' && !$this->supportsWebP()) {
            $format = 'jpg';
        }
        
        $optimizedPath = $this->optimize($imagePath, $width, $height, $quality, $format);
        
        // Отдаём файл с правильными заголовками
        header('Content-Type: image/' . $format);
        header('Cache-Control: public, max-age=' . $this->maxAge);
        header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $this->maxAge) . ' GMT');
        
        readfile($optimizedPath);
        exit;
    }
    
    private function supportsWebP() {
        return strpos($_SERVER['HTTP_ACCEPT'] ?? '', 'image/webp') !== false;
    }
}

Про настройку CDN я подробно писал в статье о настройке CDN для сайта — рекомендую изучить, если планируете серьёзную оптимизацию.

WebP в WordPress

WordPress с версии 5.8 поддерживает WebP нативно, но я всё равно рекомендую использовать специализированные плагины для большего контроля над процессом оптимизации.

Мои топ-3 плагина для оптимизации изображений в WordPress:

Но честно говоря, я предпочитаю настраивать оптимизацию на уровне кода. Вот функции, которые добавляю в functions.php:

// Добавляем поддержку WebP
function add_webp_upload_mimes($existing_mimes) {
    $existing_mimes['webp'] = 'image/webp';
    return $existing_mimes;
}
add_filter('mime_types', 'add_webp_upload_mimes');

// Автоматическая конвертация загружаемых изображений в WebP
function auto_convert_to_webp($metadata, $attachment_id) {
    $upload_dir = wp_upload_dir();
    $file_path = get_attached_file($attachment_id);
    $file_info = pathinfo($file_path);
    
    // Проверяем, что это изображение JPEG или PNG
    if (!in_array(strtolower($file_info['extension']), ['jpg', 'jpeg', 'png'])) {
        return $metadata;
    }
    
    // Путь для WebP версии
    $webp_path = $file_info['dirname'] . '/' . $file_info['filename'] . '.webp';
    
    // Конвертируем в WebP
    if (function_exists('imagewebp')) {
        $image = null;
        
        switch (strtolower($file_info['extension'])) {
            case 'jpg':
            case 'jpeg':
                $image = imagecreatefromjpeg($file_path);
                break;
            case 'png':
                $image = imagecreatefrompng($file_path);
                // Сохраняем прозрачность
                imagealphablending($image, false);
                imagesavealpha($image, true);
                break;
        }
        
        if ($image) {
            imagewebp($image, $webp_path, 80);
            imagedestroy($image);
        }
    }
    
    // Создаём WebP версии для всех размеров
    if (isset($metadata['sizes']) && is_array($metadata['sizes'])) {
        foreach ($metadata['sizes'] as $size_name => $size_info) {
            $size_file_path = $upload_dir['path'] . '/' . $size_info['file'];
            $size_file_info = pathinfo($size_file_path);
            $size_webp_path = $size_file_info['dirname'] . '/' . $size_file_info['filename'] . '.webp';
            
            if (file_exists($size_file_path)) {
                $size_image = null;
                
                switch (strtolower($size_file_info['extension'])) {
                    case 'jpg':
                    case 'jpeg':
                        $size_image = imagecreatefromjpeg($size_file_path);
                        break;
                    case 'png':
                        $size_image = imagecreatefrompng($size_file_path);
                        imagealphablending($size_image, false);
                        imagesavealpha($size_image, true);
                        break;
                }
                
                if ($size_image) {
                    imagewebp($size_image, $size_webp_path, 80);
                    imagedestroy($size_image);
                }
            }
        }
    }
    
    return $metadata;
}
add_filter('wp_generate_attachment_metadata', 'auto_convert_to_webp', 10, 2);

// Функция для получения WebP URL изображения
function get_webp_image_url($attachment_id, $size = 'full') {
    $image_url = wp_get_attachment_image_url($attachment_id, $size);
    if (!$image_url) return false;
    
    $image_path = get_attached_file($attachment_id);
    if ($size !== 'full') {
        $image_meta = wp_get_attachment_metadata($attachment_id);
        if (isset($image_meta['sizes'][$size])) {
            $upload_dir = wp_upload_dir();
            $image_path = $upload_dir['path'] . '/' . $image_meta['sizes'][$size]['file'];
        }
    }
    
    $path_info = pathinfo($image_path);
    $webp_path = $path_info['dirname'] . '/' . $path_info['filename'] . '.webp';
    
    if (file_exists($webp_path)) {
        return str_replace(
            $path_info['basename'], 
            $path_info['filename'] . '.webp', 
            $image_url
        );
    }
    
    return $image_url;
}

Эти функции автоматически создают WebP-версии всех загружаемых изображений. Плюс можно получить WebP URL через функцию get_webp_image_url(). Подробнее о том, как ускорить WordPress, я писал в отдельной статье.

Мониторинг и измерение результатов

Оптимизация без измерения результатов — пустая трата времени. Я всегда отслеживаю несколько ключевых метрик до и после внедрения WebP.

Основные инструменты для мониторинга:

Для автоматического мониторинга настраиваю простой скрипт, который проверяет процент WebP-изображений на сайте:

function analyzeImageFormats($url) {
    $html = file_get_contents($url);
    if (!$html) return false;
    
    $dom = new DOMDocument();
    @$dom->loadHTML($html);
    $images = $dom->getElementsByTagName('img');
    
    $totalImages = $images->length;
    $webpImages = 0;
    $totalSize = 0;
    $webpSize = 0;
    
    foreach ($images as $img) {
        $src = $img->getAttribute('src');
        if (!$src) continue;
        
        // Делаем URL абсолютным
        if (strpos($src, 'http') !== 0) {
            $src = rtrim($url, '/') . '/' . ltrim($src, '/');
        }
        
        // Получаем информацию о файле
        $headers = @get_headers($src, true);
        if (!$headers) continue;
        
        $contentLength = isset($headers['Content-Length']) ? 
                        (int)$headers['Content-Length'] : 0;
        $contentType = isset($headers['Content-Type']) ? 
                      $headers['Content-Type'] : '';
        
        $totalSize += $contentLength;
        
        if (strpos($contentType, 'webp') !== false) {
            $webpImages++;
            $webpSize += $contentLength;
        }
    }
    
    return [
        'total_images' => $totalImages,
        'webp_images' => $webpImages,
        'webp_percentage' => $totalImages > 0 ? round(($webpImages / $totalImages) * 100, 2) : 0,
        'total_size' => $totalSize,
        'webp_size' => $webpSize,
        'size_savings' => $totalSize > 0 ? round((($totalSize - $webpSize) / $totalSize) * 100, 2) : 0
    ];
}

// Пример использования
$stats = analyzeImageFormats('https://example.com');
echo "WebP изображений: {$stats['webp_percentage']}%\n";
echo "Экономия трафика: {$stats['size_savings']}%\n";

Этот скрипт запускаю через cron раз в день и отправляю результаты в Slack. Помогает отслеживать, что все изображения действительно отдаются в оптимальном формате.

А для мониторинга Core Web Vitals использую комбинацию Google Analytics 4 и собственных метрик. Подробнее об улучшении Core Web Vitals я писал в отдельной статье.

⚠️
Важно: После внедрения WebP обязательно проверьте сайт в разных браузерах, включая старые версии Safari. Иногда fallback на JPEG срабатывает некорректно.

Типичные ошибки и их решения

За годы работы с WebP я наступил на все возможные грабли. Вот самые частые проблемы и их решения:

Проблема №1: WebP не отображается в Safari
Safari на macOS поддерживает WebP с версии 14, но на iOS — только с 14-й версии. Обязательно настраивайте fallback на JPEG/PNG.

Проблема №2: Неправильные MIME-типы
Некоторые хостинги не знают MIME-тип для WebP. Добавьте в .htaccess:

AddType image/webp .webp

Проблема №3: Кэширование старых изображений
После конвертации в WebP браузеры могут продолжать показывать старые JPEG. Очищайте кэш CDN и добавляйте версионность:

<img src="image.webp?v=91" alt="Изображение">

Проблема №4: Размер WebP больше оригинала
Иногда WebP может быть больше PNG для простых изображений с малым количеством цветов. В таких случаях лучше оставить PNG.

Проблема №5: Потеря прозрачности
При конвертации PNG в WebP можно потерять альфа-канал. Используйте параметр -lossless для изображений с прозрачностью:

cwebp -lossless input.png -o output.webp

У одного клиента была интересная проблема — WebP изображения загружались, но не кэшировались браузером. Оказалось, что сервер отдавал неправильный заголовок Vary. Решение:

location ~* \.webp$ {
    add_header Vary "Accept, Accept-Encoding";
    add_header Cache-Control "public, max-age=31536000, immutable";
}

В 2026 году оптимизация изображений — это не просто "сделал и забыл". Это постоянный процесс мониторинга, тестирования и улучшения. WebP значительно ускоряет загрузку сайтов, но только при правильной настройке всей цепочки: от конвертации до отдачи через CDN.

Если у вас возникают сложности с настройкой WebP или оптимизацией изображений, всегда можно обратиться за профессиональной поддержкой сайта — это сэкономит время и избавит от головной боли с техническими нюансами.

Нужна помощь с оптимизацией изображений на сайте?

Поможем настроить WebP и ускорить загрузку вашего сайта до 70%.

П
Павел
Веб-разработчик · 10+ лет опыта · Bitrix, WordPress, Laravel

Читайте также

Переезд с WordPress на Битрикс: пошаговый план Переход на HTTPS: полный чек-лист Настройка почты для сайта: SPF, DKIM, DMARC