Этап 1 — Браузер: что происходит до отправки пакета

Кажется, что после Enter браузер мгновенно идёт в сеть. На самом деле он успевает сделать десяток вещей внутри себя — и если ошибиться на этом этапе, никакая оптимизация дальше не спасёт.

Рабочая ситуация

«Тикет: после релиза страница логина грузится 4 секунды, хотя бэкенд отвечает за 90 мс. Фронтендеры кивают на инфраструктуру, инфраструктура — на фронтендеров». Прежде чем читать дальше — подумай: где могут прятаться остальные 3,9 секунды, если сервер «не виноват»? Запиши 2–3 гипотезы. К концу урока ты сможешь проверить каждую из них вкладкой Network в DevTools — большинство таких секунд теряется ещё до того, как первый пакет ушёл в сеть, и на этапах соединения.

Что узнаешь — после урока ты сможешь
Вспомни из прошлых уроков

Этот трек — углубление. База должна сидеть крепко: HTTP: запрос, ответ, заголовки, коды — здесь мы разбираем, что происходит вокруг них; DNS: имя → адрес — понадобится уже в разделе про dns-prefetch; HTTPS/TLS: зачем шифровать — без этого не понять цену preconnect. Если что-то из трёх ссылок вызывает туман — сначала туда.

1. Архитектура браузера: кто делает запрос

Современный браузер — не одна программа, а оркестр процессов. Когда ты вводишь URL и жмёшь Enter, в работу включаются как минимум три:

ПроцессЧто делает
Browser process (главный)UI, адресная строка, вкладки, история. Решает «открыть URL — какой процесс этим займётся?»
Renderer process (по одному на сайт)Парсит HTML/CSS/JS, выполняет JS, рисует страницу. Изолирован — упал сайт, остальные живы.
Network process (один на браузер)Делает реальные сетевые запросы. Управляет соединениями, кэшем, куками. Через него идут запросы ВСЕХ вкладок.
GPU processРисует пиксели на экране.
Utility processesСервис-воркеры, расширения, аудио, видео.

Эта изоляция (sandbox) — фундамент безопасности современных браузеров. Один сайт не может прочитать память другого, и атака на JS не вылезает наружу процесса.

Аналогия

Браузер = бизнес-центр с разными отделами

Browser process — это ресепшен: ты приходишь сюда, говоришь «хочу попасть в офис №42», и тебя направляют. Network process — это служба доставки, она ходит наружу за заказами. Renderer process — отдельный офис каждого арендатора, в котором делается реальная работа. Никто не лезет в чужой офис.

2. Парсинг URL — спецификация WHATWG

Когда ты ввёл app.example.com/login?next=/dashboard, браузер должен это разобрать. Это сложнее, чем кажется — есть целая спецификация WHATWG URL.

Шаг 1: дополнение схемы

Если ты не написал https://, браузер сам это добавит. Логика:

Шаг 2: разбор по компонентам

# Берём URL:
https://alice:s3cr3t@api.example.com:8443/v2/users/42?tab=info&ref=email#section-2

scheme   # https
userinfo # alice:s3cr3t  (раритет, опасен)
host     # api.example.com
port     # 8443
path     # /v2/users/42
query    # tab=info&ref=email
fragment # section-2 (не уходит на сервер!)

Шаг 3: нормализация

Браузер приводит URL к каноническому виду:

Шаг 4: IDN и Punycode

Если в имени домена есть не-ASCII (например пример.рф или café.com), браузер преобразует его в Punycode:

café.com    →   xn--caf-dma.com
яндекс.рф   →   xn--d1acpjx3f.xn--p1ai

Это нужно, потому что DNS не понимает Unicode напрямую. Punycode — это «безопасный» ASCII-аналог любого Unicode-имени.

⚠️ Homograph-атака

Кириллическое «а» (U+0430) внешне неотличимо от латинского «a» (U+0061). Атакующий регистрирует аpple.com (с кириллической «а»), и пользователь не отличит от apple.com. Браузеры это знают и в подозрительных случаях показывают URL в Punycode (xn--pple-43d.com). Это твоя первая встреча с browser security.

