Только код ответа
-o /dev/null выбрасывает тело, -w печатает код. Код 000 значит, что
ответа HTTP не было совсем: соединение не открылось или оборвалось. Причину
покажет тот же запрос с -sS вместо -s.
HTTP — протокол веба: клиент шлёт метод, путь и заголовки, сервер отвечает кодом, заголовками и телом.
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).
Первая строка запроса — метод, путь и версия протокола:
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 (какой сервер ответил).
Ответ 3xx с заголовком Location отправляет клиента по другому адресу —
перенаправление. 301 Moved Permanently — насовсем:
браузер запоминает его и дальше идёт сразу на новый адрес, поисковики переносят
туда страницу. 302 Found — временно: главным остаётся старый адрес.
Location — полный адрес вместе с путём и параметрами после ?. Потеряет их
— пользователь окажется на главной вместо нужной страницы. curl -L идёт по
перенаправлениям сам.
HTTPS — тот же HTTP внутри шифрованного соединения TLS. До первого байта HTTP клиент проверяет сертификат сервера, дальше методы, коды и заголовки те же. Порт по умолчанию — 443. Подробно — в статье openssl-tls.
-o /dev/null выбрасывает тело, -w печатает код. Код 000 значит, что
ответа HTTP не было совсем: соединение не открылось или оборвалось. Причину
покажет тот же запрос с -sS вместо -s.
Первая команда меняет только заголовок Host. Для HTTPS она не годится: имя
нужно ещё и для проверки сертификата. --resolve подставляет адрес для имени,
и curl ведёт себя так, будто DNS вернул этот адрес.
Видно, что HTTP — просто текст: каждая строка кончается \r\n, заголовки —
пустой строкой (последний аргумент ''). Connection: close просит сервер закрыть соединение после ответа,
иначе nc ждёт дальше.
Строка access.log — адрес клиента, время, строка запроса, код и размер
ответа: "POST / HTTP/1.1" 405 166. Запрос есть в access.log, а в журнале
приложения его нет — ответ сформировал nginx, причину ищи в error.log.
Запрос не по правилам HTTP. Частые причины: нет заголовка Host (запрос
руками) или обычный HTTP пришёл на порт HTTPS — тогда в теле ответа
The plain HTTP request was sent to HTTPS port. Проверь схему в адресе:
https:// для порта 443.
Сервер понял запрос и отказал. У nginx две частые причины, обе видны в
error.log. (13: Permission denied) — у пользователя www-data нет права
читать файл или входить в каталог: права по всему пути покажет namei -l.
directory index of "…" is forbidden — запрошен каталог, а индексного файла в
нём нет.
Файла нет по пути, который собрал сервер. nginx пишет этот путь в error.log:
open() "/var/www/portal/nope.html" failed (2: No such file or directory).
Сравни его с тем, где файл лежит на самом деле.
Метод не подходит ресурсу: POST или DELETE к статическому файлу. Посмотри
метод в curl -v и проверь, что запрос уходит приложению, а не к файлу на
диске.
Тело запроса больше разрешённого. У nginx предел по умолчанию — 1 МБ, в
error.log — client intended to send too large body. Поднимает его
директива client_max_body_size.
Ответ прокси: до приложения он не достучался (502) или не дождался ответа
(504). Разбор по строкам error.log — в статье nginx.
Сервер отправляет на адрес из заголовка Location. nginx делает так для
каталога без / в конце (/empty → /empty/) и при переходе с HTTP на HTTPS.
curl -L идёт по редиректам сам, curl -I показывает, куда ведёт каждый.
Обратная ошибка к 400 … sent to HTTPS port: https:// ушёл на порт, где
сервер говорит обычным HTTP (https://host:80/). Поправь схему или порт.
Ответа HTTP нет: на этом адресе и порту никто не слушает или соединение режет
файрвол. Это уровень ниже HTTP — кто слушает порт, покажет
sudo ss -tlnp на сервере. curl: (6) Could not resolve host — ещё раньше:
имя не превратилось в адрес.
Без TLS запрос идёт открытым текстом: пароль из формы, cookie и заголовок
Authorization прочитает любой на пути пакета — tcpdump покажет их целиком.
Вход и всё, что передаёт секреты, — только по https://.
-v, -I, -w, -H.Есть неточность? Сообщить об ошибке в статье