Скорость сайта

Preload, preconnect и prefetch: подсказки браузеру без плагинов

Чем отличаются подсказки загрузки ресурсов, что стоит предзагружать на сайте услуг и как ими легко сделать хуже.

12 минут чтения 3 Дмитрий Акиндинов
Содержание
  1. Три подсказки и что делает каждая
  2. preconnect: сколько внешних доменов держать открытыми
  3. preload для LCP-картинки и критического шрифта
  4. prefetch: следующая страница
  5. Как этим легко сделать хуже
  6. Куда вписывать без плагина
  7. Замеры до и после
  8. Чек-лист перед тем, как закрыть задачу
  9. Что делать дальше
  10. Частые вопросы

Три подсказки решают три разные задачи: preconnect заранее открывает соединение с чужим доменом, preload скачивает конкретный нужный файл, prefetch тянет то, что понадобится на следующей странице. Десяток предзагрузок из чужой статьи делает страницу медленнее, а не быстрее.

Preload ресурсов, preconnect и prefetch выглядят как бесплатная секунда скорости: три строчки в шапке, и LCP падает. Иногда так и есть. Чаще я вижу обратное: сайт, где в <head> висит десяток предзагрузок, скачанных из чужой статьи, и страница от них стала медленнее. Разберём, что делает каждая подсказка, где она реально даёт выигрыш на сайте услуг, и как этими же тегами убить производительность вместо того, чтобы её поднять.

Окно браузера со стрелками приоритета к шрифту и картинке

Плагинов тут не нужно ни одного. Всё, о чём пойдёт речь, делается парой строк в шаблоне и одним фильтром, если у вас WordPress. Оптимизаторы вроде WP Rocket умеют то же самое, но за них надо платить и потом гадать, что именно они добавили в разметку.

Три подсказки и что делает каждая

Браузер сам решает, в каком порядке качать ресурсы. Он строит очередь по мере разбора HTML: сначала CSS в шапке, потом скрипты, потом картинки, которые встретились в разметке. Проблема в том, что часть ресурсов браузер обнаруживает поздно. Шрифт прописан внутри CSS-файла, значит, до него дойдёт очередь только после того, как CSS скачался и распарсился. Картинка вставлена через background-image в стилях, та же история. Секунда ожидания на ровном месте.

Подсказки (resource hints) позволяют сказать браузеру заранее: вот это понадобится, начинай сейчас.

ПодсказкаЧто делаетКогда полезноЦена ошибки
dns-prefetchрезолвит DNS внешнего доменамного внешних доменов, старые браузерыпочти нулевая
preconnectDNS плюс TCP плюс TLS-рукопожатие1-3 критичных внешних доменалишние сокеты, батарея на мобильном
preloadкачает конкретный файл с высоким приоритетомLCP-картинка, критический шрифтотбирает канал у нужных файлов
prefetchкачает файл впрок с низшим приоритетомследующая страница по сценариютрафик пользователя впустую
modulepreloadто же, что preload, для ES-модулейSPA и сборки на модуляхкак у preload

Разница между preload и prefetch принципиальная, и её путают чаще всего. Preload это «нужно на ЭТОЙ странице, качай немедленно». Prefetch это «может понадобиться на СЛЕДУЮЩЕЙ, качай, когда браузеру нечем заняться». Если поставить prefetch на LCP-картинку текущей страницы, эффекта не будет вообще: браузер отложит загрузку до простоя, а простой наступит уже после отрисовки.

Есть ещё prerender, который сейчас живёт в виде Speculation Rules API. Он не просто качает следующую страницу, а рендерит её в фоне. Штука рабочая, но прожорливая, и в Яндекс Браузере поддержка догоняет Chromium с задержкой. На типовом сайте услуг я его не ставлю.

preconnect: сколько внешних доменов держать открытыми