3. Проверка локального HTTP-кэша

Прежде чем ходить в сеть, браузер смотрит — нет ли уже у меня этой страницы в кэше? И если есть — может ли я её использовать без перепроверки на сервере.

Кэшированный ответ имеет заголовки, которые сервер прислал в прошлый раз:

HTTP/2 200 OK
Cache-Control: public, max-age=300, immutable
ETag: "v2-7c8f3a"
Last-Modified: Wed, 15 May 2026 11:00:00 GMT
Date: Wed, 15 May 2026 11:55:00 GMT
Content-Type: text/html

Логика браузера:

  1. Свежий? Если (now - Date) < max-age и нет no-cache — отдаём из кэша моментально. Никакого сетевого запроса.
  2. Просрочен, но есть ETag? Делаем условный запрос: GET / HTTP/2 If-None-Match: "v2-7c8f3a". Сервер сравнит и ответит либо 304 Not Modified (используй кэш), либо новый 200 с новой версией.
  3. no-store? Кэш игнорируется, всегда новый запрос.
  4. Кэша вообще нет? Делаем обычный запрос.

Это огромная экономия трафика. Большой сайт может отдавать 80% запросов из браузерного кэша.

4. Иерархия кэшей

Слово «кэш» в браузере — это на самом деле несколько кэшей:

КэшГдеСрок жизни
Memory cacheВ оперативке renderer-процессаДо закрытия вкладки
Disk cacheНа дискеДо явного очищения / нехватки места
Service Worker cacheAPI CacheStorage, управляется JSКак настроит разработчик (даже offline)
Push cacheHTTP/2 server push (устарело)Жизнь HTTP/2-соединения
Prefetch cacheЗаранее загруженные ресурсы~5 минут

Когда ты делаешь fetch — браузер по цепочке смотрит во все эти кэши. Service Worker (если зарегистрирован сайтом) имеет приоритет — он может вообще перехватить запрос и ответить «из своего кэша», не идя в сеть. Так работают PWA.

5. Fetch — единый API запросов

В современных браузерах ВСЕ исходящие запросы — будь то «открыть страницу», img src=, fetch() из JS, AJAX, iframe — проходят через Fetch Standard. Это набор шагов:

  1. CORS check. Запрос идёт на тот же origin (схема+хост+порт)? Тогда всё разрешено. Иначе — проверка CORS, возможно preflight.
  2. Service Worker intercept. Если зарегистрирован SW и его scope покрывает URL — ему даётся возможность перехватить.
  3. HTTP cache lookup. Описано выше.
  4. Content Security Policy. Если сайт прислал заголовок CSP, проверяется, разрешён ли этот URL для этого типа ресурса.
  5. Permissions Policy. Можно ли вообще этому контексту делать такие запросы.
  6. Network — если ничего не остановило, идём в Network process.
От Enter до первого пакета: конвейер внутри браузера Enter browser process Парсинг URL схема, punycode Service Worker? может ответить сам HTTP-кэш свежий? → отдать CORS / CSP запрос разрешён? Куки, заголовки SameSite, Referer Network process соединение → пакет в сеть Каждый блок может завершить путь досрочно: SW отвечает из своего кэша, HTTP-кэш отдаёт 200 без сети, CORS/CSP блокируют запрос. До Network process доживают не все запросы.
Конвейер запроса внутри браузера. Самый быстрый запрос — тот, который не дошёл до сети.

6. Preconnect, preload, prefetch — гонка за миллисекунды

Большие сайты экономят на каждом миллисекунде. Поэтому ещё до того, как HTML догрузится, браузер может начать соединение с сервером — параллельно с парсингом HTML.

В HTML это указывается так:

<link rel="dns-prefetch" href="//fonts.googleapis.com">
<link rel="preconnect" href="https://api.example.com">
<link rel="preload"      as="font" href="/Inter.woff2" crossorigin>
<link rel="prefetch"     href="/dashboard">
ПодсказкаЧто делаетКогда применять
dns-prefetchРезолвит DNS заранееЗнаешь, что скоро понадобится этот домен (баннер, аналитика)
preconnectDNS + TCP + TLS-handshakeНа критичный домен — экономит ~100-200мс
preloadСразу качает ресурсШрифты, ключевые JS — то, без чего страница не работает
prefetchКачает в фон с низким приоритетомСледующая вероятная страница
modulepreloadpreload, но для JS-модулейСовременные ES-модули
DevOps-задача

