Сайт «вроде быстрый»: как перевести это ощущение в цифры, за которые платят
Одна и та же страница получает 90 баллов на десктопе и 40 на телефоне, а «поле» и «лаборатория» показывают разное. Объясняем, что именно меряет каждая цифра, какая из них про ваших посетителей, и в каком порядке чинить.
Разговор про скорость сайта почти всегда начинается одинаково. «У нас всё быстро, я проверял» — говорит разработчик, у которого сайт открыт в соседней вкладке с прогретым кэшем, на проводном интернете и на машине за две зарплаты. «У меня грузится вечность» — говорит клиент, открывший тот же адрес с телефона в метро. Оба правы, и спорить им не о чем: они измеряют разное.
Дальше в разговор приходит цифра. Кто-то открывает публичный измеритель, получает «производительность: 38» и приносит это как приговор. Через неделю тот же измеритель показывает 71, хотя не менялось ничего. Доверие к цифре падает, и обсуждение скорости возвращается к ощущениям.
Проблема не в измерителе. Проблема в том, что за словом «скорость» прячутся минимум шесть разных величин и два принципиально разных способа их получить. Разберём, что означает каждая, какая из них про ваших посетителей, а какая — про один прогон на одной машине.
Три метрики, которые решают
Core Web Vitals — это набор из трёх показателей, каждый про свой этап знакомства посетителя со страницей. Они специально подобраны так, чтобы не дублировать друг друга.
| Метрика | Вопрос, на который отвечает | Хорошо | Плохо |
|---|---|---|---|
| LCP — появление главного содержимого | Когда посетитель увидел то, ради чего пришёл: крупную картинку, заголовок, блок текста | до 2,5 с | больше 4 с |
| INP — отзывчивость на действия | Сколько страница думает, прежде чем отреагировать на нажатие, клик или ввод | до 200 мс | больше 500 мс |
| CLS — стабильность вёрстки | Насколько содержимое прыгает во время загрузки: успел нажать «отмена», попал в «подтвердить» | до 0,1 | больше 0,25 |
К ним обычно прилагаются вспомогательные величины: время до первой отрисовки (когда появилось хоть что-то), общее время блокировки (сколько главный поток был занят и не реагировал), индекс скорости (как быстро заполнялся экран). Они не входят в Core Web Vitals, но полезны при разборе: именно они подсказывают, почему LCP плохой.
Лаборатория против поля: главное различие
Дальше начинается то, из-за чего чаще всего ломаются разговоры о скорости. Одни и те же метрики получают двумя несовместимыми способами, и путать их нельзя.
| Лабораторный замер | Данные реальных посетителей | |
|---|---|---|
| Как получается | Страница загружается прямо сейчас на стандартизованном устройстве и канале | Собирается статистика по посещениям живых людей за прошедший период |
| Что описывает | Один прогон в одинаковых для всех условиях | Распределение по всем устройствам, каналам и географии вашей аудитории |
| Когда обновляется | Немедленно: выкатили исправление — увидели результат | С задержкой: изменения проявятся, когда наберётся статистика |
| Для чего годится | Сравнение «до и после», поиск причины, регрессионный контроль | Оценка реального положения дел и приоритизация: что чинить первым |
| Когда недоступно | Практически никогда: страницу можно загрузить всегда | У малопосещаемых страниц — статистики просто не хватает |
| Есть ли INP | Нет: некому нажимать. Вместо него — общее время блокировки | Да: живые люди действительно нажимают |
Последняя строка — хороший пример того, почему смешивание невозможно в принципе. Отзывчивость на действия нельзя измерить там, где никто не действует. В лаборатории вместо неё смотрят на косвенный признак — сколько времени главный поток был занят. Это разумная замена, но это другая величина, и подставлять одну вместо другой нельзя.
Мобильный и десктопный профили — это два разных ответа
Мобильный замер выполняется на характеристиках среднего телефона: слабее процессор, уже канал, выше задержки. Это не пессимизм ради пессимизма — это медианное устройство реальной аудитории. Отсюда правило, которое экономит много споров: сравнивать мобильную оценку с десктопной бессмысленно.
Разница в 30–40 пунктов между профилями у одной и той же страницы — обычное дело. Тяжёлый JavaScript, который на десктопе разбирается за 200 миллисекунд, на среднем телефоне занимает секунду с лишним, и именно эта секунда решает, останется ли посетитель. Поэтому профили считаются и хранятся раздельно: усреднение здесь скрыло бы ровно ту проблему, ради которой замер и делался.
Код: замер и регрессионный контроль
Один вызов возвращает четыре оценки, лабораторные метрики, полевые данные (если их достаточно) и список узких мест с экономией в миллисекундах по каждому.
# Мобильный профиль (по умолчанию)
curl -H "Authorization: Bearer ak_sandbox_demo_mockdata_v1" \
"https://atlorium.com/api/pagespeed?url=https://example.com"
# Десктопный профиль
curl -H "Authorization: Bearer ak_sandbox_demo_mockdata_v1" \
"https://atlorium.com/api/pagespeed?url=https://example.com&strategy=desktop"
# Только что выкатили оптимизацию — нужен свежий замер, а не сохранённый
curl -H "Authorization: Bearer ak_sandbox_demo_mockdata_v1" \
"https://atlorium.com/api/pagespeed?url=https://example.com&forceRefresh=true"
# Пакет: ключевые страницы сайта одним вызовом (до 20 адресов)
curl -X POST \
-H "Authorization: Bearer ak_sandbox_demo_mockdata_v1" \
-H "Content-Type: application/json" \
-d '["https://example.com","https://example.com/catalog","https://example.com/checkout"]' \
"https://atlorium.com/api/pagespeed/batch?strategy=mobile"
import requests
BASE = "https://atlorium.com"
HEADERS = {"Authorization": "Bearer ak_sandbox_demo_mockdata_v1"}
# Таймаут — не меньше 120 секунд. Это внешний замер страницы, а не выборка
# из справочника: браузер действительно загружает сайт, и 10-30 секунд
# на ответ — норма, а не признак неполадки.
TIMEOUT = 150
def audit(url: str, strategy: str = "mobile") -> dict:
r = requests.get(
f"{BASE}/api/pagespeed",
params={"url": url, "strategy": strategy},
headers=HEADERS,
timeout=TIMEOUT,
)
r.raise_for_status()
return r.json()
def regression_guard(url: str, floor: int = 60) -> None:
"""Ночной контроль: падение ниже порога — повод разбираться.
Сравнивается ИМЕННО мобильный профиль: он ближе к медианному
посетителю и первым показывает деградацию от нового скрипта.
"""
report = audit(url, "mobile")
score = report.get("performanceScore")
if score is None:
print(f"{url}: замер не выполнен — {report.get('message')}")
return
if score < floor:
print(f"{url}: производительность {score} (порог {floor})")
# Что чинить — в порядке убывания выигрыша.
for item in report["opportunities"][:3]:
print(f" - {item['title']}: экономия {item['savingsMs']:.0f} мс")
using System.Net.Http.Json;
// Таймаут клиента поднят намеренно: замер занимает 10-30 секунд.
HttpClient http = new()
{
BaseAddress = new Uri("https://atlorium.com"),
Timeout = TimeSpan.FromSeconds(150),
};
http.DefaultRequestHeaders.Add("Authorization", "Bearer ak_sandbox_demo_mockdata_v1");
SiteReport? report = await http.GetFromJsonAsync<SiteReport>(
$"/api/pagespeed?url={Uri.EscapeDataString(url)}&strategy=mobile");
// Полевого блока может не быть: у малопосещаемой страницы просто нет статистики.
// Это НЕ ошибка — обрабатываем null как «данных о живых посетителях нет».
string field = report?.FieldData is null
? "данных реальных посетителей недостаточно"
: $"оценка по посетителям: {report.FieldData.OverallRating}";
record SiteFieldData(string Scope, string OverallRating);
record SiteOpportunity(string Title, double SavingsMs);
record SiteReport(
string Url,
string FinalUrl,
int? PerformanceScore,
int? SeoScore,
SiteFieldData? FieldData,
SiteOpportunity[] Opportunities);
{
"url": "https://example.com",
"finalUrl": "https://example.com/",
"strategy": "mobile",
"fetchedAtUtc": "2026-09-01T09:31:07Z",
"performanceScore": 54,
"accessibilityScore": 92,
"bestPracticesScore": 79,
"seoScore": 96,
"labMetrics": [
{ "key": "largest-contentful-paint", "title": "Появление главного содержимого",
"displayValue": "4,1 с", "numericValue": 4120, "unit": "ms", "rating": "poor" },
{ "key": "cumulative-layout-shift", "title": "Сдвиг вёрстки",
"displayValue": "0,04", "numericValue": 0.04, "rating": "good" }
],
"fieldData": {
"scope": "page",
"overallRating": "needs_improvement",
"metrics": [
{ "key": "LARGEST_CONTENTFUL_PAINT_MS", "percentile": 3200,
"displayValue": "3,2 с", "rating": "needs_improvement",
"goodPercent": 61.4, "needsImprovementPercent": 27.1, "poorPercent": 11.5 }
]
},
"opportunities": [
{ "key": "unused-javascript", "title": "Неиспользуемый JavaScript", "savingsMs": 1840 },
{ "key": "modern-image-formats", "title": "Изображения в современных форматах", "savingsMs": 920 }
],
"elapsedMs": 18430
}
Тот же вызов на шести языках (Python, TypeScript, Go, Java, C#, PHP) — в репозитории site-performance-api-client. Примеры запускаются сразу: ключ в них уже стоит демонстрационный.
В каком порядке чинить
Оценка сама по себе — это диагноз без назначений. Практическую ценность даёт список узких мест: он отсортирован по выигрышу, и в нём указано, сколько миллисекунд сэкономит каждое исправление. Это превращает «у нас 54 балла» в план работ.
Начинайте с самого крупного пункта, а не с самого простого. Соблазн сделать сначала лёгкое понятен, но экономия в 40 миллисекунд на фоне полутора секунд неиспользуемого скрипта не изменит ничего — а ощущение работы создаст.
Смотрите, какая метрика плохая, а не только общую оценку. Плохой LCP чинится одними средствами (картинка на первом экране, шрифты, ответ сервера), плохой INP — другими (тяжёлые обработчики, лишние скрипты), плохой CLS — третьими (размеры под изображения и рекламные блоки, зарезервированные заранее). Оптимизация «вообще» распыляет усилия.
Меряйте до и после в одном профиле. Сравнение мобильного замера с десктопным даст «улучшение», которого не было. И не делайте выводов по одному прогону: общая оценка плавает от запуска к запуску даже на неизменной странице.
Проверяйте не только главную. Люди приходят из поиска на карточку товара и статью, а платят на странице оформления заказа. Ключевые страницы стоит держать в ночном прогоне пакетом — заодно поймаете деградацию, приехавшую с чужим скриптом аналитики.
Границы: чего сервис не делает
Не измеряет закрытые страницы. Личный кабинет за формой входа, внутренний портал, страница за корпоративным периметром — замерить их снаружи нельзя. Ответом будет «страница недоступна», и списания за него не будет.
Не заменяет мониторинг доступности. Одиночный замер говорит о скорости в момент замера, а не о том, что сайт лежал ночью двадцать минут. Это разные задачи и разные инструменты.
Не гарантирует позицию в поиске. Скорость — один из факторов ранжирования, и далеко не главный. Страница с отличными метриками и плохим содержимым выше не поднимется. Обратное, впрочем, тоже верно: медленная страница теряет посетителей ещё до того, как они увидят содержимое, — и это заметно по отказам, а не по позициям.
Не показывает историю. Возвращается текущее состояние: свежий лабораторный замер и актуальный срез полевых данных. Динамику за месяцы стройте на своей стороне, сохраняя результаты ночных прогонов, — так вы к тому же увидите ровно те даты, которые вам важны.
Частые вопросы
Частые вопросы
Что такое Core Web Vitals простыми словами?
Три показателя о разном. LCP — через сколько посетитель увидел главное содержимое (хорошо — до 2,5 секунды). INP — насколько быстро страница отвечает на нажатия и клики (хорошо — до 200 миллисекунд). CLS — насколько сильно прыгает вёрстка во время загрузки (хорошо — до 0,1). Одной цифрой они не заменяются: страница может мгновенно показать картинку и при этом десять секунд не реагировать на нажатия.
Почему оценка каждый раз разная, хотя сайт не менялся?
Общая оценка производительности — производная от нескольких метрик, а сами метрики зависят от условий конкретного прогона: загруженности сети, состояния вашего сервера, сторонних скриптов. Разброс в несколько пунктов — норма. Поэтому выводы делают по метрикам и по тенденции, а не по одному значению общей оценки, и сравнивают замеры в одном и том же профиле устройства.
В поле метрики хорошие, а в лаборатории плохие. Кому верить?
Обоим — это разные утверждения. Лабораторный замер делается прямо сейчас на стандартизованном «среднем телефоне» и всегда описывает первый визит: он воспроизводим и годится для сравнения «до и после». Полевые данные — статистика по вашим живым посетителям за прошедший период; они часто лучше, потому что аудитория приходит с устройств получше и нередко с прогретым кэшем. Тревожна обратная картина: в лаборатории хорошо, а в поле плохо.
Почему в ответе нет данных о реальных посетителях?
У страницы не хватает трафика, чтобы статистика была представительной. Это нормальный ответ, а не сбой: полевой блок появляется только там, где посещений достаточно. Лабораторный замер при этом выполняется всегда, и для работы «до и после» его хватает.
Чем мобильный замер отличается от десктопного и какой смотреть?
Мобильный выполняется на характеристиках среднего телефона: слабее процессор, уже канал, выше задержки. Разница в 30–40 пунктов у одной и той же страницы — обычное дело, поэтому профили считаются и хранятся раздельно, а сравнивать их между собой бессмысленно. Смотреть стоит тот, с которого приходит ваша аудитория; в большинстве случаев это мобильный.
Сколько ждать ответа и почему так долго?
От 10 до 30 секунд. Это реальная загрузка вашей страницы браузером в стандартизованном окружении, а не выборка из справочника. Практический вывод: клиентский таймаут ставьте не меньше 120 секунд и не вызывайте замер синхронно внутри обработки пользовательского запроса — его место в фоновой задаче или ночном расписании.
Можно ли замерить страницу за формой входа?
Нет. Замер выполняется снаружи, как обычным посетителем, поэтому личный кабинет, внутренний портал и страницы за корпоративным периметром недоступны. Ответом будет «страница недоступна», и списания за него не произойдёт.
Поднимет ли оптимизация скорости сайт в поиске?
Скорость — один из факторов ранжирования и далеко не решающий: страница с отличными метриками и слабым содержимым выше не поднимется. Зато медленная страница теряет посетителей до того, как они увидят содержимое, и это видно по отказам. Оптимизацию стоит делать ради поведения людей, а улучшение позиций считать приятным следствием.
Сервисы из этой статьи
Скорость загрузки и Core Web Vitals: лабораторный прогон, данные реальных посетителей и рекомендации по убыванию выигрыша
Попробовать прямо сейчас — без регистрации
Демо-ключ ak_sandbox_demo_mockdata_v1 — публичный и общий для всех.
С ним API отвечает моками: данные правдоподобные, но сгенерированные,
и они не меняются от запроса к запросу — на них удобно писать тесты.
Настоящие данные приходят с личным ключом.
curl -H "Authorization: Bearer ak_sandbox_demo_mockdata_v1" \
https://atlorium.com/openapi/speed_ru.json