Каждое подключение к новому домену это три раунда: DNS, TCP, TLS. На мобильном интернете в Ленобласти это спокойно 300-600 мс до того, как уйдёт первый байт запроса. Preconnect делает эти три раунда заранее, пока браузер занят разбором HTML.

Выглядит так:

<link rel="preconnect" href="https://mc.yandex.ru">
<link rel="preconnect" href="https://cdn.example.ru" crossorigin>
<link rel="dns-prefetch" href="https://mc.yandex.ru">

Два момента, на которых спотыкаются почти все.

Первый: атрибут crossorigin. Если по этому домену будут качаться шрифты или любые запросы в режиме CORS, crossorigin обязателен. Без него браузер откроет одно соединение, а потом для шрифта откроет второе, и вся экономия уйдёт в никуда. В DevTools это видно как два подключения к одному хосту.

Второй: сколько таких строк ставить. Ответ: не больше двух-трёх. Каждое соединение это память, дескриптор и радиомодуль на телефоне, который не даёт устройству уснуть. Chrome сам закрывает неиспользованное preconnect-соединение через 10 секунд, то есть если ресурс за это время не понадобился, вы просто потратили батарею пользователя.

Как выбрать эти два-три домена: откройте DevTools, вкладка Network, отсортируйте по времени начала запроса и найдите домены, к которым идёт обращение в первые 2 секунды. Обычно это домен счётчика, домен CDN со шрифтами и картинками, иногда домен виджета обратного звонка.

Про счётчик отдельно. Preconnect к mc.yandex.ru разумен, потому что скрипт Метрики грузится почти всегда и тормозит он в основном на установке соединения, а не на размере. Но если Метрика у вас подключена криво и висит в критическом пути, preconnect замаскирует симптом, а не вылечит болезнь. Про это отдельно разбирал в статье о том, как Метрика замедляет сайт.

Чего делать не надо: ставить preconnect ко всем доменам подряд «на всякий случай». Видел сайт студии, где в шапке было 11 preconnect, включая домены, с которых грузились шрифты, вырезанные из проекта год назад. Браузер честно открывал 11 соединений и ждал.

preload для LCP-картинки и критического шрифта

Это единственное место, где preload даёт измеримые секунды, а не украшает отчёт.

Шрифт

Типичная цепочка на WordPress: HTML грузит style.css, внутри style.css лежит @font-face с ссылкой на woff2, шрифт начинает качаться только после разбора CSS. Итого: подключение к серверу, скачивание CSS, парсинг, только потом запрос шрифта. На среднем 4G это 700-900 мс до старта загрузки шрифта.

Preload сокращает цепочку до одного шага:

<link rel="preload" href="/wp-content/themes/saitfix/assets/fonts/inter-400.woff2"
      as="font" type="font/woff2" crossorigin>

Атрибут crossorigin для шрифтов обязателен даже на своём домене, потому что шрифты всегда качаются в CORS-режиме. Забыли его: файл скачается дважды, в консоли появится предупреждение «was preloaded using link preload but not used within a few seconds». Это самая частая ошибка, которую я вижу в чужих шаблонах.

Сколько шрифтов предзагружать: один, максимум два. Основное начертание текста и, если у вас другой шрифт в заголовках, начертание заголовка. Курсивы, полужирные, вариативные оси предзагружать не нужно, они понадобятся ниже по странице. Заодно проверьте, не грузите ли вы 17 файлов там, где хватает двух: подробный разбор в статье про то, почему шрифты тормозят сайт.

И ещё: preload не отменяет font-display: swap. Одно ускоряет загрузку, другое разрешает показать текст запасным шрифтом, пока основной едет. Нужны оба.

LCP-картинка

Largest Contentful Paint на сайте услуг это почти всегда картинка в первом экране: фотография мастера, фон героя, обложка статьи. Если она вставлена обычным тегом <img> в начале HTML, браузер найдёт её сам и рано, preload не нужен. Если она приходит через CSS background-image, через слайдер на JS или через ленивую загрузку, браузер узнает о ней поздно, и вот тут preload работает.