Если у тебя сайт критичен к производительности, в шаблон HTML добавляешь preconnect на все домены, с которых придут шрифты, картинки, аналитика. Эффект — Largest Contentful Paint улучшается на 50-200 мс. Это просто, бесплатно, и почему-то 80% сайтов этим пренебрегают.

7. 103 Early Hints — что нового в HTTP

HTTP-статус 103 Early Hints — относительно новая возможность. Сервер может прислать сначала «предварительный» ответ со списком ресурсов, которые точно понадобятся для страницы, ещё до того, как сама страница готова.

# Сервер начинает обрабатывать запрос…
# …и сразу шлёт промежуточный ответ:
HTTP/2 103 Early Hints
Link: </css/critical.css>; rel=preload; as=style
Link: </fonts/Inter.woff2>; rel=preload; as=font; crossorigin

# Браузер начинает качать эти ресурсы, пока сервер ещё думает.
# Когда сервер закончит — пришлёт обычный 200 OK с HTML.
HTTP/2 200 OK
Content-Type: text/html

<!DOCTYPE html>…

Это позволяет «склеить» подготовку страницы и загрузку её ресурсов параллельно. Cloudflare и Fastly поддерживают, AWS CloudFront — в beta. Запомни и предлагай на собеседовании, если речь о performance.

8. Connection reuse — почему второй запрос быстрее

Открыл сайт. Браузер установил TCP+TLS соединение — занимает 100-300мс. А потом этот же сайт делает запрос к API на том же домене. Что происходит?

Браузер переиспользует уже открытое соединение. Никакого нового TCP/TLS — просто отправляет следующий запрос. Это называется HTTP keep-alive (в HTTP/1.1) или multiplexing (в HTTP/2 и /3).

Сколько соединений браузер держит к одному домену?

Поэтому когда сайт переходит с HTTP/1.1 на HTTP/2, скорость может вырасти кратно — особенно при множестве мелких ресурсов.

9. Connection coalescing — секретная оптимизация

А что, если у тебя на сайте подключены ресурсы с разных поддоменов одного хоста — www.example.com, api.example.com, cdn.example.com? По логике нужно три соединения.

Но если все три указывают через DNS на одни и те же IP, и у них один сертификат с Subject Alternative Names, покрывающий все три имени, — браузер использует одно HTTP/2-соединение для всех трёх. Это и есть connection coalescing.

В цифрах: сайт-«монстр» с 20 поддоменами на одной инфраструктуре в HTTP/2 откроет 1-2 соединения вместо 20. Экономит 1-2 секунды на первой загрузке.

10. Referrer и куки — что улетает наружу

Прежде чем браузер отправит запрос, он подкладывает:

GET /v2/users/42 HTTP/2
Host: api.example.com
Referer: https://app.example.com/dashboard       # откуда я пришёл
Origin: https://app.example.com                 # origin страницы (без пути)
User-Agent: Mozilla/5.0 ...
Accept-Language: ru-RU,ru;q=0.9,en;q=0.8
Sec-Ch-Ua: "Chrome";v="120", ...
Cookie: session=abc; csrf=xyz                # куки для этого хоста

Какие куки попадают в запрос

Браузер просматривает cookie jar и берёт куки, у которых:

Referrer Policy

Заголовок Referer может раскрывать чувствительную информацию (например, путь с токеном). Сайты управляют через Referrer-Policy:

Referrer-Policy: strict-origin-when-cross-origin  # умолчание сейчас
# Шлёт полный URL — на тот же origin. Только origin — на чужие HTTPS. Ничего — на HTTP.

11. CORS — почему запрос вообще может отказаться уйти

