С чего начать Первые шаги трек · 15 ступеней
Linux Labs

HTTP — запросы, ответы, коды и заголовки

10 мин HTTP/1.1 · curl 8.5 · nginx 1.24

HTTP — протокол веба: клиент шлёт метод, путь и заголовки, сервер отвечает кодом, заголовками и телом.

Шпаргалка

8 команд Версия для печати
  • curl -i http://portal.lab.test/ код, заголовки и тело
  • curl -I http://portal.lab.test/ только заголовки (HEAD)
  • curl -v -o /dev/null http://portal.lab.test/ весь обмен
  • curl -s -o /dev/null -w '%{http_code}\n' URL только код
  • curl -H 'Host: docs.lab.test' http://127.0.0.1/ сайт по имени
  • curl -X POST -d 'a=1' http://portal.lab.test/f POST с телом
  • curl -L http://portal.lab.test/old пройти по 301 и 302
  • sudo tail /var/log/nginx/access.log какие коды отдал nginx

Обзор

HTTP — протокол, на котором браузер, curl или приложение разговаривает с веб-сервером. Клиент открывает TCP-соединение — порт 80, для HTTPS 443 — и шлёт запрос. Сервер присылает ответ. Оба — текст: первая строка, заголовки, пустая строка, тело.

Весь обмен показывает curl -v: строки с > — запрос, с < — ответ, с * — что делал сам curl (адрес, соединение, TLS).

text
*   Trying 127.0.0.1:80...
* Connected to portal.lab.test (127.0.0.1) port 80
> GET / HTTP/1.1
> Host: portal.lab.test
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.24.0 (Ubuntu)
< Content-Type: text/html
< Content-Length: 12

Запрос: метод, путь, Host

Первая строка запроса — метод, путь и версия протокола: GET / HTTP/1.1.

Метод Что просит клиент
GET отдать ресурс
HEAD то же, но без тела (curl -I)
POST принять данные: форму, JSON, файл
PUT, DELETE заменить или удалить ресурс (API)

Файлы с диска nginx отдаёт на GET и HEAD. POST или DELETE к такому файлу получает 405 Not Allowed.

Из заголовков запроса в HTTP/1.1 обязателен один — Host, имя сайта из адреса. По нему сервер выбирает сайт, когда на одном адресе и порту их несколько (виртуальные сайты). Запрос без Host nginx отклоняет кодом 400 Bad Request.

curl берёт Host из адреса, но его можно задать руками: curl -H 'Host: docs.lab.test' http://127.0.0.1/ соединяется с 127.0.0.1 и просит сайт docs.lab.test. Нет у сервера такого имени — ответит сервер по умолчанию.

Ответ: код, заголовки, тело

Первая строка ответа — версия, код и фраза: HTTP/1.1 200 OK. Первая цифра кода — класс ответа:

Класс Что значит Частые коды
1xx промежуточный ответ 101
2xx успех 200, 204
3xx ресурс по другому адресу 301, 302, 304
4xx сервер не принял запрос 400, 403, 404, 405, 413
5xx сбой на стороне сервера 500, 502, 503, 504

Код ставит тот, кто ответил. За обратным прокси это nginx или приложение за ним. 502 и 504 ставит сам прокси: до приложения он не достучался или не дождался ответа. Кто ответил на самом деле, видно по журналам: access.log nginx пишет код каждого запроса, журнал приложения — только те запросы, что до него дошли.

Из заголовков ответа чаще всего нужны Content-Type (что в теле), Content-Length (размер тела в байтах), Location (куда идти при 3xx) и Server (какой сервер ответил).

Перенаправление: 301 и 302

Ответ 3xx с заголовком Location отправляет клиента по другому адресу — перенаправление. 301 Moved Permanently — насовсем: браузер запоминает его и дальше идёт сразу на новый адрес, поисковики переносят туда страницу. 302 Found — временно: главным остаётся старый адрес.

text
$ curl -sI 'http://secure.lab.test/x?y=1'
HTTP/1.1 301 Moved Permanently
Location: https://secure.lab.test/x?y=1

Location — полный адрес вместе с путём и параметрами после ?. Потеряет их — пользователь окажется на главной вместо нужной страницы. curl -L идёт по перенаправлениям сам.

HTTPS

HTTPS — тот же HTTP внутри шифрованного соединения TLS. До первого байта HTTP клиент проверяет сертификат сервера, дальше методы, коды и заголовки те же. Порт по умолчанию — 443. Подробно — в статье openssl-tls.

Рецепты

4

Только код ответа

bash
curl -s -o /dev/null -w '%{http_code}\n' http://portal.lab.test/

-o /dev/null выбрасывает тело, -w печатает код. Код 000 значит, что ответа HTTP не было совсем: соединение не открылось или оборвалось. Причину покажет тот же запрос с -sS вместо -s.

Сайт по имени, когда DNS его не знает

bash
curl -H 'Host: docs.lab.test' http://127.0.0.1/
curl --resolve docs.lab.test:80:127.0.0.1 http://docs.lab.test/

