Core Web Vitals: как улучшить показатели

Когда Google в 2021 году официально включил Core Web Vitals в алгоритм ранжирования, я понял — это не просто очередная метрика для галочки. За 10+ лет работы с сайтами видел, как изменение скорости загрузки на 2-3 секунды влияет на конверсию клиентов.

Что такое Core Web Vitals и почему они важны

Core Web Vitals — это три ключевых показателя пользовательского опыта, которые Google считает критически важными для всех сайтов. Честно говоря, когда я первый раз увидел эти метрики в PageSpeed Insights, подумал: "Ещё одна головная боль для разработчиков". Но на деле оказалось — это именно то, что нужно было формализовать.

Три основных показателя:

У меня был клиент с интернет-магазином на WordPress — трафик хороший, товары качественные, а продажи никак не росли. Проверили Core Web Vitals: LCP 8.2 секунды, CLS 0.45. После оптимизации — LCP 2.1 секунды, CLS 0.05, конверсия выросла на 34%. И это не единичный случай.

ℹ️
Хорошие показатели Core Web Vitals: LCP ≤ 2.5 сек, FID ≤ 100 мс, CLS ≤ 0.1. Эти значения должны быть достигнуты для 75% всех посещений страницы.

Google не просто так ввёл эти метрики. По их исследованиям, сайты с плохими Core Web Vitals теряют 24% больше пользователей на первой же странице. А для мобильного трафика эта цифра ещё выше — до 40%.

LCP (Largest Contentful Paint) — оптимизация скорости загрузки

LCP измеряет время загрузки самого крупного элемента в видимой области. Обычно это главное изображение, видео или большой текстовый блок. На моей практике 80% проблем с LCP связаны с неоптимизированными изображениями и медленным сервером.

Вот что я всегда проверяю в первую очередь:

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

Самая частая причина плохого LCP — тяжёлые картинки. Был случай: клиент загружал фото товаров прямо с фотоаппарата — по 8-12 МБ каждое. LCP был 15+ секунд. После сжатия до 200-300 КБ и конвертации в WebP получили LCP 2.8 секунды.

Я всегда использую этот nginx-конфиг для автоматической подачи WebP:

location ~* \.(png|jpg|jpeg)$ {
    add_header Vary Accept;
    try_files $uri$webp_suffix $uri =404;
    expires 1y;
    add_header Cache-Control "public, immutable";
}

map $http_accept $webp_suffix {
    "~*webp" ".webp";
}

Для WordPress обязательно настраиваю ленивую загрузку через Intersection Observer API. Стандартный loading="lazy" работает не везде стабильно, особенно в старых браузерах.

Настройка сервера

Второй по важности фактор — время отклика сервера (TTFB). Если сервер отвечает дольше 600 мс, хорошего LCP не видать. У одного клиента на Bitrix был TTFB 2.3 секунды — проблема оказалась в неоптимизированных SQL-запросах к базе данных.

Вот что помогло:

-- Добавил индексы для частых запросов
ALTER TABLE b_iblock_element ADD INDEX idx_iblock_active (IBLOCK_ID, ACTIVE);
ALTER TABLE b_catalog_price ADD INDEX idx_product_type (PRODUCT_ID, CATALOG_GROUP_ID);

-- Оптимизировал запрос выборки товаров
SELECT e.ID, e.NAME, p.PRICE 
FROM b_iblock_element e
INNER JOIN b_catalog_price p ON e.ID = p.PRODUCT_ID
WHERE e.IBLOCK_ID = 2 AND e.ACTIVE = 'Y'
ORDER BY e.SORT ASC
LIMIT 20;
💡
Лайфхак: Используйте CDN для статики. У меня клиенты на Cloudflare в среднем получают улучшение LCP на 40-60%. Особенно заметно для пользователей из регионов.

Приоритизация критических ресурсов

Если главное изображение загружается последним — LCP будет плохим. Я всегда добавляю rel="preload" для hero-изображений:

<link rel="preload" as="image" href="/images/hero-banner.webp">
<link rel="preload" as="font" href="/fonts/main.woff2" type="font/woff2" crossorigin>

И обязательно убираю блокирующие CSS и JavaScript из критического пути. Грубо говоря, всё что не нужно для первого экрана — загружаем асинхронно.

FID (First Input Delay) — улучшение отзывчивости