Кросс-доменные запросы (когда страница с одного origin делает запрос к другому) — это особый случай. Без специального разрешения сервера браузер их не отправит. Это Same-Origin Policy — фундамент веб-безопасности с 90-х.

Чтобы разрешить кросс-доменный запрос, сервер должен ответить заголовком:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true

Для «небезопасных» запросов (PUT, DELETE, не-простые заголовки) браузер сначала шлёт preflightOPTIONS-запрос, который спрашивает: «можно ли мне сделать такой запрос?». Если ответ ok — браузер шлёт настоящий запрос.

DevOps-боль

«CORS error» — самая частая жалоба разработчиков фронтенда. И всегда — это сервер что-то не так настроил, а не браузер «неправильно работает». Чините на стороне ответов сервера: правильные Access-Control-Allow-Origin, корректная обработка OPTIONS, правильные Vary-заголовки.

12. CSP — Content Security Policy

Сайт может прислать заголовок CSP, говорящий браузеру: «загружай ресурсы только с этих источников, остальные блокируй». Это защита от XSS — даже если злоумышленник внедрил скрипт через дыру в HTML, браузер откажется его исполнить.

Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.example.com;
  img-src 'self' data: https:;
  connect-src 'self' https://api.example.com;
  frame-ancestors 'none';

Это твоя обязательная гигиена как DevSecOps. Сайт без CSP в 2026 году — это плохо.

13. Просмотреть всё это в DevTools

Открой Chrome / Firefox DevTools (F12) → вкладка Network. Сделай запрос. Вкладка Timing у каждого ресурса покажет:

Queued at:           0 ms       # когда фетч встал в очередь
Started at:          12 ms      # когда реально начался
Stalled:             4 ms       # ожидание свободного соединения
DNS Lookup:          18 ms      # резолвинг (если кэша не было)
Initial connection:  40 ms      # TCP handshake
SSL:                 35 ms      # TLS handshake
Request sent:        0.3 ms     # отправка запроса
Waiting (TTFB):      150 ms     # ожидание ответа сервера
Content Download:    25 ms      # скачивание тела

Каждая из этих секунд — отдельный этап в этом deep dive. Через 9 уроков ты будешь читать эту таблицу как родную.

14. Мини-лаба: попробуй прямо сейчас

Всё из этого урока видно невооружённым глазом — нужен только терминал и браузер. Три упражнения, каждое на 3–5 минут.

Через curl увидеть тот же запрос

# Показать всё, что curl шлёт и получает
curl -v --http2 https://example.com

# Эмулировать поведение браузера — нужный User-Agent, follow redirects
curl -v -L \
  -H "User-Agent: Mozilla/5.0" \
  -H "Accept: text/html,application/xhtml+xml,*/*;q=0.8" \
  https://example.com

# Получить разбивку времени по фазам
curl -o /dev/null -s -w \
  "DNS: %{time_namelookup}s
Connect: %{time_connect}s
TLS: %{time_appconnect}s
TTFB: %{time_starttransfer}s
Total: %{time_total}s
" https://example.com

Посмотреть свой HTTP-кэш

Chrome: chrome://net-internals/#httpCache — список всех закэшированных ответов. Можно изучить.

Тестировать preconnect

# Открой DevTools → Performance → Record
# Загрузи страницу один раз с preconnect, второй — без.
# Сравни LCP (Largest Contentful Paint).
Ожидаемый результат

В curl -w ты увидишь, что DNS + Connect + TLS вместе занимают заметно больше, чем сама передача данных, — на быстрых сайтах именно установка соединения и есть основная цена запроса. Во втором прогоне той же команды DNS обычно падает почти до нуля (кэш резолвера). В DevTools → Network при перезагрузке страницы (F5) у большинства статики появится «(memory cache)» / «(disk cache)» или ответ 304 — это разделы 3–4 в действии.

15. Что с этим делает DevOps

16. Задачи самопроверки — реши, потом раскрой ответ

Задача 1. Пользователь жалуется: «ввожу адрес сайта, а попадаю в поиск Google». Адрес — intranet-portal (без точек). Что происходит и как это починить для сотрудников?