<link rel="preload" as="image"
      href="/wp-content/uploads/2026/09/hero-1200.webp"
      imagesrcset="/wp-content/uploads/2026/09/hero-600.webp 600w,
                   /wp-content/uploads/2026/09/hero-1200.webp 1200w"
      imagesizes="(max-width: 768px) 100vw, 1200px"
      fetchpriority="high">

imagesrcset и imagesizes нужны, чтобы браузер предзагрузил тот же вариант, который потом реально покажет. Без них легко выйдет так: preload тянет десктопный файл на 1200 пикселей, а потом вёрстка запрашивает мобильный на 600, и телефон качает обе картинки.

Часто вместо preload достаточно одного атрибута на самом теге:

<img src="/uploads/hero-1200.webp" fetchpriority="high" alt="Мастер за работой">

fetchpriority="high" поднимает приоритет картинки, которую браузер и так нашёл, без лишней строки в шапке. Правило простое: если картинка есть в HTML обычным тегом, хватит fetchpriority. Если она спрятана в CSS или JS, нужен preload. И убедитесь, что на LCP-картинке нет loading="lazy": этот атрибут и preload воюют друг с другом, побеждает обычно lazy. Если LCP не укладывается в норматив и после этих правок, копайте дальше по разбору что делать, когда LCP больше 2,5 секунды.

prefetch: следующая страница

Prefetch качает ресурс с минимальным приоритетом, когда браузер свободен, и кладёт в кэш. Пользователь кликает, страница открывается мгновенно. Звучит красиво, но осмысленно это только там, где следующий шаг предсказуем.

На сайте услуг предсказуемых сценариев немного:

  • с главной идут на страницу конкретной услуги (обычно на одну-две самые популярные),
  • со страницы услуги идут на контакты или на прайс,
  • из статьи блога идут на соседнюю статью того же кластера.

В этих местах prefetch оправдан. На странице контактов, откуда пользователь уходит писать в мессенджер, он бесполезен.

<link rel="prefetch" href="/uslugi/tehpodderzhka-sayta/" as="document">

Более честный вариант: качать не заранее, а на наведение курсора. Пользователь навёл мышь, до клика у вас 150-300 мс, за это время HTML успевает приехать. Реализация в десяток строк, никакого плагина.

document.addEventListener("mouseover", function (e) {
  var a = e.target.closest("a[href^='/']");
  if (!a || a.dataset.prefetched) return;
  a.dataset.prefetched = "1";
  var l = document.createElement("link");
  l.rel = "prefetch";
  l.href = a.href;
  document.head.appendChild(l);
}, { passive: true });

Обязательная оговорка: prefetch тратит трафик пользователя на страницу, которую он может не открыть. Если у вас заметная доля мобильного трафика с лимитными тарифами, добавьте проверку navigator.connection.saveData и не качайте ничего, когда включён режим экономии.

Второй нюанс: prefetch-запрос заходит на сервер и попадает в логи. Метрика по кликам от него не поедет (счётчик срабатывает при реальном просмотре), а вот в сырых логах хостинга и в счётчиках нагрузки появятся лишние обращения. На shared-хостинге с лимитом по CPU это стоит держать в голове.

Как этим легко сделать хуже

Пропускная способность канала конечна. Любой preload не создаёт скорость, а перераспределяет её: то, что вы подняли в приоритете, отбирает канал у остального. Дальше типовые способы выстрелить себе в ногу, все встречались у меня в работе.

Предзагрузка всего подряд

Классика: в шапке 8 preload на все CSS, все шрифты и логотип. Браузер начинает качать всё одновременно, ширина канала делится на восемь, и LCP-картинка приезжает позже, чем без единой подсказки. Правило: не больше 3-4 preload на страницу, и каждый должен быть обоснован замером.