FID показывает, насколько быстро сайт реагирует на действия пользователя. Плохой FID — это когда пользователь нажал кнопку, а ничего не происходит. На мобильных устройствах это особенно критично.

Основная причина плохого FID — блокирующий JavaScript. Браузер не может обработать клик, пока не выполнит весь JS в главном потоке. У меня был проект на Laravel с FID 890 мс — виноваты были тяжёлые библиотеки аналитики и чат-виджеты.

Оптимизация JavaScript

Первое правило — разделяй и властвуй. Весь JS делю на критический (нужен сразу) и второстепенный (можно загрузить потом). Для второстепенного использую dynamic imports:

// Вместо импорта всей библиотеки сразу
import Chart from 'chart.js';

// Загружаем только когда нужно
async function loadChart() {
    const { Chart } = await import('chart.js');
    return Chart;
}

// Или по событию пользователя
document.getElementById('show-chart').addEventListener('click', async () => {
    const Chart = await loadChart();
    // Инициализируем график
});

Второй важный момент — web workers для тяжёлых вычислений. Если нужно обработать большой массив данных или сделать сложные расчёты, выношу это в worker:

// main.js
const worker = new Worker('/js/data-processor.js');
worker.postMessage({data: largeDataset});

worker.onmessage = function(e) {
    const processedData = e.data;
    updateUI(processedData);
};

// data-processor.js
self.onmessage = function(e) {
    const data = e.data.data;
    const result = processLargeDataset(data);
    self.postMessage(result);
};

Оптимизация сторонних скриптов

Google Analytics, Яндекс.Метрика, чаты, рекламные сети — все эти скрипты могут убить FID. Я всегда загружаю их асинхронно и с задержкой:

// Загружаем аналитику только после взаимодействия с пользователем
let analyticsLoaded = false;

function loadAnalytics() {
    if (analyticsLoaded) return;
    
    // Google Analytics
    const gtag = document.createElement('script');
    gtag.async = true;
    gtag.src = 'https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID';
    document.head.appendChild(gtag);
    
    analyticsLoaded = true;
}

// Загружаем при первом взаимодействии
['mousedown', 'touchstart', 'keydown', 'scroll'].forEach(event => {
    document.addEventListener(event, loadAnalytics, { once: true, passive: true });
});

// Или через 3 секунды, если пользователь не взаимодействовал
setTimeout(loadAnalytics, 3000);
⚠️
Осторожно с полифиллами: Не подключайте полифиллы для всех браузеров сразу. Используйте conditional loading — загружайте только для браузеров, которым это действительно нужно.

CLS (Cumulative Layout Shift) — стабильность макета

CLS измеряет, насколько сильно "прыгает" контент при загрузке. Классическая ситуация: пользователь хочет нажать кнопку, а в этот момент загружается баннер сверху, всё сдвигается, и он случайно кликает по рекламе. Раздражает безумно.

На моей практике главные виновники плохого CLS:

Изображения без размеров

Если не указать размеры изображения, браузер сначала покажет пустое место, а потом "растолкает" контент, когда картинка загрузится. Всегда указываю width и height:

<!-- Плохо -->
<img src="product.jpg" alt="Товар">

<!-- Хорошо -->
<img src="product.jpg" alt="Товар" width="400" height="300">

<!-- Ещё лучше с aspect-ratio -->
<img src="product.jpg" alt="Товар" width="400" height="300" 
     style="aspect-ratio: 4/3; width: 100%; height: auto;">

Для адаптивных изображений использую CSS aspect-ratio:

.product-image {
    aspect-ratio: 4 / 3;
    width: 100%;
    height: auto;
    object-fit: cover;
}

/* Fallback для старых браузеров */
.product-image {
    width: 100%;
    padding-bottom: 75%; /* 3/4 = 0.75 */
    height: 0;
    position: relative;
}

.product-image img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

Динамический контент

Баннеры, уведомления, формы обратной связи — всё что появляется после загрузки страницы, может испортить CLS. Я всегда резервирую место под такой контент:

/* Резервируем место под баннер */
.banner-placeholder {
    min-height: 200px;
    background: #f5f5f5;
    display: flex;
    align-items: center;
    justify-content: center;
}

.banner-placeholder.loaded {
    min-height: auto;
    background: none;
}