Первая команда меняет только заголовок Host. Для HTTPS она не годится: имя нужно ещё и для проверки сертификата. --resolve подставляет адрес для имени, и curl ведёт себя так, будто DNS вернул этот адрес.

Запрос руками

bash
printf '%s\r\n' 'GET / HTTP/1.1' 'Host: portal.lab.test' \
  'Connection: close' '' | nc 127.0.0.1 80

Видно, что HTTP — просто текст: каждая строка кончается \r\n, заголовки — пустой строкой (последний аргумент ''). Connection: close просит сервер закрыть соединение после ответа, иначе nc ждёт дальше.

Кто ответил: nginx или приложение

bash
curl -sI http://app.lab.test/ | head -1     # код
sudo tail -n 3 /var/log/nginx/access.log    # код глазами nginx
sudo tail -n 3 /var/log/nginx/error.log     # почему

Строка access.log — адрес клиента, время, строка запроса, код и размер ответа: "POST / HTTP/1.1" 405 166. Запрос есть в access.log, а в журнале приложения его нет — ответ сформировал nginx, причину ищи в error.log.

Неполадки

9

400 Bad Request

Запрос не по правилам HTTP. Частые причины: нет заголовка Host (запрос руками) или обычный HTTP пришёл на порт HTTPS — тогда в теле ответа The plain HTTP request was sent to HTTPS port. Проверь схему в адресе: https:// для порта 443.

403 Forbidden

Сервер понял запрос и отказал. У nginx две частые причины, обе видны в error.log. (13: Permission denied) — у пользователя www-data нет права читать файл или входить в каталог: права по всему пути покажет namei -l. directory index of "…" is forbidden — запрошен каталог, а индексного файла в нём нет.

404 Not Found

Файла нет по пути, который собрал сервер. nginx пишет этот путь в error.log: open() "/var/www/portal/nope.html" failed (2: No such file or directory). Сравни его с тем, где файл лежит на самом деле.

405 Not Allowed

Метод не подходит ресурсу: POST или DELETE к статическому файлу. Посмотри метод в curl -v и проверь, что запрос уходит приложению, а не к файлу на диске.

413 Request Entity Too Large

Тело запроса больше разрешённого. У nginx предел по умолчанию — 1 МБ, в error.log — client intended to send too large body. Поднимает его директива client_max_body_size.

502 Bad Gateway, 504 Gateway Timeout

Ответ прокси: до приложения он не достучался (502) или не дождался ответа (504). Разбор по строкам error.log — в статье nginx.

Ответ 301, а ждёшь 200

Сервер отправляет на адрес из заголовка Location. nginx делает так для каталога без / в конце (/empty → /empty/) и при переходе с HTTP на HTTPS. curl -L идёт по редиректам сам, curl -I показывает, куда ведёт каждый.

curl: (35) … wrong version number

Обратная ошибка к 400 … sent to HTTPS port: https:// ушёл на порт, где сервер говорит обычным HTTP (https://host:80/). Поправь схему или порт.

curl: (7) Failed to connect … Couldn't connect to server

Ответа HTTP нет: на этом адресе и порту никто не слушает или соединение режет файрвол. Это уровень ниже HTTP — кто слушает порт, покажет sudo ss -tlnp на сервере. curl: (6) Could not resolve host — ещё раньше: имя не превратилось в адрес.

Опасно

1

Пароль по http:// виден в сети

Без TLS запрос идёт открытым текстом: пароль из формы, cookie и заголовок Authorization прочитает любой на пути пакета — tcpdump покажет их целиком. Вход и всё, что передаёт секреты, — только по https://.

Термины

5
HTTP-статус HTTP status
Трёхзначный код в ответе HTTP, описывающий результат обработки запроса сервером.
метод HTTP HTTP method
Первое слово запроса HTTP — что клиент просит сделать с ресурсом: GET — отдать, HEAD — только заголовки, POST — принять данные.
заголовок HTTP HTTP header
Строка «Имя: значение» после первой строки запроса или ответа — Host, Content-Type, Location и другие; заголовки кончаются пустой строкой.
заголовок Host Host header
Заголовок запроса с именем сайта из адреса. По нему сервер выбирает сайт среди нескольких на одном адресе и порту; в HTTP/1.1 он обязателен. Ещё встречается в: nginx, Unix-сокеты
перенаправление (301) redirect (301)
Ответ 3xx с заголовком Location — сервер отправляет клиента по другому адресу; 301 — насовсем, 302 — временно. curl -L идёт по ним сам. Ещё встречается в: Перенаправление вывода и конвейеры, /proc: процессы и ядро в виде файлов, tee, bash и ещё 9

См. также

  • curl — запросы HTTP из консоли: -v, -I, -w, -H.
  • nginx — веб-сервер: выбор сайта по Host, прокси, коды 502 и 504.
  • openssl-tls — HTTPS: сертификат, цепочка, ошибки проверки.
  • unix-sockets — HTTP без сети: приложение за nginx на сокете.
  • nc — сырое TCP-соединение, чтобы послать запрос руками.
  • ss — кто слушает порт 80 или 443.
  • dns-resolv — как имя сайта превращается в адрес.
Загрузка…