Preload ресурса, который не нужен на этой странице

Шаблон один на весь сайт, в нём preload шрифта, который используется только на странице тарифов. На остальных 40 страницах браузер качает файл впустую и пишет в консоль предупреждение. Лечится условием в шаблоне: предзагружаем только там, где ресурс действительно в первом экране.

Забытый crossorigin

Уже упоминал, но повторю, потому что это половина случаев. Шрифт без crossorigin качается дважды: один раз по preload, второй раз по факту использования. Вы не ускорили загрузку, вы удвоили вес шрифтов.

Preload вместо инлайна

Критический CSS размером 4 КБ логичнее вставить прямо в HTML, а не предзагружать отдельным файлом: инлайн это ноль запросов, preload это всё равно один запрос. Как собрать критический CSS руками, разбирал в статье про критический CSS без плагинов.

Подсказки на несуществующие файлы

После смены темы или переезда пути поменялись, а строки в шапке остались. Браузер получает 404 и тратит на это соединение. Проверяется за минуту:

grep -oP '(?<=href=")[^"]+' page.html | while read u; do
  curl -o /dev/null -s -w "%{http_code} $un" "https://example.ru$u"
done

Куда вписывать без плагина

WordPress

Правильное место это хук wp_head с ранним приоритетом, чтобы подсказки оказались выше подключения стилей. В functions.php темы:

add_action( 'wp_head', function () {
	$theme = get_template_directory_uri();

	echo '<link rel="preconnect" href="https://mc.yandex.ru">' . "n";
	printf(
		'<link rel="preload" href="%s/assets/fonts/inter-400.woff2" as="font" type="font/woff2" crossorigin>' . "n",
		esc_url( $theme )
	);

	if ( is_front_page() ) {
		$hero = get_field( 'hero_image' );
		if ( $hero ) {
			printf(
				'<link rel="preload" as="image" href="%s" fetchpriority="high">' . "n",
				esc_url( $hero['sizes']['large'] )
			);
		}
	}
}, 1 );

Есть и штатный фильтр wp_resource_hints, но он умеет только dns-prefetch, preconnect, prefetch и prerender, а preload через него не отдать. Для preload проще писать в wp_head напрямую.

Отдельно проверьте, не добавляет ли ваш кэш-плагин свои подсказки. LiteSpeed Cache и W3 Total Cache умеют вставлять preconnect и preload автоматически, и если руками добавить то же самое, в шапке окажутся дубли.

Битрикс

В header.php шаблона сайта, до вызова ShowHead(), либо через API:

$asset = BitrixMainPageAsset::getInstance();
$asset->addString(
	'<link rel="preload" href="/local/templates/main/fonts/inter-400.woff2" as="font" type="font/woff2" crossorigin>',
	true,
	BitrixMainPageAssetLocation::BEFORE_CSS
);

AssetLocation::BEFORE_CSS важен: подсказка должна стоять выше подключения стилей, иначе половина смысла теряется.

Tilda и другие конструкторы

В Tilda есть поле для вставки кода в <head> (Настройки сайта, вкладка «Ещё», блок HEAD). Preconnect туда встаёт нормально. С preload сложнее: Tilda сама генерирует имена файлов картинок с хэшами, и после любой правки блока путь меняется. Ставить preload на такой URL смысла нет, он протухнет при первом же редактировании. Ограничьтесь preconnect к static.tildacdn.com и доменам счётчиков.

Замеры до и после

Без замера всё вышеописанное это гадание. Порядок работы у меня такой.

  1. Снять базовую линию: PageSpeed Insights по мобильной вкладке, три прогона, берём медиану. Записать LCP, TTFB и общий балл.
  2. Открыть DevTools, Network, поставить throttling «Slow 4G» и снять водопад. Найти LCP-элемент (в Chrome он подсвечивается на вкладке Performance, секция Timings).
  3. Внести ОДНО изменение. Не пачку.
  4. Сбросить кэш плагина и CDN, прогнать замер заново.
  5. Сравнить. Если LCP не сдвинулся больше чем на 100 мс, изменение не работает, откатываем.