Для уведомлений использую position: fixed, чтобы они не сдвигали основной контент:

.notification {
    position: fixed;
    top: 20px;
    right: 20px;
    z-index: 1000;
    /* Не влияет на layout */
}

Web-шрифты

Загрузка веб-шрифтов может вызвать FOUT (Flash of Unstyled Text) или FOIT (Flash of Invisible Text). И то, и другое влияет на CLS. Я всегда использую font-display: swap и предзагрузку:

<link rel="preload" href="/fonts/main-regular.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/main-bold.woff2" as="font" type="font/woff2" crossorigin>
@font-face {
    font-family: 'Main';
    src: url('/fonts/main-regular.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap; /* Показывает fallback шрифт сразу */
}

body {
    font-family: 'Main', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
💡
Проверяйте CLS в реальных условиях: Используйте Chrome DevTools с медленным 3G соединением. То что работает на быстром интернете, может сильно "прыгать" на медленном.

Инструменты для измерения Core Web Vitals

За годы работы перепробовал кучу инструментов. Сейчас использую связку из нескольких сервисов — каждый даёт свою картину.

Google PageSpeed Insights

Начинаю всегда с PageSpeed Insights. Даёт как лабораторные данные (синтетические тесты), так и полевые данные (от реальных пользователей). Лабораторные показывают потенциал, полевые — реальность.

Но есть нюанс: PageSpeed часто показывает мобильную версию хуже, чем она есть на самом деле. Связано с тем, что тест идёт с медленного устройства и 4G соединения.

Chrome DevTools

Для детальной диагностики использую вкладку Performance в Chrome DevTools. Особенно полезен режим throttling — можно эмулировать медленные устройства и соединения.

Полезные фичи, которые многие не знают:

Web Vitals Extension

Ставлю расширение Web Vitals в Chrome — показывает метрики прямо на странице в реальном времени. Удобно для быстрой проверки после изменений.

Real User Monitoring (RUM)

Для серьёзных проектов настраиваю сбор реальных метрик от пользователей. Использую web-vitals библиотеку от Google:

import {getCLS, getFID, getFCP, getLCP, getTTFB} from 'web-vitals';

function sendToAnalytics(metric) {
    const body = JSON.stringify(metric);
    
    // Используем sendBeacon для надёжной отправки
    if (navigator.sendBeacon) {
        navigator.sendBeacon('/analytics/web-vitals', body);
    } else {
        fetch('/analytics/web-vitals', {
            body,
            method: 'POST',
            keepalive: true,
            headers: {
                'Content-Type': 'application/json',
            },
        });
    }
}

// Отправляем метрики
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);

Эти данные потом анализирую в Google Analytics или отправляю в собственную систему мониторинга. Помогает понять, как сайт работает для разных групп пользователей.

Настройка сервера и хостинга для Core Web Vitals

Хороший хостинг — это 50% успеха в оптимизации Core Web Vitals. У меня есть клиенты на дешёвом shared хостинге, где даже после всех оптимизаций LCP не опускается ниже 4-5 секунд. А есть проекты на выделенных серверах с NVMe дисками — там LCP 1.2-1.8 секунды без особых усилий.

Выбор правильного хостинга

Вот на что я обращаю внимание при выборе хостинга для клиентов:

Для WordPress проектов рекомендую управляемый хостинг типа WP Engine или Kinsta. Да, дороже обычного shared, но Core Web Vitals там "из коробки" намного лучше. Подробнее о выборе хостинга писал в отдельной статье.

Настройка nginx

Вот мой стандартный конфиг nginx для оптимизации Core Web Vitals:

# Включаем gzip сжатие
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
    text/plain
    text/css
    text/xml
    text/javascript
    application/json
    application/javascript
    application/xml+rss
    application/atom+xml
    image/svg+xml;

# Brotli сжатие (если модуль установлен)
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml;

# Кеширование статических файлов
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    access_log off;
}

# Предзагрузка критических ресурсов
location = / {
    add_header Link "; rel=preload; as=style" always;
    add_header Link "; rel=preload; as=script" always;
    add_header Link "; rel=preload; as=font; type=font/woff2; crossorigin" always;
}

# HTTP/2 Server Push (если поддерживается)
location = /index.html {
    http2_push /css/main.css;
    http2_push /js/app.js;
}

Оптимизация базы данных

