// Engineering Log

HTTP, HTTPS и TLS: Часть 9 — Отладка HTTP с curl: подмена DNS, редиректы, тайминги и трассировка

Опубликовано 14.10.2026

// Быстрый маршрут

Эта статья относится к теме Деплой и стабильная работа.

curl — консольный HTTP-клиент, который есть в Linux, macOS и Windows 10 и новее. Для отладки он удобнее браузера: показывает запрос и ответ целиком, позволяет задать любой заголовок и метод, отправить запрос на конкретный IP в обход DNS и замерить, на каком этапе уходит время. Ниже — приёмы отладки: от просмотра заголовков до диагностики медленного сайта за балансировщиком. Если вы только начинаете работать с curl, начните со статьи «curl для начинающих»: там разобраны основные ключи, запросы к API и загрузка файлов.

Версия curl в системе может быть старой; часть ключей появилась недавно (у каждого указана версия). Свою версию и возможности сборки покажет curl --version.

Посмотреть ответ и заголовки

bash
curl https://example.ru/                 # только тело ответа
curl -i https://example.ru/              # заголовки ответа + тело
curl -I https://example.ru/              # только заголовки (метод HEAD)
curl -v https://example.ru/              # всё: DNS, TCP, TLS, запрос, ответ
curl -sv -o /dev/null https://example.ru/ 2>&1   # всё, кроме тела
curl -s -D - -o /dev/null https://example.ru/    # только заголовки, но методом GET

Что означают ключи:

  • -v (verbose) — подробный вывод в stderr: строки * — служебные сообщения (адрес, TLS, сертификат), > — отправленный запрос, < — полученные заголовки;
  • -s — без индикатора загрузки; вместе с -S ошибки всё равно выводятся;
  • -o /dev/null — тело выбросить; -o file — сохранить в файл, -O — сохранить под именем из адреса;
  • -D - — записать заголовки ответа в stdout.

Осторожно с -I. Он отправляет HEAD, а не GET. Часть приложений отвечает на HEAD иначе — 405, 404 или без нужных заголовков. Если ответ на -I выглядит странно, проверьте тот же адрес через -s -D - -o /dev/null.

Передать заголовки

Заголовок задаётся ключом -H, ключ можно повторять:

bash
curl -H 'Accept: application/json' \
     -H 'Authorization: Bearer eyJhbGciOi...' \
     -H 'X-Request-ID: debug-42' \
     https://api.example.ru/v1/orders

Особые формы:

bash
curl -H 'User-Agent:' https://example.ru/      # удалить заголовок, который curl добавляет сам
curl -H 'X-Empty;' https://example.ru/         # отправить заголовок с пустым значением
curl -H @headers.txt https://example.ru/       # взять заголовки из файла, по одному на строку (7.55+)

Для частых заголовков есть отдельные ключи:

bash
curl -A 'Mozilla/5.0 ...' https://example.ru/     # User-Agent
curl -e https://example.ru/ https://example.ru/x  # Referer
curl -u admin:secret https://example.ru/admin/    # Basic-авторизация
curl --oauth2-bearer TOKEN https://api.example.ru/ # Authorization: Bearer
curl --compressed https://example.ru/             # Accept-Encoding + распаковка ответа

Указывать -H 'Accept-Encoding: gzip' вручную не стоит: curl получит сжатый ответ и выведет его в терминал как есть. --compressed и запрашивает сжатие, и распаковывает.

Пароли и токены в командной строке видны в ps и остаются в истории оболочки. В скриптах передавайте их из файла: -H @auth-header.txt, или используйте ~/.netrc с ключом --netrc.

bash
curl -b 'session=abc123; lang=ru' https://example.ru/account   # отправить cookie
curl -c cookies.txt https://example.ru/login                   # сохранить полученные
curl -b cookies.txt -c cookies.txt https://example.ru/account  # читать и обновлять файл

Типичный сценарий — войти через форму и дальше ходить с полученной сессией:

bash
curl -c jar.txt -d 'login=ivanov&password=secret' https://example.ru/login
curl -b jar.txt https://example.ru/account

Методы и тело запроса

Метод curl выбирает сам: без тела — GET, с -d — POST, с -I — HEAD. Явно метод задаётся ключом -X:

bash
curl -X DELETE https://api.example.ru/v1/orders/42
curl -X PUT -H 'Content-Type: application/json' -d '{"status":"paid"}' https://api.example.ru/v1/orders/42

-X POST вместе с -d не нужен — POST и так будет. А -X вместе с -L опасен: curl применит заданный метод ко всем запросам после редиректов, даже там, где по стандарту должен был быть GET.

Форма (application/x-www-form-urlencoded):

bash
curl -d 'name=Иван&city=Москва' https://example.ru/form
curl --data-urlencode 'comment=Цена 100% & доставка' https://example.ru/form   # с кодированием

