Шрифты и скорость сайта: скрытый тормоз

Когда сайт медленный, первым делом сжимают картинки, выносят скрипты и подключают кэш. Шрифты при этом остаются нетронутыми — они весят «всего» пару сотен килобайт и кажутся мелочью. Между тем именно веб-шрифты чаще всего дают самую заметную часть Cumulative Layout Shift, откладывают отрисовку основного текста и удлиняют Largest Contentful Paint на сотни миллисекунд, потому что подключаются в самом неудачном месте цепочки загрузки — после того, как браузер уже разобрал CSS и построил дерево. Причём на русскоязычных сайтах проблема острее: кириллический набор глифов тяжелее латинского, а типовые способы подключения через сторонние CDN добавляют лишние соединения и риск недоступности. Разбираем механику от первого байта до стабильной вёрстки и показываем, что именно чинить в рамках SEO-продвижения сайта.
Коротко
- Шрифт — ресурс с отложенным обнаружением: браузер узнаёт о нём только после построения CSSOM и сопоставления правил с текстом, поэтому загрузка стартует поздно.
- Без font-display Chrome прячет текст до трёх секунд (FOIT). Если текстовый блок является LCP-элементом, LCP сдвигается ровно на это время.
- Свап шрифта без выравнивания метрик — прямая причина CLS: строки меняют ширину и высоту, блок ниже уезжает. Лечится size-adjust, ascent-override и подбором фолбэка.
- Кириллический сабсет — обязательная операция: полный многоязычный файл легко весит в разы больше нужного, а лишние диапазоны Unicode на сайте никогда не отрисуются.
- Локальное размещение почти всегда быстрее стороннего CDN: с 2020 года общий кэш шрифтов между сайтами не работает, а лишний домен стоит DNS + TCP + TLS.
- Формат один — WOFF2. Всё остальное (TTF, EOT, SVG, WOFF) на живом проекте 2026 года — балласт в разметке @font-face.
- Иконочные шрифты — отдельный источник и веса, и сдвигов; в большинстве случаев их место занимает SVG-спрайт.
Почему шрифты попадают в слепую зону оптимизации
Проверка скорости обычно выглядит так: прогнали страницу через PageSpeed Insights, посмотрели список рекомендаций, отдали разработчику. В списке шрифты либо не всплывают вовсе, либо появляются одной строкой «Ensure text remains visible during webfont load». Строка выглядит косметической, её откладывают. Результат: сайт с идеально пережатыми изображениями и отложенным JavaScript всё равно показывает пустые блоки текста в первые секунды и получает сдвиг макета при подмене шрифта.
Причина недооценки — в неправильной ментальной модели. Шрифт воспринимается как «ещё одна картинка», то есть как ресурс, который просто занимает килобайты. На деле у него три особенности, которых нет ни у изображений, ни у скриптов.
Первая — позднее обнаружение. Браузер находит шрифт не в HTML, а внутри CSS: сначала нужно загрузить и распарсить таблицу стилей, построить CSSOM, сопоставить селекторы с реальными узлами и только потом понять, что для этого текста требуется файл шрифта. Между началом загрузки страницы и стартом запроса за шрифтом проходит время, которого нет у ресурсов, объявленных прямо в разметке.
Вторая — блокировка отрисовки текста. Пока файл не пришёл, браузер по умолчанию не рисует текст вообще. Не рисует запасным шрифтом, а именно прячет: это поведение называется FOIT, Flash of Invisible Text. Пользователь видит картинки, фон, кнопки без подписей — и пустоту там, где должен быть заголовок.
Третья — геометрия. Когда шрифт наконец приходит, текст перерисовывается уже другой гарнитурой, с другой шириной символов и другой высотой строки. Абзац, занимавший четыре строки, становится пятистрочным. Всё, что ниже, съезжает вниз. Это и есть layout shift — и он считается системой Core Web Vitals наравне с любым другим сдвигом.
Изображение без размеров сдвигает макет один раз и заметно. Шрифт сдвигает его тихо, на каждом текстовом блоке страницы сразу — и потому суммарно часто даёт больший вклад в CLS.
Как браузер на самом деле загружает шрифт
Чтобы понимать, где именно вставлять правки, нужно держать в голове полную цепочку. Она одинакова во всех современных браузерах, различия только в длительности таймаутов.
HTML приходит и парсится
Браузер встречает <link rel="stylesheet"> и останавливает отрисовку: CSS блокирует рендеринг. Если таблица стилей лежит на стороннем домене, сюда же добавляется DNS-резолв, TCP-хендшейк и TLS-договорённость с этим доменом.
CSS загружен, строится CSSOM
В нём находятся правила @font-face. Важно: сам факт наличия @font-face ещё не запускает загрузку. Файл скачивается только тогда, когда браузер убедился, что этот шрифт реально применяется к существующему на странице тексту.
Сопоставление unicode-range
Если у @font-face указан unicode-range, браузер проверяет, есть ли на странице символы из этого диапазона. Латинский сабсет на чисто русской странице не будет загружен вообще — это бесплатная экономия, которую даёт правильная нарезка.
Запрос на файл и период блокировки
Начинается загрузка. Одновременно стартует block period — окно, в течение которого текст, ждущий этот шрифт, отрисовывается невидимым. В Chrome и Firefox по умолчанию это до трёх секунд.
Файл пришёл или таймаут истёк
Пришёл вовремя — текст рисуется сразу нужной гарнитурой, сдвига нет. Не успел — браузер рисует запасным шрифтом (swap period), а когда файл всё-таки придёт, перерисовывает текст заново. Вторая перерисовка и есть источник CLS.
Перерасчёт макета
Смена гарнитуры меняет метрики: ширину глифов, высоту прописных и строчных, межстрочный интервал. Браузер пересчитывает поток документа. Каждый блок, чья высота изменилась, толкает соседей — и каждый такой толчок попадает в счётчик CLS.
Из этой цепочки следуют все практические выводы. Ускорить пункт 1 — значит убрать сторонние домены и уменьшить критический CSS. Ускорить пункт 4 — значит объявить шрифт заранее через preload, чтобы запрос ушёл параллельно с CSS, а не после него. Обезвредить пункт 6 — значит подогнать метрики фолбэка так, чтобы подмена прошла без изменения геометрии. Ниже — по каждому пункту отдельно. Общий контекст по метрикам скорости мы разбирали в материале про Core Web Vitals: LCP, CLS и INP, здесь же речь идёт исключительно о шрифтовой части.
font-display: одно свойство, пять сценариев
Дескриптор font-display внутри @font-face управляет тем, что происходит с текстом, пока шрифт едет. Это первое, что нужно проверить в любом аудите: его отсутствие — самая частая шрифтовая ошибка вообще.
| Значение | Период блокировки | Период подмены | Что видит пользователь | Когда применять |
|---|---|---|---|---|
| auto (по умолчанию) | Решает браузер, на практике до 3 с | Бесконечный | Невидимый текст, затем подмена | Никогда не оставлять осознанно — это отсутствие решения |
| block | До 3 с | Бесконечный | Долгий FOIT, затем нужный шрифт | Только логотипы и крупные декоративные надписи, где важна гарнитура |
| swap | ~100 мс (крайне короткий) | Бесконечный | Сразу запасной шрифт, потом подмена | Основной текст, заголовки — базовый безопасный выбор |
| fallback | ~100 мс | ~3 с | Запасной шрифт; если файл опоздал — остаётся навсегда | Компромисс для body-текста на медленных сетях |
| optional | ~100 мс | Отсутствует | Либо сразу нужный шрифт из кэша, либо запасной без подмены | Когда CLS критичен и брендовая гарнитура не обязательна |
Практическая логика выбора выглядит так. swap устраняет FOIT и спасает LCP, но не устраняет CLS — сдвиг просто переносится с «текста нет» на «текст перерисовался». optional — единственное значение, которое гарантированно убирает шрифтовой CLS: браузер либо успевает взять файл из кэша за отведённые ~100 мс, либо навсегда остаётся на фолбэке для этой загрузки страницы, а скачанный файл кладёт в кэш для следующих визитов. Цена — первый визит пользователь видит системный шрифт.
ГЛАВНОЕ ПРАВИЛО ВЫБОРА
Если бренд-гайд допускает системный шрифт на первом экране первого визита — ставьте font-display: optional и получайте нулевой шрифтовой CLS без дополнительной возни с метриками. Если гарнитура обязательна (а на большинстве коммерческих сайтов это так) — ставьте swap и обязательно выравнивайте метрики фолбэка, иначе вы просто обменяли одну проблему на другую.
CLS: как подмена шрифта ломает вёрстку и как это чинить
Сдвиг при свапе возникает потому, что запасной шрифт и целевой имеют разные метрики. Ключевых параметров три: средняя ширина глифа (влияет на количество строк в абзаце), высота строчных букв x-height (влияет на визуальную плотность) и вертикальные метрики ascent/descent (влияют на высоту строки). Arial и, скажем, узкая гротескная гарнитура при одинаковом font-size дают разное число строк на один и тот же текст — и разницу видно как рывок макета.
Подгонка метрик через дескрипторы @font-face
Современный CSS позволяет объявить «локальный фолбэк с исправленными метриками» и использовать его в стеке шрифтов. Механика такая: создаётся отдельный @font-face, в котором src ссылается на системный шрифт через local(), а четыре дескриптора корректируют его геометрию под целевую гарнитуру.
- size-adjust — масштабирует глифы в процентах. Основной рычаг: подбирается так, чтобы средняя ширина строки фолбэка совпала с целевой.
- ascent-override — задаёт высоту над базовой линией. Отвечает за верхнюю границу строки.
- descent-override — задаёт глубину подстрочных элементов.
- line-gap-override — межстрочный зазор, обычно выставляется в 0%.
Значения не угадываются, а вычисляются из таблиц метрик обоих шрифтов. Есть готовые генераторы, но принцип полезно понимать: соотношение unitsPerEm и hhea-метрик целевого шрифта переносится на фолбэк. После правильной подгонки переключение с системного шрифта на брендовый визуально почти незаметно, а CLS от него становится нулевым при сохранении font-display: swap. Это самый сильный приём в теме и одновременно самый редко применяемый на практике.
Что ещё даёт сдвиг помимо самого свапа
- Динамический подбор размера JS-скриптом — библиотеки «умного» уменьшения заголовков под ширину контейнера срабатывают после загрузки шрифта и добавляют ещё один сдвиг поверх первого.
- Кнопки и бейджи с текстом внутри — их ширина зависит от ширины строки, а значит, при свапе меняется вся сетка вокруг.
- Иконочные шрифты в шапке — иконка до загрузки занимает место одного символа-заглушки, после загрузки становится квадратной, элементы навигации разъезжаются.
- Отсутствие min-height у первого экрана — если высота hero-блока определяется длиной заголовка, свап двигает вообще всё содержимое страницы.
Порог, к которому надо идти: CLS не выше 0,1 у 75-го перцентиля реальных пользователей. Значение 0,1–0,25 считается «требует улучшения», выше 0,25 — «плохо». Шрифтовой вклад считается просто: снимите два прогона в лаборатории — с включённым веб-шрифтом и с принудительным фолбэком — и сравните дельту. Обычно она объясняет от четверти до половины всего CLS на контентных страницах.
Кириллица: почему у русских сайтов файлы тяжелее
Латинский базовый набор — это примерно сотня глифов. Кириллический добавляет ещё около семидесяти, и это только базовый диапазон U+0400–U+04FF. Проблема в другом: большинство популярных гарнитур поставляются одним файлом, в который упакованы латиница, кириллица, греческий, вьетнамский, расширенная латиница, символы валют, математические знаки, стрелки и лигатуры. Русскоязычный сайт использует из этого богатства процентов двадцать, а платит за все сто.
Что такое сабсеттинг и как его делать
Сабсеттинг — это пересборка файла шрифта с сохранением только нужных диапазонов Unicode. Стандартный инструмент — pyftsubset из библиотеки fontTools; есть надстройки вроде glyphhanger, которые сами сканируют сайт и составляют список реально используемых символов.
Минимальный практический набор для русского коммерческого сайта:
| Диапазон Unicode | Что входит | Нужен русскому сайту? | Комментарий |
|---|---|---|---|
| U+0000–00FF | Базовая латиница, знаки препинания, ¤ € § © | Да | Латиница неизбежна: бренды, модели, единицы, e-mail |
| U+0400–045F | Основная кириллица | Да | Ядро набора, без него сайт не отрисуется |
| U+0490–0491 | Украинская Ґ ґ | По ситуации | Оставляйте, если есть украиноязычные вкрапления |
| U+2116 | Знак номера № | Да | Постоянно встречается в реквизитах и каталогах |
| U+2010–2027 | Тире, кавычки-ёлочки, многоточие | Да | Без них типографика ломается на фолбэк |
| U+0370–03FF | Греческий алфавит | Нет | Исключение — научные и медицинские тексты |
| U+0100–024F | Расширенная латиница | Обычно нет | Нужна только при европейских названиях брендов |
| U+1E00–1EFF | Вьетнамский | Нет | Классический балласт в дефолтных сборках |
Второй приём — не один сабсет, а несколько @font-face с разными unicode-range: отдельно кириллица, отдельно латиница, отдельно пунктуация. Браузер сам скачает только те файлы, символы из которых реально встретились на странице. Для многоязычных проектов это обязательная схема; принципы работы с несколькими языковыми версиями мы разбирали в статье про hreflang и мультиязычные сайты.
ЧАСТАЯ ЛОВУШКА САБСЕТТИНГА
Обрезав шрифт до кириллицы, легко забыть про символы, которые появляются на сайте динамически: значки валют других стран, математические знаки в характеристиках товара, буквы Ё и ё в редких текстах, цифры в верхнем регистре, эмодзи в отзывах. Если глифа в сабсете нет, браузер подставит его из другого шрифта — и вы получите разнобой в одной строке. Перед выкаткой обязательно прогоните полный дамп текстов сайта через сборщик символов, а не составляйте список вручную.
Форматы файлов: почему всё, кроме WOFF2, — лишнее
Классический «пуленепробиваемый @font-face» из руководств десятилетней давности перечисляет пять форматов: EOT для старого IE, TTF/OTF, WOFF, WOFF2 и SVG для древних мобильных Safari. Такая конструкция до сих пор кочует по шаблонам и генераторам.
| Формат | Сжатие | Относительный вес | Поддержка браузерами | Вердикт на 2026 год |
|---|---|---|---|---|
| WOFF2 | Brotli | Эталон — минимальный | Все актуальные браузеры | Единственный нужный формат |
| WOFF | zlib | Примерно на четверть–треть тяжелее WOFF2 | Все, включая давно устаревшие | Оставлять только при требовании поддержки очень старых устройств |
| TTF / OTF | Без сжатия внутри файла | Кратно тяжелее WOFF2 | Все | Формат для дизайнеров, не для веба |
| EOT | Проприетарное | Тяжёлый | Только Internet Explorer | Удалять безусловно |
| SVG-шрифты | Нет | Самый тяжёлый | Технология удалена из браузеров | Удалять безусловно |
Практическое следствие: почистите @font-face до одной строки src с WOFF2. Это не только уменьшает вес — это убирает риск, что браузер скачает не тот формат из-за неправильного порядка перечисления. И проверьте, что сервер отдаёт шрифты с корректным Content-Type и, главное, со сжатием на уровне транспорта отключённым: WOFF2 уже сжат Brotli внутри, повторное gzip-сжатие на nginx только тратит процессорное время. Настройки уровня сервера подробнее — в материале про то, как хостинг влияет на скорость и позиции.
Локально или через CDN: развенчание старого мифа
Аргумент «подключим Google Fonts, он у пользователя уже в кэше с другого сайта» был верен примерно до 2020 года. Затем браузеры перешли на партиционированный кэш: ресурс, скачанный на сайте A, больше не переиспользуется на сайте B, даже если URL идентичен. Это сделано ради приватности — общий кэш позволял отслеживать пользователей. С тех пор общий аргумент в пользу стороннего шрифтового CDN не работает вообще.
Что осталось на чаше «внешний CDN»: не нужно ничего хостить, автоматически отдаётся оптимальный сабсет под браузер. Что на чаше «локально»: полное отсутствие лишних соединений и полный контроль.
Минус один домен в критическом пути
Сторонний шрифтовой сервис — это отдельные DNS-резолв, TCP-хендшейк и TLS-договорённость, а часто и два домена сразу (CSS с одного, файлы с другого). На мобильной сети это десятки и сотни миллисекунд ещё до первого байта шрифта.
Свой домен — свой HTTP/2
Файлы на собственном origin едут по уже установленному соединению вместе с остальными ресурсами, с общим управлением приоритетами. Внешний домен всегда стартует с нуля.
Cache-Control под вашим контролем
Локальный шрифт с хэшем в имени файла отдаётся с max-age на год и immutable. Никаких перепроверок, никакой зависимости от чужой политики кэширования.
Отказ третьей стороны не ломает сайт
Если внешний шрифтовой домен недоступен или отвечает медленно, ваш текст ждёт таймаута. Локальный файл такой зависимости не создаёт в принципе.
Нет передачи IP третьей стороне
Каждый запрос к внешнему шрифтовому сервису передаёт IP посетителя и заголовки на чужой сервер. Для проектов, где вопросы обработки персональных данных стоят остро, это отдельный аргумент.
Свой сабсет вместо чужого
Локально вы решаете, какие диапазоны Unicode и какие начертания включить. Внешний сервис отдаёт то, что заложено в его пресетах, и лишние килобайты вы не уберёте.
Отдельный вопрос — использовать ли собственный CDN для раздачи шрифтов. Если у вас уже настроена раздача статики через CDN на том же домене или поддомене с общим соединением, это скорее плюс: файлы едут с ближайшей точки присутствия. Если CDN добавляет новый домен в критический путь — эффект будет обратным. Логику выбора и подводные камни мы разбирали в статье про CDN и SEO.
preload и preconnect: чиним позднее обнаружение
Даже локальный, идеально сабсеченный WOFF2 стартует поздно — после CSS. Лечится это директивой предзагрузки в <head>: <link rel="preload" as="font" type="font/woff2" href="/fonts/main-cyrillic.woff2" crossorigin>. Она заставляет браузер начать загрузку сразу, параллельно с таблицей стилей.
Четыре правила, которые нарушают чаще всего
- Атрибут crossorigin обязателен всегда. Шрифты запрашиваются в режиме CORS даже с собственного домена. Без crossorigin браузер скачает файл дважды: один раз по preload, второй раз по реальному запросу из CSS. Это самая частая ошибка внедрения — и она делает preload вредным вместо полезного.
- Предзагружать только то, что видно на первом экране. Preload повышает приоритет ресурса, отбирая пропускную способность у других. Три-четыре предзагруженных шрифта конкурируют между собой и с LCP-изображением, и в сумме страница становится медленнее.
- Preload не отменяет font-display. Это разные механизмы: preload ускоряет загрузку, font-display определяет поведение во время неё. Нужны оба.
- Не предзагружать несуществующие пути. Опечатка в href даёт лишний запрос с ответом 404 и предупреждение в консоли, но не даёт ошибки на экране — поэтому живёт годами.
Если по каким-то причинам отказаться от стороннего сервиса нельзя, минимальная компенсация — <link rel="preconnect"> к обоим доменам сервиса (для CSS и для файлов), с тем же crossorigin для файлового домена. Это не убирает лишние соединения, но устанавливает их заранее, пока браузер занят HTML. Полумера, но она стоит одной строки.
Сколько начертаний вам действительно нужно
Типичная ситуация на коммерческом сайте: в @font-face объявлено шесть-восемь начертаний одной гарнитуры — Light, Regular, Medium, SemiBold, Bold, Black плюс курсивы. Реально в вёрстке используются два-три. Каждое лишнее начертание — отдельный файл, отдельный запрос и отдельный вес.
Считайте по факту: пройдите по вёрстке и выпишите, какие значения font-weight встречаются в CSS. Обычно набор сводится к обычному тексту (400), акцентам (600 или 700) и заголовкам. Всё остальное — наследство дизайн-макета, где дизайнер экспериментировал.
Вариативные шрифты: когда это выигрыш, а когда нет
Variable font — один файл, содержащий непрерывную ось начертаний: любое значение веса от 100 до 900 без отдельных файлов. Логика выбора простая арифметика:
- Нужно 1–2 начертания — статические сабсеты почти всегда легче. Вариативный файл несёт в себе всю ось и служебные таблицы, за которые вы платите независимо от того, сколько весов используете.
- Нужно 3–4 начертания — точка безубыточности, считайте на конкретных файлах после сабсеттинга.
- Нужно 5 и больше, есть анимация веса или плавная адаптация под ширину — вариативный вариант выигрывает уверенно.
Отдельно предупреждение: не полагайтесь на синтетическое начертание. Если объявлен только Regular, а в вёрстке стоит font-weight: 700, браузер нарисует «искусственный жирный» — механически утолщит контуры. Выглядит это грязно, а главное — метрики такого текста отличаются и от Regular, и от настоящего Bold, что добавляет ещё один источник сдвигов.
Иконочные шрифты: отдельная категория проблем
Шрифты с иконками (наследие библиотек вроде Font Awesome и самосборных наборов через генераторы) объединяют в себе все шрифтовые проблемы и добавляют свои.
- Вес не соответствует пользе. В наборе сотни глифов, на сайте используются 8–15. Сабсеттинг иконочного шрифта делают редко, потому что он неудобен: коды символов теряются вместе с классами.
- FOIT на иконках выглядит как поломка. До загрузки шрифта на месте иконок либо пусто, либо квадраты-заглушки, либо случайные символы из фолбэка — часто именно этот эффект видят пользователи в первую секунду.
- Гарантированный сдвиг. Иконка меняет ширину при подмене, и вся горизонтальная навигация или строка кнопок перестраивается.
- Доступность и семантика. Скринридер читает служебный символ; поисковый робот при рендеринге видит мусорный глиф в тексте ссылки, если иконка вставлена не через псевдоэлемент.
Правильное решение почти всегда — перевести иконки на SVG: инлайновый спрайт в разметке или отдельный файл со ссылками через <use>. SVG не блокирует отрисовку, не создаёт сдвига (у него есть явные размеры), масштабируется без потерь и корректно читается вспомогательными технологиями. Работа с векторной графикой и её оптимизацией пересекается с общей темой оптимизации изображений на сайте.
Влияние на Core Web Vitals: что именно меняется
Разложим по метрикам, чтобы понимать, чего ждать от каждой правки и как аргументировать её разработчику.
| Проблема | Механика | Какая метрика страдает | Решение | Приоритет |
|---|---|---|---|---|
| Нет font-display | Текст невидим до 3 с; если LCP-элемент текстовый — метрика ждёт шрифт | LCP, FCP | font-display: swap или optional | Критический |
| Свап без выравнивания метрик | Смена гарнитуры меняет число строк и высоту блоков | CLS | size-adjust + ascent/descent-override или optional | Критический |
| Шрифт грузится после CSS | Позднее обнаружение ресурса удлиняет цепочку | LCP | preload с crossorigin для шрифтов первого экрана | Высокий |
| Внешний шрифтовой домен | Дополнительные DNS + TCP + TLS перед первым байтом | LCP, FCP | Перенос файлов на свой origin | Высокий |
| Полный многоязычный файл | Скачиваются диапазоны, которые никогда не отрисуются | LCP на медленных сетях | Сабсет кириллицы + unicode-range | Высокий |
| 6–8 начертаний | Параллельные запросы конкурируют за канал с LCP-ресурсом | LCP | Оставить 2–3 реально используемых | Средний |
| Иконочный шрифт | Вес набора + сдвиг навигации при подмене | CLS, LCP | Перевод на SVG-спрайт | Средний |
| TTF/EOT в src | Риск скачивания несжатого формата | LCP | Оставить только WOFF2 | Средний |
| JS-подгонка размеров текста | Скрипт пересчитывает размеры после загрузки шрифта | CLS, INP | Убрать или перенести расчёт в CSS (clamp) | Низкий |
| Короткий Cache-Control | Повторные визиты снова тянут шрифты по сети | LCP при возврате | Хэш в имени + max-age на год + immutable | Низкий |
Важное уточнение про измерение. Лабораторные прогоны (Lighthouse, PageSpeed Insights в режиме анализа) почти всегда показывают шрифтовые проблемы мягче, чем они есть: тест идёт с холодным кэшем на смоделированной сети, но без реального разброса условий. Полевые данные — CrUX и Яндекс Метрика с её отчётами по скорости — дают честную картину. Особенно это касается CLS: у 75-го перцентиля реальных пользователей он часто в разы хуже лабораторного, потому что на медленных соединениях свап происходит позже, когда пользователь уже начал читать. Полная методика замеров и приоритизации описана в материале про скорость загрузки сайта.
Пошаговый аудит шрифтов на живом сайте
Последовательность, которую мы проходим на технической части SEO-аудита. Занимает от часа на простом сайте до дня на крупном проекте с несколькими шаблонами.
Снять инвентарь
DevTools → Network, фильтр Font, жёсткая перезагрузка. Выписать: сколько файлов, какой суммарный вес, с каких доменов, какие форматы, какие коды ответа. Отдельно повторить на мобильном профиле — шаблоны часто различаются.
Сопоставить с реальным использованием
Пройти по CSS и собрать все встречающиеся пары font-family + font-weight. Сравнить со списком загружаемых файлов. Разница — кандидаты на удаление; она почти всегда есть.
Проверить font-display
Найти все блоки @font-face (включая те, что приходят из сторонних CSS и плагинов) и убедиться, что дескриптор задан осознанно. Отсутствие = block по умолчанию = FOIT до трёх секунд.
Измерить шрифтовой вклад в CLS
Два прогона Lighthouse: обычный и с принудительной блокировкой шрифтовых запросов. Разница по CLS и есть цена подмены. Заодно видно, входит ли текст в LCP-элемент.
Пересобрать файлы
Сабсет по нужным диапазонам Unicode, конвертация в WOFF2, хэш в имени файла. Проверить сборку на странице с максимально «богатым» текстом: реквизиты, характеристики, таблицы, отзывы.
Перенести на свой origin
Убрать внешние домены из критического пути, настроить отдачу с длинным max-age и immutable, отключить повторное сжатие WOFF2 на уровне сервера.
Настроить preload и метрики фолбэка
Предзагрузить один-два шрифта первого экрана с crossorigin. Создать @font-face-фолбэк с size-adjust и переопределением вертикальных метрик, поставить его в стек перед системным.
Проверить на реальных устройствах и в полевых данных
Прогнать на среднем Android с дросселированной сетью, посмотреть, нет ли разнобоя глифов после сабсеттинга. Через 4 недели снять полевые метрики и сравнить с исходным срезом.
Восьмой шаг важен не меньше остальных: полевые данные обновляются с задержкой в 28 дней скользящего окна, поэтому сразу после выкатки вы увидите только лабораторные цифры. Планируйте контрольный замер заранее, чтобы результат не потерялся. Как выстроить систему регулярных проверок — в статье про внутреннюю оптимизацию сайта. Примеры технических перестроек на клиентских проектах есть в нашем портфолио.
Особенности популярных CMS и фреймворков
Практика показывает: источник шрифтовых проблем чаще всего не в основном шаблоне, а в том, что подтягивается вокруг него.
- WordPress. Классический сценарий: тема подключает свой набор шрифтов, конструктор страниц — свой, плагин формы — иконочный шрифт, плагин слайдера — ещё один. В сумме на странице оказывается десяток файлов из трёх источников. Начинайте аудит с отключения источников по одному; типовые узкие места разобраны в материале про SEO на WordPress.
- 1С-Битрикс. Шрифты обычно лежат в шаблоне и попадают в объединённый CSS. Ловушка — механизм оптимизации CSS, который может нарушить порядок правил @font-face или переписать относительные пути в src. Детали настройки — в статье про SEO на 1С-Битрикс.
- Конструкторы сайтов. Контроль над @font-face чаще всего отсутствует полностью: шрифты подключаются платформой так, как решила платформа. Максимум доступного — не добавлять сверху собственные внешние шрифты. Границы возможного описаны в разборе SEO на Tilda.
- SPA и приложения на JS-фреймворках. Шрифты нередко импортируются внутри JS-бандла, то есть обнаруживаются ещё позже обычного — после загрузки и выполнения скрипта. Плюс риск, что при серверном рендеринге стили шрифтов приезжают отдельным чанком. Тема пересекается с JavaScript-SEO.
ЧТО ПРОВЕРИТЬ ПЕРВЫМ ДЕЛОМ
Откройте DevTools на главной странице, отфильтруйте Network по типу Font и посмотрите на два числа: количество файлов и суммарный вес. Если файлов больше четырёх или суммарный вес превышает 150–200 КБ — у вас есть работа минимум на несколько часов, и она почти наверняка окупится быстрее, чем очередная итерация правок текстов.
Что шрифты дают бизнесу, а не только метрикам
Скорость — не самоцель, и в отчёте перед собственником цифра CLS сама по себе ничего не значит. Практический смысл шрифтовой оптимизации в трёх вещах.
Скорость до первого прочитанного слова. FOIT — это буквально секунды, когда посетитель смотрит на страницу без текста. На мобильном трафике из поиска, где терпение измеряется единицами секунд, это прямые отказы. Отказы на входной странице влияют на поведенческие факторы, а через них — на ранжирование.
Стабильность интерфейса. Сдвиг макета в момент, когда пользователь тянется к кнопке, — это мискликов и раздражение. На страницах с формами и корзиной это конвертируется в потери напрямую; связка «удобство → заявки» разобрана в статьях про юзабилити и SEO и конверсию сайта.
Устойчивость к плохим условиям. Значительная часть аудитории заходит с мобильного интернета переменного качества. Сайт, который в лаборатории показывает отличные цифры, а на 3G держит текст невидимым четыре секунды, теряет именно тех посетителей, за которых вы платите больше всего. Мобильная сторона вопроса подробно разобрана в материале про мобильную оптимизацию сайта.
И последнее по порядку, но не по важности: шрифтовые правки относятся к редкой категории задач с высоким соотношением эффекта к трудозатратам. Здесь не нужно менять архитектуру, переписывать бэкенд или согласовывать контент — почти вся работа умещается в правку @font-face, пересборку двух-трёх файлов и добавление нескольких строк в <head>. Это часто самая дешёвая точка входа в улучшение технических метрик на сайте, где всё остальное уже сделано.
Частые вопросы
Напрямую — никак: ни одна поисковая система не ранжирует по font-display. Влияние идёт двумя опосредованными путями. Первый — через Core Web Vitals, которые входят в оценку страничного опыта у Google и учитываются в комплексе технических сигналов у Яндекса. Второй, более весомый на практике, — через поведение: невидимый текст и прыгающая вёрстка увеличивают отказы, а отказы на входной странице поисковики видят и учитывают. Поэтому корректная постановка задачи звучит не «поднимем позиции шрифтами», а «уберём техническое препятствие, которое мешает работать всему остальному».
Одновременно и то, и другое даёт только один путь: font-display: swap плюс аккуратно выровненные метрики фолбэка через size-adjust, ascent-override, descent-override и line-gap-override. Тогда подмена происходит, но геометрия текста не меняется, и сдвига не возникает. Это требует вычисления значений по метрикам обеих гарнитур — работа на пару часов, зато результат постоянный. Вариант optional проще, но ценой того, что на первом визите часть пользователей увидит системный шрифт и брендовая типографика до них не доедет.
Для контентных проектов, блогов и сервисов, где типографика не является частью брендинга, это абсолютно рабочее решение: системный стек убирает из критического пути ноль запросов, ноль килобайт и ноль сдвигов. Для коммерческих сайтов, где фирменная гарнитура прописана в бренд-буке, отказ обычно не согласуют — и тогда задача сводится к тому, чтобы веб-шрифт стоил как можно дешевле: один сабсет, одно-два начертания, WOFF2, свой origin, preload и выровненные метрики.
Да, потому что preload решает не проблему сетевого расстояния, а проблему позднего обнаружения. Без него браузер узнаёт о шрифте только после загрузки и разбора CSS. Но предзагружать нужно строго то, что рисуется на первом экране, и обязательно с атрибутом crossorigin — без него файл скачается дважды, и правка из полезной превратится во вредную. Три и более preload одновременно обычно уже отбирают канал у LCP-изображения.
Самый быстрый метод — два лабораторных прогона одной страницы: обычный и с заблокированными шрифтовыми запросами (в DevTools это делается через блокировку паттерна запроса). Разница в CLS между прогонами и есть шрифтовой вклад. Дополнительно в панели Performance можно найти конкретные записи Layout Shift и посмотреть, какие узлы сдвинулись — если это абзацы и заголовки, а момент сдвига совпадает с завершением загрузки шрифта, вопрос закрыт. Полевую картину потом проверяйте по данным реальных пользователей, а не по лаборатории.
Может сломать, если список диапазонов составлен «на глаз». Правильный порядок: выгрузить все тексты сайта, включая динамические данные из карточек товаров, отзывов и характеристик, собрать по ним фактический набор символов и уже на его основе строить сабсет — этим занимаются инструменты вроде glyphhanger. Обязательно включите тире, кавычки-ёлочки, знак номера, символы валют и математические знаки: их часто забывают, и в результате в одной строке появляются глифы из двух разных гарнитур. После пересборки прогоните проверку на самых «богатых» текстом страницах.
Сначала выяснить, какой именно компонент их подключает: отключайте источники по одному и смотрите вкладку Network. Дальше три варианта по убыванию предпочтительности: настройка внутри самого компонента, если она предусмотрена; замена компонента на более лёгкий; в крайнем случае — снятие внешней регистрации стилей на уровне темы и подключение собственного, уже оптимизированного набора. На конструкторах с закрытым кодом возможности ограничены — там задача сводится к тому, чтобы хотя бы не добавлять сверху собственные внешние шрифты.
Универсального норматива нет, но рабочий ориентир для русскоязычного коммерческого сайта такой: одна гарнитура, два-три начертания, каждое — отдельным кириллическим сабсетом в WOFF2, суммарно в пределах нескольких десятков килобайт. Если файлов больше четырёх или суммарный вес переваливает за 150–200 КБ, это почти наверняка означает лишние начертания, отсутствие сабсеттинга либо параллельные наборы от разных плагинов. Точную цифру всегда сверяйте с полевыми метриками: важен не вес сам по себе, а его влияние на LCP у реальных пользователей.
Уберём шрифтовой тормоз и приведём Core Web Vitals в зелёную зону
Проведём аудит шрифтов: соберём инвентарь файлов, измерим их вклад в CLS и LCP, пересоберём кириллические сабсеты в WOFF2, перенесём на ваш origin, настроим font-display, preload и метрики фолбэка. Доведём правки до внедрения и покажем результат на полевых данных. Прозрачный договор, отчёты каждую неделю.
- Пакет «Старт» от 55 000 ₽/мес
- Пакет «Стандарт» 75 000 ₽/мес
- Пакет «Премиум» 95 000 ₽/мес
- Бесплатный аудит и прогноз
- Договор с гарантией результата
- Отчёты каждую неделю
Комментарии (14)
Дмитрий В.
Таблица по font-display — лучшее, что я видел по этой теме на русском. Наконец понял разницу между fallback и optional, до этого путал их местами и ставил наугад.
Марина_К
Про crossorigin в preload — это прямо про нас. Полгода жил preload без атрибута, в Network было по два запроса на каждый шрифт, и никто не замечал. Убрал дубли, LCP на мобильных заметно просел вниз.
AdminSEO Мастер
Марина, это действительно ошибка номер один при внедрении: preload без crossorigin не ускоряет, а удваивает трафик на шрифты. Заодно проверьте, сколько файлов вы предзагружаете — если их три и больше, они начинают отбирать канал у LCP-изображения.
vladimir77
Выкинул из @font-face EOT, SVG и TTF, оставил один WOFF2. Разметка стала короче в пять раз, ничего не сломалось.
Ольга
Раздел про size-adjust и ascent-override интересный, но непонятно, откуда брать конкретные числа. Вручную считать unitsPerEm и hhea-метрики страшновато, боюсь ошибиться и сделать хуже.
AdminSEO Мастер
Ольга, руками считать не нужно — есть готовые генераторы, которые берут метрики обеих гарнитур и выдают четыре дескриптора. Ваша задача после этого одна: сделать два скриншота первого экрана, с фолбэком и с загруженным шрифтом, и убедиться, что число строк в абзацах совпало. Если совпало — шрифтовой CLS у вас нулевой.
Сергей_бизнес
Всё логично, но optional в реальной жизни не согласуют. У нас бренд-бук, маркетолог за системный шрифт на первом экране голову снимет. Так что остаётся только возня с метриками, других вариантов у коммерческих сайтов нет.
Анна Т.
А как быть с иконочным шрифтом, если он вшит в тему и иконки расставлены классами по всему сайту? Переводить на SVG — это переписывать половину шаблона.
AdminSEO Мастер
Анна, полный перенос на SVG-спрайт правильнее, но начать можно дешевле: сабсетьте иконочный файл до тех 10–15 глифов, что реально используются, и поставьте ему font-display: block, чтобы вместо квадратов-заглушек была пустота. Это снимает самое неприятное — разъезжающуюся навигацию в шапке. На SVG переходите постепенно, начиная с иконок первого экрана.
Максим
Открыл DevTools, отфильтровал по Font, как советуете в конце. Девять файлов, 340 КБ. Пошёл разбираться, спасибо за пинок.
Станислав
Не преувеличиваете ли вы масштаб? Пара сотен килобайт шрифтов на фоне мегабайта картинок и джаваскрипта — это шум. Мне кажется, эффект будет в пределах погрешности замера.
Екатерина Л.
А что если сайт на Битриксе и включено объединение CSS? У нас после включения оптимизации шрифты просто перестали грузиться, пути в src поехали. Пришлось откатывать, и теперь боимся трогать.
AdminSEO Мастер
Екатерина, это ровно та ловушка, о которой в разделе про CMS: объединённый файл лежит в другой директории, и относительные пути в src ломаются. Лечится переводом путей на абсолютные, от корня сайта, — после этого объединение можно спокойно включать обратно. Если не хочется экспериментировать на живом проекте, приходите на бесплатный аудит, посмотрим вашу сборку.
pavel_dev
Подтверждаю арифметику по вариативным шрифтам. Считал на нашем проекте: два начертания статикой после кириллического сабсета вышли легче вариативного файла почти вдвое. Variable имеет смысл, когда весов реально пять и больше.
Ксения В.
Спасибо за таблицу диапазонов Unicode, забрала себе как чек-лист. Мы в прошлый раз забыли знак номера и кавычки-ёлочки, в реквизитах на странице контактов вылезла каша из двух гарнитур.
Тимур
Сайт на Тильде. Правильно понимаю, что мне из всего этого доступно только не добавлять свои внешние шрифты сверху? Или всё-таки есть что-то, что можно выжать?
AdminSEO Мастер
Тимур, к @font-face платформы доступа действительно нет, но два рычага остаются. Первый — не подключать поверх свои шрифты через блок с кодом, это самая частая самодеятельность. Второй — сократить число начертаний в настройках проекта до реально используемых: каждое лишнее это отдельный файл в критическом пути.
Жанна
После сабсеттинга у нас в отзывах поехали буквы ё и тире — как раз то, о чём предупреждаете в блоке про ловушку. Прогнали дамп текстов через сборщик символов, пересобрали, стало нормально.
Лев Т.
Про партиционированный кэш стоило написать ещё раз крупными буквами. До сих пор половина разработчиков искренне уверена, что шрифт с общего CDN уже лежит у пользователя с другого сайта.