Медленные запросы к базе — частая причина плохого LCP. Особенно на WordPress и Bitrix проектах. Вот что я проверяю в первую очередь:

-- Находим медленные запросы
SHOW PROCESSLIST;

-- Включаем логирование медленных запросов
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;

-- Анализируем производительность
EXPLAIN SELECT * FROM wp_posts 
WHERE post_status = 'publish' 
AND post_type = 'product' 
ORDER BY post_date DESC 
LIMIT 10;

-- Добавляем нужные индексы
ALTER TABLE wp_posts 
ADD INDEX idx_status_type_date (post_status, post_type, post_date);

Для Bitrix у меня есть стандартный набор индексов, которые улучшают производительность в 90% случаев. Подробнее об этом писал в статье про оптимизацию MySQL.

⚠️
Не переоптимизируйте: Слишком много индексов может замедлить INSERT/UPDATE операции. Добавляйте индексы только для действительно медленных запросов.

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

Изображения — это 60-70% веса большинства современных сайтов. И основная причина плохого LCP. За годы работы выработал чёткий алгоритм оптимизации, который применяю для всех проектов.

Форматы изображений

WebP и AVIF дают сжатие на 25-50% лучше JPEG при том же качестве. Но поддержка AVIF пока не везде есть, поэтому использую прогрессивную загрузку:

<picture>
    <source srcset="image.avif" type="image/avif">
    <source srcset="image.webp" type="image/webp">
    <img src="image.jpg" alt="Описание" width="800" height="600">
</picture>

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

&php
function generateWebP($source, $destination, $quality = 80) {
    $info = getimagesize($source);
    
    if ($info['mime'] == 'image/jpeg') {
        $image = imagecreatefromjpeg($source);
    } elseif ($info['mime'] == 'image/png') {
        $image = imagecreatefrompng($source);
        imagealphablending($image, true);
        imagesavealpha($image, true);
    } else {
        return false;
    }
    
    return imagewebp($image, $destination, $quality);
}

// Автоматическая конвертация при загрузке
function autoConvertToWebP($file_path) {
    $webp_path = preg_replace('/\.(jpg|jpeg|png)$/i', '.webp', $file_path);
    
    if (!file_exists($webp_path)) {
        generateWebP($file_path, $webp_path, 85);
    }
    
    return $webp_path;
}
?>

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

Зачем загружать изображение 2000px на экран 375px? Всегда генерирую несколько размеров:

<img src="image-800.webp" 
     srcset="image-400.webp 400w, 
             image-800.webp 800w, 
             image-1200.webp 1200w"
     sizes="(max-width: 768px) 100vw, 
            (max-width: 1024px) 50vw, 
            33vw"
     alt="Описание"
     width="800" 
     height="600">

Для WordPress использую функцию add_image_size() и генерирую нужные размеры автоматически:

// functions.php
add_theme_support('post-thumbnails');

// Добавляем кастомные размеры
add_image_size('mobile', 400, 300, true);
add_image_size('tablet', 800, 600, true);
add_image_size('desktop', 1200, 900, true);

// Функция для генерации srcset
function get_responsive_image($attachment_id, $alt = '') {
    $mobile = wp_get_attachment_image_src($attachment_id, 'mobile');
    $tablet = wp_get_attachment_image_src($attachment_id, 'tablet');
    $desktop = wp_get_attachment_image_src($attachment_id, 'desktop');
    
    $srcset = sprintf(
        '%s 400w, %s 800w, %s 1200w',
        $mobile[0],
        $tablet[0], 
        $desktop[0]
    );
    
    return sprintf(
        '<img src="%s" srcset="%s" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw" alt="%s" width="%d" height="%d">',
        $tablet[0],
        $srcset,
        esc_attr($alt),
        $tablet[1],
        $tablet[2]
    );
}

Ленивая загрузка

Стандартный loading="lazy" работает хорошо, но не везде. Для критически важных изображений (hero-баннеры, логотипы) его не использую — они должны загружаться сразу.

Для более точного контроля использую Intersection Observer:

// Ленивая загрузка с Intersection Observer
const lazyImages = document.querySelectorAll('img[data-src]');

const imageObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            const img = entry.target;
            img.src = img.dataset.src;
            img.srcset = img.dataset.srcset || '';
            img.classList.remove('lazy');
            img.classList.add('loaded');
            
            imageObserver.unobserve(img);
        }
    });
}, {
    rootMargin: '50px 0px' // Начинаем загрузку за 50px до появления
});