По спецификации WHATWG строка без точек не похожа на host — браузер отправляет её как поисковый запрос. Решения: приучить вводить http://intranet-portal (схема снимает неоднозначность), либо дать интранет-имени FQDN с точкой (intranet-portal.corp.local), либо настроить браузерную политику/поисковые ярлыки в корпоративном профиле.

Задача 2. В отчёте безопасности: пользователи получили фишинговую ссылку на «наш» сайт, но домен в письме выглядел в точности как настоящий. Как такое возможно и какой механизм браузера тут последняя линия защиты?

Это homograph-атака: в имени домена латинские буквы заменены визуально идентичными Unicode-символами (кириллическая «а» вместо латинской). DNS увидит совсем другое имя — punycode xn--…. Браузеры при подозрительном смешении алфавитов показывают адрес именно в punycode — приучай пользователей смотреть на адресную строку, а не на текст ссылки.

Задача 3. Ты выкатил новую версию CSS, но у половины пользователей стили старые. Заголовок ответа: Cache-Control: max-age=86400, имя файла style.css. Что произошло и как выкатывать правильно?

Браузеры честно держат файл сутки: пока max-age не истёк, они даже не спрашивают сервер. Правильная схема: статика с хэшем в имени (style.a1b2c3.css) и max-age=31536000, immutable, а HTML — no-cache: тогда новая версия HTML сразу ссылается на новый файл, а старый спокойно доживает в кэшах.

Задача 4. Фронтенд с app.example.com делает fetch на api.example.com и получает «CORS error», хотя бэкендер клянётся, что «GET работает, я curl'ом проверял». Почему curl работает, а браузер — нет?

CORS — ограничение именно браузера: curl не проверяет Same-Origin Policy. Браузер требует от сервера заголовок Access-Control-Allow-Origin (и корректный ответ на preflight OPTIONS, если запрос «не простой» — например, с заголовком Authorization). Чинится на сервере: правильные CORS-заголовки + обработка OPTIONS; curl тут ничего не докажет.

Задача 5. На странице подключены ресурсы с www, static и api поддоменов. В DevTools видно только одно TLS-соединение. Какие два условия для этого выполнились?

Сработал connection coalescing: (1) все имена резолвятся в одни и те же IP-адреса и (2) сертификат сервера покрывает все три имени через Subject Alternative Names. Тогда HTTP/2 гоняет запросы ко всем трём хостам по одному соединению.

17. Словарик урока

Термины этого этапа — по-русски и по-английски (собеседования и документация говорят на втором).

песочница · sandboxизоляция renderer-процесса: сайт не достаёт до чужой памяти и системы
разбор URL · URL parsingразложение адреса на схему, хост, порт, путь, query и fragment по спецификации WHATWG
punycodeASCII-кодировка Unicode-доменов (xn--…); DNS понимает только ASCII
атака-двойник · homograph attackдомен из визуально неотличимых символов другого алфавита
условный запрос · conditional requestGET с If-None-Match/If-Modified-Since; ответ 304 = «бери из кэша»
предсоединение · preconnectзаранее выполнить DNS + TCP + TLS к домену, который скоро понадобится
переиспользование соединения · connection reuseследующие запросы идут по уже открытому TCP/TLS (keep-alive, мультиплексирование)
склейка соединений · connection coalescingодно HTTP/2-соединение на несколько хостов с общими IP и сертификатом (SAN)
политика одного источника · Same-Origin Policyскрипты одного origin не читают ответы другого без разрешения (CORS)
предзапрос · preflightOPTIONS-запрос браузера перед «не простым» кросс-доменным запросом
TTFB · time to first byteвремя от отправки запроса до первого байта ответа — «сколько думал сервер + дорога»

Чек-лист «понимаю это»

Прежде чем пакет уйдёт в сеть, браузер: (1) парсит URL по WHATWG, (2) ищет ответ в иерархии кэшей, (3) проходит CORS/CSP-проверки, (4) переиспользует или открывает соединение, (5) подкладывает куки и заголовки. На этом этапе DevOps управляет: Cache-Control, HSTS, CSP, CORS, preconnect-подсказки.