Реальный пример из практики: сайт мастерской на WordPress, тема с двумя шрифтами и героем через background-image. Замеры по мобильной вкладке PageSpeed:

Что сделалиLCPБалл
Исходное состояние4,1 с42
Preload шрифта Inter 4003,7 с48
Preload hero-картинки с imagesrcset2,6 с61
Preconnect к mc.yandex.ru2,5 с63
Убрали 5 лишних preload из чужой инструкции2,3 с68

Последняя строка показательна. Удаление подсказок дало почти столько же, сколько их добавление. Работы на всё ушло около полутора часов, включая замеры.

Чего смотреть не надо: общий балл PageSpeed сам по себе. Он собирается из синтетики на эмулируемом устройстве и скачет на 5-8 пунктов между прогонами. Смотрите на LCP в поле, то есть на данные CrUX внизу отчёта, и на графики скорости в Метрике (Мониторинг, Скорость загрузки страниц). Синтетика годится только для сравнения «до и после» в один и тот же день.

Чек-лист перед тем, как закрыть задачу

  • В <head> не больше 3-4 preload и не больше 2-3 preconnect.
  • У каждого preload шрифта есть crossorigin и type="font/woff2".
  • В консоли браузера нет предупреждений «preloaded but not used».
  • У LCP-картинки нет loading="lazy", есть fetchpriority="high".
  • Preload картинки содержит imagesrcset и imagesizes, если вёрстка адаптивная.
  • Подсказки стоят выше подключения CSS, а не в подвале.
  • Кэш-плагин не дублирует то же самое своими средствами.
  • Все URL в подсказках отдают 200, а не 404.
  • Замер до и после сделан на одном устройстве и в один день.

Что делать дальше

Если LCP у вас больше трёх секунд, начинайте не с подсказок, а с хостинга и картинок: preload не спасёт страницу, где TTFB 900 мс, а hero весит 1,4 МБ в PNG. Подсказки это последние 10-20 процентов, которые снимают уже после того, как убраны основные тормоза.

Если хочется, чтобы кто-то это сделал руками и с замерами, а не по чек-листу из интернета, напишите через форму на главной. Разовая оптимизация первого экрана обычно укладывается в 2-4 часа работы: замер, правки, повторный замер, отчёт с цифрами до и после. Если сайт нужно держать быстрым постоянно, а не разово, посмотрите тарифы на обслуживание: проверка скорости после каждого обновления входит в абонентку, и это дешевле, чем раз в год откапывать, какой плагин съел вторую секунду.

Частые вопросы

Чем отличаются preload, preconnect и prefetch

Preconnect открывает соединение с внешним доменом заранее. Preload скачивает конкретный файл с высоким приоритетом. Prefetch тянет ресурсы для следующей страницы в фоне, с низким приоритетом.

Что имеет смысл предзагружать

Картинку первого экрана, по которой считается LCP, и критический шрифт. Больше почти ничего: каждый лишний preload отбирает канал у того, что действительно нужно сейчас.

Сколько держать preconnect

Два-три домена, к которым обращение идёт в первые секунды. Браузер закрывает неиспользованное соединение примерно через десять секунд, так что предугадывать впрок бесполезно.

Как этим сделать хуже

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

Передайте техническую часть в надёжные руки.

Слежу за хостингом, бэкапами и обновлениями. Когда что-то пойдёт не так – звоните мне, не своему программисту, не в спортзал и не маме.

Короткие вопросы по теме

Все вопросы по теме "Скорость сайта"

Заявка

Обсудим задачу

Напишите имя и телефон, выберите удобный канал. Перезвоню или напишу в течение часа в рабочее время.

Куда удобнее ответить?