lazyImages.forEach(img => {
    imageObserver.observe(img);
});

И соответствующий CSS для плавного появления:

img.lazy {
    opacity: 0;
    transition: opacity 0.3s ease;
}

img.loaded {
    opacity: 1;
}

/* Placeholder пока изображение не загрузилось */
img.lazy::before {
    content: '';
    display: block;
    background: #f0f0f0;
    width: 100%;
    height: 100%;
}

CMS-специфичные оптимизации

Каждая CMS имеет свои особенности, которые влияют на Core Web Vitals. За годы работы с разными системами накопил список проверенных оптимизаций для каждой.

WordPress оптимизация

WordPress "из коробки" не очень дружит с Core Web Vitals. Но правильная настройка творит чудеса. Вот мой стандартный чек-лист:

1. Кеширование — обязательно использую WP Rocket или W3 Total Cache. Настройки кеширования подробно описывал в статье про ускорение WordPress.

2. Оптимизация базы данных — убираю ревизии, спам-комментарии, неиспользуемые плагины:

-- Удаляем старые ревизии (оставляем последние 3)
DELETE p1 FROM wp_posts p1
INNER JOIN wp_posts p2
WHERE p1.post_parent = p2.post_parent
AND p1.post_type = 'revision'
AND p2.post_type = 'revision'
AND p1.post_date < p2.post_date;

-- Очищаем корзину
DELETE FROM wp_posts WHERE post_status = 'trash';

-- Удаляем спам-комментарии
DELETE FROM wp_comments WHERE comment_approved = 'spam';

-- Оптимизируем таблицы
OPTIMIZE TABLE wp_posts, wp_comments, wp_postmeta, wp_options;

3. Критический CSS — генерирую критический CSS для главной страницы и категорий товаров:

// functions.php
function inline_critical_css() {
    if (is_front_page()) {
        echo '<style>' . file_get_contents(get_template_directory() . '/css/critical-home.css') . '</style>';
    } elseif (is_category()) {
        echo '<style>' . file_get_contents(get_template_directory() . '/css/critical-category.css') . '</style>';
    }
}
add_action('wp_head', 'inline_critical_css', 1);

// Отложенная загрузка основного CSS
function defer_main_css() {
    echo '<link rel="preload" href="' . get_stylesheet_uri() . '" as="style" onload="this.onload=null;this.rel=\'stylesheet\'">';
    echo '<noscript><link rel="stylesheet" href="' . get_stylesheet_uri() . '"></noscript>';
}
add_action('wp_head', 'defer_main_css', 20);

Bitrix оптимизация

Bitrix изначально тяжеловат, но при правильной настройке показывает отличные результаты. Основные точки оптимизации:

1. Композитный сайт — включаю всегда, даже для интернет-магазинов. Настройки кеширования описывал в отдельной статье.

2. Оптимизация компонентов — убираю лишние поля из выборки, использую постраничную навигацию:

// Оптимизированная выборка товаров
$arSelect = [
    'ID', 
    'NAME', 
    'DETAIL_PAGE_URL',
    'PREVIEW_PICTURE',
    'PROPERTY_PRICE'
];

$arFilter = [
    'IBLOCK_ID' => 2,
    'ACTIVE' => 'Y',
    'SECTION_ID' => $sectionId
];

// Используем GetList вместо GetElementList для лучшей производительности
$rsElements = CIBlockElement::GetList(
    ['SORT' => 'ASC'],
    $arFilter,
    false,
    ['nPageSize' => 20], // Пагинация
    $arSelect
);

3. Отключение ненужных модулей — в админке отключаю модули, которые не используются. Каждый модуль добавляет свои CSS/JS файлы.

Laravel оптимизация

Laravel проекты обычно показывают хорошие Core Web Vitals, если правильно настроить кеширование и оптимизацию ресурсов:

// config/app.php - включаем продакшн оптимизации
'debug' => env('APP_DEBUG', false),

// Кеширование конфигурации
php artisan config:cache
php artisan route:cache
php artisan view:cache

// Оптимизация автозагрузчика
composer install --optimize-autoloader --no-dev

Подробнее о Laravel для бизнес-проектов писал в отдельной статье.