-d отправляет данные как есть и удаляет переводы строк из файла (-d @file). Для файлов без изменений — --data-binary @file, для строк с символом @ в начале — --data-raw.

JSON:

bash
curl --json '{"name":"Иван","qty":2}' https://api.example.ru/v1/orders    # curl 7.82+
curl --json @order.json https://api.example.ru/v1/orders

--json сам ставит Content-Type: application/json и Accept: application/json. В старых версиях то же самое:

bash
curl -H 'Content-Type: application/json' -d '{"name":"Иван"}' https://api.example.ru/v1/orders

Загрузка файла (multipart/form-data, как форма с полем «файл»):

bash
curl -F 'title=Отчёт' -F 'file=@report.pdf;type=application/pdf' https://example.ru/upload

Параметры GET из данных: -G превращает -d в строку запроса:

bash
curl -G -d 'q=nginx' -d 'page=2' https://example.ru/search   # GET /search?q=nginx&page=2

Редиректы

По умолчанию curl не следует перенаправлениям и показывает сам ответ 301/302. Проследить всю цепочку:

bash
curl -sIL https://example.ru/old-page | grep -iE '^(HTTP|location)'
HTTP/1.1 301 Moved Permanently
Location: https://example.ru/old-page
HTTP/2 301
location: https://www.example.ru/new-page
HTTP/2 200
  • -L — следовать редиректам;
  • --max-redirs 5 — ограничить число переходов (по умолчанию 50);
  • -w '%{num_redirects} %{url_effective}\n' — сколько было переходов и где закончилось.

При переходе на другой хост curl не передаёт дальше заголовок Authorization и данные -u — это защита от утечки. Если это действительно нужно, есть --location-trusted.

Запрос на конкретный сервер в обход DNS

Самый полезный приём при переезде, настройке балансировщика или проверке одного узла из нескольких.

--resolve — подставить IP-адрес для имени:

bash
curl --resolve example.ru:443:192.0.2.10 https://example.ru/

curl подключится к 192.0.2.10, но Host, SNI и проверка сертификата будут для example.ru — ровно как у браузера после смены DNS. Так проверяют новый сервер до переключения записи.

--connect-to — подменить хост и порт подключения:

bash
curl --connect-to example.ru:443:backend-2.internal:8443 https://example.ru/

Удобно, чтобы пройти мимо балансировщика прямо на один из серверов.

Почему не -H 'Host: …'. Запрос curl -H 'Host: example.ru' https://192.0.2.10/ поменяет только заголовок Host, а SNI и проверка сертификата пойдут для 192.0.2.10: сервер вернёт сертификат по умолчанию, и curl выдаст ошибку несовпадения имени. Для HTTPS используйте --resolve или --connect-to. Для HTTP -H 'Host: …' работает.

Замер времени

Ключ -w (write-out) выводит переменные после запроса. Разбивка по этапам:

bash
curl -s -o /dev/null -w '
dns:       %{time_namelookup}
tcp:       %{time_connect}
tls:       %{time_appconnect}
ttfb:      %{time_starttransfer}
total:     %{time_total}
code:      %{http_code}
http:      %{http_version}
ip:        %{remote_ip}
size:      %{size_download}
' https://example.ru/

Все времена — в секундах от начала запроса, нарастающим итогом:

  • time_namelookup — DNS; больше 0,1 с — проблема с резолвером;
  • time_connect − time_namelookup — установка TCP, примерно один круг до сервера (RTT);
  • time_appconnect − time_connect — рукопожатие TLS;
  • time_starttransfer − time_appconnect — время, пока сервер готовил ответ (TTFB без сети). Если большое — проблема в приложении или базе, а не в сети;
  • time_total − time_starttransfer — загрузка тела.

Шаблон удобно держать в файле и вызывать -w @curl-format.txt. С curl 7.70 есть -w '%{json}' — все переменные сразу в JSON, а с 7.83 — %{header_json} для заголовков ответа.

Проверка стабильности — серия запросов:

bash
for i in $(seq 1 20); do
  curl -s -o /dev/null -w '%{http_code} %{time_total}\n' https://example.ru/
done

Выбор версии и протокола

bash
curl --http1.1 https://example.ru/
curl --http2 https://example.ru/
curl --http3 https://example.ru/          # если сборка поддерживает HTTP/3
curl -4 https://example.ru/               # только IPv4
curl -6 https://example.ru/               # только IPv6

Если сайт открывается по IPv4 и не открывается у части пользователей, проверьте -6: частая причина — запись AAAA на адрес, где сайт не слушает.

Прокси

bash
curl -x http://proxy.example.ru:3128 https://example.ru/
curl -x socks5h://127.0.0.1:1080 https://example.ru/    # h — DNS тоже через прокси
curl --noproxy '*' https://example.ru/                  # игнорировать переменные окружения

curl учитывает переменные http_proxy, https_proxy, no_proxy. Если запрос из одного окружения работает, а из другого нет, проверьте env | grep -i proxy — прокси мог подхватиться незаметно.

