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

Плагинов тут не нужно ни одного. Всё, о чём пойдёт речь, делается парой строк в шаблоне и одним фильтром, если у вас WordPress. Оптимизаторы вроде WP Rocket умеют то же самое, но за них надо платить и потом гадать, что именно они добавили в разметку.
Три подсказки и что делает каждая
Браузер сам решает, в каком порядке качать ресурсы. Он строит очередь по мере разбора HTML: сначала CSS в шапке, потом скрипты, потом картинки, которые встретились в разметке. Проблема в том, что часть ресурсов браузер обнаруживает поздно. Шрифт прописан внутри CSS-файла, значит, до него дойдёт очередь только после того, как CSS скачался и распарсился. Картинка вставлена через background-image в стилях, та же история. Секунда ожидания на ровном месте.
Подсказки (resource hints) позволяют сказать браузеру заранее: вот это понадобится, начинай сейчас.
| Подсказка | Что делает | Когда полезно | Цена ошибки |
|---|---|---|---|
dns-prefetch | резолвит DNS внешнего домена | много внешних доменов, старые браузеры | почти нулевая |
preconnect | DNS плюс 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 и доменам счётчиков.
Замеры до и после
Без замера всё вышеописанное это гадание. Порядок работы у меня такой.
- Снять базовую линию: PageSpeed Insights по мобильной вкладке, три прогона, берём медиану. Записать LCP, TTFB и общий балл.
- Открыть DevTools, Network, поставить throttling «Slow 4G» и снять водопад. Найти LCP-элемент (в Chrome он подсвечивается на вкладке Performance, секция Timings).
- Внести ОДНО изменение. Не пачку.
- Сбросить кэш плагина и CDN, прогнать замер заново.
- Сравнить. Если LCP не сдвинулся больше чем на 100 мс, изменение не работает, откатываем.
Реальный пример из практики: сайт мастерской на WordPress, тема с двумя шрифтами и героем через background-image. Замеры по мобильной вкладке PageSpeed:
| Что сделали | LCP | Балл |
|---|---|---|
| Исходное состояние | 4,1 с | 42 |
| Preload шрифта Inter 400 | 3,7 с | 48 |
| Preload hero-картинки с imagesrcset | 2,6 с | 61 |
| Preconnect к mc.yandex.ru | 2,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
Два-три домена, к которым обращение идёт в первые секунды. Браузер закрывает неиспользованное соединение примерно через десять секунд, так что предугадывать впрок бесполезно.
Как этим сделать хуже
Навесить предзагрузку на всё подряд. Приоритеты выстраиваются в очередь, и главная картинка начинает грузиться позже, чем грузилась без всяких подсказок.