Мониторинг и постоянное улучшение

Оптимизация Core Web Vitals — это не разовая задача. Сайт постоянно развивается: добавляется новый функционал, меняется контент, обновляются плагины. Каждое изменение может повлиять на показатели.

У меня есть клиент — интернет-магазин электроники. После первой оптимизации получили отличные показатели: LCP 2.1 сек, FID 45 мс, CLS 0.08. Через полгода владелец пожаловался, что сайт снова медленный. Проверил — LCP 4.8 сек, CLS 0.23. Оказалось, добавили новый виджет чата, который загружался синхронно и весил 800 КБ.

Автоматический мониторинг

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

1. Lighthouse CI — интегрирую в CI/CD пайплайн, чтобы проверять каждый деплой:

# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run Lighthouse CI
        run: |
          npm install -g @lhci/cli@0.12.x
          lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}

2. Synthetic monitoring — настраиваю регулярные проверки через WebPageTest API:

// Еженедельная проверка Core Web Vitals
async function checkCoreWebVitals(url) {
    const testId = await fetch('https://www.webpagetest.org/runtest.php', {
        method: 'POST',
        headers: {
            'X-WPT-API-KEY': process.env.WPT_API_KEY,
        },
        body: new URLSearchParams({
            url: url,
            runs: 3,
            location: 'Dulles:Chrome',
            fvonly: 1,
            f: 'json'
        })
    });
    
    const result = await testId.json();
    
    // Проверяем результат через 5 минут
    setTimeout(async () => {
        const testResult = await fetch(`https://www.webpagetest.org/jsonResult.php?test=${result.data.testId}`);
        const data = await testResult.json();
        
        const lcp = data.data.median.firstView.chromeUserTiming['LargestContentfulPaint'];
        const cls = data.data.median.firstView.chromeUserTiming['CumulativeLayoutShift'];
        
        // Отправляем алерт если показатели плохие
        if (lcp > 2500 || cls > 0.1) {
            sendAlert(`Core Web Vitals degraded: LCP ${lcp}ms, CLS ${cls}`);
        }
    }, 300000);
}

3. Real User Monitoring — собираю данные от реальных пользователей и анализирую тренды:

// Простая система сбора RUM данных
<?php
// rum-endpoint.php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Allow-Headers: Content-Type');

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $data = json_decode(file_get_contents('php://input'), true);
    
    // Валидируем данные
    $metric = [
        'name' => $data['name'],
        'value' => (float) $data['value'],
        'url' => $data['url'],
        'timestamp' => time(),
        'user_agent' => $_SERVER['HTTP_USER_AGENT']
    ];
    
    // Сохраняем в базу
    $pdo = new PDO('mysql:host=localhost;dbname=metrics', $username, $password);
    $stmt = $pdo->prepare('INSERT INTO core_web_vitals (name, value, url, timestamp, user_agent) VALUES (?, ?, ?, ?, ?)');
    $stmt->execute([$metric['name'], $metric['value'], $metric['url'], $metric['timestamp'], $metric['user_agent']]);
    
    echo json_encode(['status' => 'ok']);
}
?>

Регулярные аудиты

Раз в квартал провожу полный аудит Core Web Vitals для каждого проекта. Проверяю:

Часто нахожу "низко висящие плоды" — простые улучшения, которые дают заметный эффект. Например, сжатие одного тяжёлого изображения может улучшить LCP на 30-40%.

💡
Документируйте изменения: Ведите журнал оптимизаций с датами и результатами. Помогает понять, какие методы работают лучше для конкретного проекта.

Core Web Vitals — это марафон, а не спринт. Но результаты стоят вложенных усилий. Хорошие показатели улучшают не только позиции в поиске, но и конверсию, время на сайте, лояльность пользователей. А главное — делают интернет чуточку лучше для всех.

Если нужна помощь с оптимизацией Core Web Vitals для вашего проекта — обращайтесь. Мы проведём аудит и поможем улучшить все ключевые показатели. Подробности на странице поддержки Битрикс или поддержки WordPress.

Хотите улучшить показатели Core Web Vitals вашего сайта?

Получите профессиональный аудит производительности и план оптимизации от наших экспертов.

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

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

Настройка robots.txt: полное руководство Защита форм от спама без CAPTCHA Почему сайт не индексируется в Яндекс и Google