Коды возврата и скрипты

По умолчанию curl завершается успешно (код 0) и при ответе 404 или 500 — ошибкой он считает только сбой соединения. Для скриптов и проверок:

bash
curl -fsS https://example.ru/health || echo 'сервис недоступен'
curl --fail-with-body -sS https://api.example.ru/v1/check   # 7.76+: код ошибки и тело ответа
  • -f (--fail) — при ответе 400 и выше код возврата 22;
  • --retry 3 --retry-delay 2 — повторить при временных ошибках (таймаут, 408, 429, 5xx);
  • --connect-timeout 5 --max-time 20 — ограничить время подключения и всего запроса. Без них curl может ждать зависший сервер очень долго.

Частые коды ошибок curl: 6 — не удалось разрешить имя; 7 — не удалось подключиться; 28 — таймаут; 35 — ошибка рукопожатия TLS; 60 — сертификат не прошёл проверку.

Полная трассировка

Когда -v недостаточно — например, нужно увидеть точные байты тела запроса или порядок событий:

bash
curl --trace-ascii trace.txt --trace-time https://example.ru/

В файле — каждый отправленный и полученный блок данных с временной меткой. --trace пишет то же с шестнадцатеричным дампом.

Скопировать запрос из браузера

В инструментах разработчика (F12) на вкладке Network щёлкните правой кнопкой по запросу → Copy → Copy as cURL. Получится команда со всеми заголовками и cookie браузера. Это самый быстрый способ повторить в консоли запрос, который ведёт себя странно, и затем по одному убирать заголовки, чтобы найти причину.

Учтите: такой запрос повторит заголовки, но не отпечаток TLS браузера. Если сайт за системой защиты от ботов отвечает браузеру 200, а тому же запросу из curl — 403, дело скорее всего в отпечатке.

Типичные ошибки

  • -I для проверки приложения, которое не обрабатывает HEAD, — ложные 404 и 405.
  • -H 'Host: …' для HTTPS вместо --resolve — ошибка сертификата и неверный сайт по умолчанию.
  • -k в скриптах проверки — истекший или чужой сертификат остаётся незамеченным.
  • Проверка доступности без -f — скрипт считает 502 успехом.
  • JSON через -d без Content-Type — curl отправляет application/x-www-form-urlencoded, и приложение не видит данных.
  • Нет --max-time в cron-задачах — зависшие процессы curl копятся.

// Похожая задача

Если у вас похожая ситуация

Эта статья относится к одной из рабочих тем. Можно продолжить чтение по теме, перейти на главную, чтобы понять, чем я занимаюсь, или сразу открыть услуги.

Тема статьи

Деплой и стабильная работа

Docker, CI/CD, релизы, мониторинг, observability и разбор инцидентов.

Часто с этим приходят

  • Настроить деплой без ручных действий и хаоса
  • Подключить мониторинг, алерты и базовую observability
  • Разобрать инциденты и стабилизировать production

// Следующий шаг

Если вам нужна не только статья, а помощь по этой теме, удобнее сразу перейти в услугу. Главная и подборка материалов остаются рядом.

Открыть услуги

// Reviews

Отзывы по теме

Пришел с дорогим запросом по настройке VPS-сервера, но в процессе консультации Михаил предложил гораздо более простое и экономичное решение. В итоге сэкономил бюджет и время. Михаил — настоящий эксперт, который работает на результат клиента, а не на чек. Рекомендую!

Пришел с дорогим запросом по настройке VPS-сервера, но в процессе консультации Михаил предложил гораздо более простое и экономичное решение. В итоге сэкономил бюджет и время. Михаил — настоящий эксперт, который работает …

kfhzasorin

Настройка vps, настройка сервера

12.05.2026 · ★ 5/5

Отличная работа! Очень быстро настроил сервер, установил панель, прописал IP. Однозначно могу порекомендовать!

Отличная работа ! Очень быстро настроил сервер, установил панель прописал IP Однозначно могу по рекомендовать !

fedinseo

Настройка vps, настройка сервера

19.04.2026 · ★ 5/5

Покупатель профи-эксперт

Было несколько проблем касаясь как технической части так и понимания в целом. Михаил быстро ответил на запрос, помог разобраться и решил проблеммы технические и помог разобраться в понимании, за что отдельное спасибо. Результатом доволен.

Было несколько проблем касаясь как технической части так и понимания в целом. Михаил быстро ответил на запрос, помог разобраться и решил проблеммы технические и помог разобраться в понимании, за что отдельное спасибо. …

abazawolf

Настройка vps, настройка сервера

18.02.2026 · ★ 5/5

// Contact

Нужна помощь?

Свяжись со мной и я помогу решить проблему

Написать в Telegram

Отвечаю в течение рабочего дня (03:00–13:00 GMT)

Или оставьте заявку здесь:

Подтвердите, что вы не бот.

Написать и получить быстрый ответ