Скорость загрузки сайта влияет на ранжирование в поисковой выдаче и восприятие пользователем, а, следовательно, на конверсию.
Средняя скорость реакции человека 200-250 миллисекунд.
То, что происходит в течение 250 миллисекунд человек воспринимает как «мгновенно», а несколько сотен миллисекунд - как «достаточно быстро».
Загрузку веб-страницы можно условно разделить на три большие части:
- Сеть
- Сервер
- Клиент
В этой статье рассматривается первая часть, но и другие две будут отчасти затронуты.
Как работает браузер?
Сначала пользователь вбивает URL в адресную строку браузера. Для примера возьмём адрес этой статьи: https://azat.io/ru/fluid-interfaces
.
Браузер разбивает адрес на части:
https
- протоколazat.io
- хост/ru/fluid-interfaces
- ресурс
На основе хоста браузер получает IP-адрес сервера, где находится сайт. Для этого используется DNS.
DNS-сервера сравнивают жёлтым телефонным книжкам с номерами телефонов, где имени контакта соответствует хост сайта, а номеру — его IP-адрес в сети.
Если запрос к хосту уже выполнялся, то запроса к DNS повторно не будет, и IP-адрес сервера браузер возьмёт из локального кэша.
Также браузер посещает файл hosts
, который содержит базу доменных имен и используется для их трансляции в сетевые адреса узлов (в macOS, например, этот файл находится в /private/etc/hosts
). Но это уже детали.
По IP-адресу посредством протокола передачи данных TCP устанавливается соединение. Для TCP-соединения важен порт: для протокола HTTP — 80, для HTTPS — 443.
При установке соединения по протоколу HTTPS происходит рукопожатие —TLS Handshake:
Клиент отправляет серверу сообщение, которое содержит версию криптографического протокола TLS и список поддерживаемых алгоритмов шифрования и методов компрессии данных. Сервер отвечает клиенту тем же и посылает SSL сертификат сервиса, который содержит публичный ключ.
Клиент в ответ генерирует случайную строку и шифрует с помощью публичного ключа сервера. Сервер расшифровывает её с помощью своего секретного ключа и использует эту строку для создания мастер-ключа, на основе которого клиент и сервер генерируют симметричный ключ.
После этого данные, полученные посредством HTTP, будут передаваться в зашифрованном виде с помощью симметричного ключа.
После установки соединения формируется HTTP-заголовок.
HTTP-заголовок — это текстовый заголовок, который начинается со строки, где указывается метод (GET
, POST
, PUT
и т.д.), за которой следует локейшен ресурса и версия HTTP протокола. Кроме того, указывается заголовок Host, который обычно совпадает с доменным именем и заголовок Connection: close
. Последний добавляется, потому что некоторые браузеры поддерживают постоянные соединения.
GET / HTTP/1.1
Host: azat.io
Connection: close
[...]
Ответ на HTTP-запрос содержит информацию об ошибке и её коде или заголовок со статус-кодом 200 и HTML-страницей. Время от начала процесса до получения первого пакета данных HTML называется Time to First Byte — TTFB.
Далее браузер начинает процесс парсинга HTML-страницы, браузер формирует DOM и CSSOM, создаёт дерево рендеринга и набор элементов, на основе которых делается отображение.
Скрипты могут изменять положение и размер элементов, что запускает процессы reflow и redraw. Парсинг страницы — задача объёмная, которая включает как минимум один reflow и один redraw.
Устройство сетевого взаимодействия
Ширина канала — максимальный объём данных, который можно передать через соединение за определённый период времени. Она измеряется в мегабитах в секунду и сильно влияет на скорость загрузки веб-страницы. Ширину канала можно сравнить с водопроводной трубой: чем она шире, тем больше воды сможет по ней пройти за единицу времени.
На мобильных устройствах ширина канала ниже, особенно в сетях 3G. Поэтому здесь влияние сети на скорость загрузки страницы выше, чем на десктопе.
Время, которое нужно, чтоб запрос дошёл от клиента до сервера и обратно называется время приёма-передачи или Round trip time — RTT.
Время приёма-передачи можно узнать с помощью команды ping
. Среднее время идеального раунд трипа - 50 миллисекунд.
PING azat.io (75.2.60.5): 56 data bytes
64 bytes from 75.2.60.5: icmp_seq=0 ttl=117 time=31.788 ms
64 bytes from 75.2.60.5: icmp_seq=1 ttl=117 time=32.157 ms
64 bytes from 75.2.60.5: icmp_seq=2 ttl=117 time=31.282 ms
64 bytes from 75.2.60.5: icmp_seq=3 ttl=117 time=31.204 ms
--- azat.io ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 31.204/31.608/32.157/0.388 ms
Кроме HTML веб-страница состоит также из CSS, JS файлов, картинок и т. п. На каждый ресурс нужен поход по сети.
На RTT напрямую влияет задержка — latency. Задержка растёт по мере роста расстояния до конечного сервера и маршрута к нему, поскольку данные передаются не через одну сеть, а проходят через несколько автономных систем. Также влияет качество сети: сети 3G могут иметь задержку 100-500 миллисекунд, что сильно замедляет загрузку контента. Другие причины: серверы, которые не могут справиться с большим объёмом трафика, загрузка сетей в местах скопления людей, и неэффективная работа маршрутизаторов.
С улучшением технологий ширина канала увеличивается, но задержку как минимум ограничивает скорость света.
Расстояние между Марсом и Землёй в разное время составляет от 55.75 млн. км до 401 млн. км. Так, сообщение с Марса будет доходить от 3 минут и 22 секунд до 44 минут.
По этой причине в Австралии и Новой Зеландии интернет-ресурсы грузятся, как правило, медленнее, поскольку большая часть серверов размещается в США и Европе.
В нестабильных сетях возникают проблемы с потерей пакетов, что приводит к росту RTT.
Как измерить скорость загрузки страницы?
Чтобы оценить перфоманс, можно использовать Lighthouse в инструментах разработчика Chrome DevTools или аналогичный облачный сервис.
Что там можно увидеть:
First Contentful Paint — время, которое требуется браузеру для отображения первой части содержимого DOM. Быстрым считается FCP, который не превышает 1.8 секунды.
Speed Index — скорость визуального отображения контента при загрузке страницы. Браузер оценивает эту скорость, сравнивая сайт с другими из HTTP-архива.
Largest Contentful Paint — время загрузки самого большого элемента в области просмотра пользователя.
Time to Interactive — время с момента начала загрузки до момента, когда страница станет полностью интерактивной.
Total Blocking Time — время блокировки страницы, когда пользователю недоступны никакие действия.
Cumulative Layout Shift — смещение макета из-за асинхронной загрузки ресурсов.
Что можно с этим сделать?
Спецификация медленного старта TCP устанавливает, что объём первого пакета данных всегда составляет 14 кБ.
Cервер не знает, с каким объёмом данных справится интернет-соединение, поэтому отправляется небольшое надёжное количество данных — 10 TCP пакетов с максимальным размером 1500 байтов.
По этой причине веб-страница размером в 14 кБ грузится существенно быстрее, чем веб-страница размером 15 кБ из-за дополнительного RTT, хотя разница между ними минимальна.
Для уменьшения объёма данных следует использовать алгоритмы сжатия данных, такие как Brotli и Gzip.
При использовании TCP/IP на задержки влияет количество маршрутов между клиентом и сервером, чем дальше пользователь находится от сервера, тем дольше он ждёт ответа. Проблему задержек решают сервисы доставки содержимого — CDN. Это распределенные по миру серверы, которые хранят статические данные.
Чтобы уменьшить влияние задержек нужно избегать цепочек последовательных вызовов. Например, когда шрифт подгружаются из CSS файлов.
Неплохой вариант — использование preload
:
<link
rel="preload"
href="/fonts/awesome-font.woff2"
type="font/woff2"
as="font"
crossorigin
/>
Во время загрузки стороннего веб-шрифта текст должен оставаться видимым. Это можно сделать, включив font-display: swap
:
@font-face {
font-family: 'Awesome Font';
font-style: normal;
font-weight: 400;
src: url('/fonts/awesome-font.woff2') format('woff2');
font-display: swap;
}
Отображение страницы замедляют редиректы для показа первой страницы. Вместо редиректа на мобильную версию сайта лучше верстать адаптивно.
Для ускорения показа страницы используют технику инлайна стилей, поскольку они загружаются с первого запроса, а даже preload
только со второго.
При работе со скриптами нужно использовать атрибуты defer
/async
, чтобы не было блокировок при отрисовке страницы. Также важно хорошо продумывать код-сплиттинг для оптимизации скорости загрузки.
Ленивая загрузка изображений и свойство content-visibility
в CSS - это хорошие способы уменьшения времени рендеринга страниц с большим количеством контента.
Использование протокола HTTP/2 ускоряет загрузку страницы. Это достигается благодаря тому, что при использовании второй версии протокола несколько запросов могут быть отправлены через одно TCP-соединение, независимо от типа ресурса, а также благодаря сжатию HTTP-заголовков.