{ "index": 24, "slug": "editorial-2027-05-practice-http-tls-guide", "title": "HTTP и TLS: как найти границу ошибки до того, как менять код", "excerpt": "404, 503 и ошибка сертификата возникают на разных этапах запроса. Разбираем их по наблюдаемым признакам, проверяем локальным примером и не превращаем retry или --insecure в случайное исправление.", "contentHtml": "

Браузер показывает ошибку, а команда сразу меняет timeout, маршрут или сертификат. Через час выясняется, что запрос вообще не дошёл до приложения. В другом случае приложение вернуло 404, но инженер ищет проблему в TLS. Цена такой ошибки — лишний rollout, потерянное время и риск сломать рабочий путь, пытаясь исправить не тот слой.

\n

Тезис: сначала нужно определить первый подтверждённый этап отказа. До HTTP находятся DNS, TCP и TLS. После успешного TLS появляются метод, URI, заголовки и статус HTTP. Если перепутать границу, проверка не отвечает на вопрос и создаёт ложное ощущение прогресса.

\n

Один запрос, несколько разных отказов

\n

У HTTPS-запроса есть последовательность. Клиент разрешает имя, открывает TCP-соединение, проводит TLS-рукопожатие, отправляет HTTP-сообщение и читает ответ. Посредник может завершить запрос на любом шаге. Поэтому текст ошибки важнее цвета страницы: ERR_TLS_CERT_ALTNAME_INVALID ещё не является HTTP-ответом, а 404 означает, что HTTP-обмен уже состоялся.

\n

Уровень ошибки задаёт набор допустимых проверок. Заголовок Host не исправит сертификат, если TLS-клиент не доверяет имени. Увеличение timeout не создаст отсутствующий маршрут. Повтор POST не становится безопасным только потому, что сервер вернул 503.

\n
Симптом → причина → проверка → действие
СимптомПричинаПроверкаДействие
ERR_TLS_CERT_ALTNAME_INVALIDИмя в URL не совпадает с SANСверить hostname, SAN и адресИсправить имя, сертификат или vhost
404 Not FoundURI или метод не попал в маршрутПроверить известный endpoint и лог маршрутаИсправить путь, метод или route config
503 Service UnavailableОбработчик или зависимость недоступныПроверить Retry-After и upstream-логиУстранить недоступность; retry ограничить контрактом
Timeout без статусаНеизвестен этап задержкиРазделить connect и read timeoutНайти этап, затем менять лимит
\n
\"Последовательность
Диагностика идёт слева направо. Первый наблюдаемый сбой ограничивает область поиска.
\n

Что означает HTTP-статус

\n

404 — это статус HTTP-ответа. Он не доказывает, что ответ сформировало origin-приложение: его мог вернуть reverse proxy или другой посредник. Сохраняйте метод, нормализованный путь, статус, request ID и безопасный набор заголовков. Полные Cookie, Authorization и чувствительные query-параметры в запись не нужны.

\n

503 сообщает о временной невозможности обработать запрос. Заголовок Retry-After может дать ориентир, но не гарантирует безопасность повтора. Для чтения задайте ограниченный retry с общим deadline. Для записи сначала проверьте идемпотентность и правило дедупликации. Иначе потерянный ответ после успешной записи превратится в дубль.

\n

Отрицательный путь обязателен. Если метод меняет деньги, заказ, подписку или другой ресурс, а сервер не принимает ключ операции и не описывает повтор, клиент должен остановиться. Автоматический retry в таком месте скрывает неопределённый результат.

\n

Почему ошибка сертификата возникает раньше HTTP

\n

TLS защищает канал и связывает его с именем узла. Клиент сравнивает имя назначения с именами в Subject Alternative Name сертификата. Если URL содержит старый alias, IP-адрес или имя другого виртуального хоста, проверка может завершиться до отправки HTTP-запроса. Тогда у приложения нет статуса и тела для анализа.

\n

Проверяйте три свойства: имя, цепочку доверия и срок действия. Общий текст certificate error скрывает различия между ними. Не подменяйте проверку флагом --insecure. Он может показать, что endpoint отвечает без валидации сертификата, но не исправляет доверие и не доказывает безопасность соединения.

\n

Учебная проверка на локальном сервере

\n

Пример ниже учебный. Он запускает только локальный HTTP-сервер, не обращается к внешней сети и не моделирует TLS. Его задача — показать разницу между известным маршрутом и отсутствующим URI. В production этот код не заменяет proxy, сертификат, health-check или журнал приложения.

\n
import { createServer } from 'node:http'; const server = createServer((req, res) => { if (req.method === 'GET' && req.url === '/health') { res.writeHead(200, { 'content-type': 'text/plain' }); res.end('ok'); return; } res.writeHead(404, { 'content-type': 'text/plain' }); res.end('missing'); }); server.listen({ port: 0, host: '127.0.0.1' }, async () => { const { port } = server.address(); const response = await fetch('http://127.0.0.1:' + port + '/missing'); console.log(response.status, await response.text()); server.close(); });
\n

Запуск node check-http.mjs в этом учебном случае печатает 404 missing. Если заменить путь на /health, получится 200 ok. Мы проверяем и статус, и тело. Один только текст страницы не показывает, какой контракт нарушен.

\n

Отправьте POST /health и получите 404: маршрут принимает только GET. Это не проблема TLS и не причина увеличивать timeout. Сначала решите, должен ли такой метод существовать в контракте.

\n

Порядок диагностики

\n
  1. Запишите URL, метод, время и request ID. Удалите Authorization, Cookie и персональные параметры.
  2. Проверьте DNS и адрес назначения отдельно от приложения. Несколько адресов могут вести к разным конфигурациям.
  3. Для HTTPS проверьте hostname, SAN, срок действия и цепочку сертификата обычным клиентом. Не отключайте проверку в рабочем запросе.
  4. После успешного TLS проверьте статус, Allow, Location, Retry-After, тип тела и идентификатор ответа.
  5. Сопоставьте метод с эффектом. Для изменения состояния определите идемпотентность или ключ операции до включения повторов.
  6. Сравните ошибочный запрос с безопасным endpoint на том же hostname и зафиксируйте ожидаемый результат.
\n

Ограничения

\n

HTTP-статус не раскрывает автоматически путь внутри прокси или состояние upstream. Заголовок Server не является доказательством источника ответа. DNS-ответ не доказывает наличие нужного маршрута. Для вывода о реальной инфраструктуре нужны согласованные логи, сетевые данные и разрешённый доступ.

\n

Локальный пример не проверяет CDN, балансировщик, корпоративный proxy, реальную цепочку сертификатов, рестарт процесса или запись в базе. Он показывает только границу между URI, методом и ответом локального HTTP-сервера. Не переносите его упрощённое поведение в production без явного контракта и тестов.

\n

Если TLS не проходит, не ищите заголовки приложения. Если TLS проходит, но статус равен 404, ищите маршрут и метод. Если пришёл 503, определяйте доступность обработчика и безопасность повтора. Если нет статуса, сначала найдите этап timeout.

\n

Проверяемый критерий готовности

\n

Диагностика готова, если для одного hostname можно воспроизвести успешный HTTPS-запрос, ошибку проверки имени сертификата, известный 404 и временный 503. Для каждого случая запись содержит этап, метод, путь, статус или TLS-ошибку, безопасный request ID и одно действие. Для записи с неопределённым результатом retry остановлен или защищён идемпотентным контрактом.

\n

Проверяемый результат — не «ошибка исчезла», а совпадение наблюдения с уровнем проверки. Повторите запрос после изменения одного условия. Если причина и новый результат не различаются по логам или команде, ремонт ещё не доказан.

\n

Проверяемые источники

\n" }