curl отправляет запрос по URL и печатает тело ответа в stdout. Это главный способ проверить веб-сервер или API из консоли: что он отвечает, с каким кодом и какими заголовками, сколько времени занял запрос.
Синтаксис
curl [опции] URL…
Без опций curl делает GET и выводит тело. Метод, заголовки и тело задаются флагами. Короткие флаги можно склеивать: -sSfL = -s -S -f -L.
Вывод и сохранение
| Опция | Что делает |
|---|
-s | тихий режим: без индикатора прогресса и без сообщений об ошибках |
-S | вместе с -s всё-таки показывает ошибки — пара -sS |
-o файл | сохранить тело в файл; -o /dev/null — выбросить |
-O | сохранить под именем из URL (последний сегмент пути) |
-J | вместе с -O: взять имя из заголовка Content-Disposition |
-C - | докачать файл с места обрыва |
-i | показать заголовки ответа вместе с телом |
-I | запрос HEAD: только заголовки ответа |
-D файл | записать заголовки ответа в файл; -D - — в stdout |
-w 'формат' | после запроса напечатать переменные (см. ниже); -w @файл — формат из файла |
-f | при коде 400 и выше не печатать тело, код выхода 22 |
--fail-with-body | как -f, но тело ошибки печатается |
Метод, заголовки, тело
| Опция | Что делает |
|---|
-X МЕТОД | задать метод: PUT, DELETE, PATCH… |
-H 'Имя: значение' | добавить заголовок; можно повторять. -H 'Имя:' — убрать заголовок, который curl ставит сам |
-A 'строка' | заголовок User-Agent |
-e URL | заголовок Referer |
-b 'k=v' / -b файл | отправить cookie строкой или из файла |
-c файл | сохранить полученные cookie в файл (cookie jar) |
-d 'данные' | тело запроса; делает POST с application/x-www-form-urlencoded. -d @файл — из файла, переводы строк выкидываются |
--data-binary @файл | тело из файла байт в байт |
--data-raw 'данные' | как -d, но @ в начале — обычный символ |
--data-urlencode 'k=v' | закодировать значение для URL (пробел → +, & → %26) |
-G | превратить все -d в параметры строки запроса и отправить GET |
--json 'JSON' | тело JSON и заголовки Content-Type/Accept: application/json (curl 7.82+) |
-F 'k=v' / -F 'file=@путь' | форма multipart/form-data, загрузка файла |
-T файл | загрузить файл методом PUT |
--compressed | попросить сжатый ответ и распаковать его |
Авторизация и TLS
| Опция | Что делает |
|---|
-u логин:пароль | Basic-авторизация; -u логин — пароль спросит |
-H 'Authorization: Bearer ТОКЕН' | токен-авторизация |
-k | не проверять сертификат сервера |
--cacert ca.pem | доверять этому CA (для своего или самоподписанного сертификата) |
--cert cert.pem --key key.pem | клиентский сертификат (mTLS) |
Сеть, редиректы, таймауты
| Опция | Что делает |
|---|
-L | идти по редиректам (301, 302…); --max-redirs N — не больше N переходов |
--resolve хост:порт:IP | для этого хоста и порта ходить на указанный IP, минуя DNS |
--connect-to хост:порт:новый_хост:новый_порт | подключаться к другому адресу, оставив в запросе исходное имя |
-x http://прокси:порт | через прокси; --noproxy '*' — игнорировать переменные http_proxy |
-4 / -6 | только IPv4 / только IPv6 |
--connect-timeout N | ждать соединения не больше N секунд |
-m N / --max-time N | весь запрос не дольше N секунд (код выхода 28) |
--retry N | повторить при временных ошибках; --retry-delay S, --retry-connrefused — и при отказе в соединении |
--http1.1 / --http2 | версия протокола |
Отладка
| Опция | Что делает |
|---|
-v | заголовки запроса (>), ответа (<), служебные строки (*): DNS, подключение, TLS |
--trace-ascii файл | полный дамп обмена, включая тело; - — в stdout |
Переменные для -w
| Переменная | Что значит |
|---|
%{http_code} | код ответа (000, если ответа не было) |
%{time_namelookup}, %{time_connect}, %{time_appconnect} | время до окончания DNS, TCP-подключения, TLS-рукопожатия (секунды, от начала) |
%{time_starttransfer} | время до первого байта ответа (TTFB) |
%{time_total} | полное время запроса |
%{size_download}, %{content_type} | размер тела в байтах, тип содержимого |
%{remote_ip}, %{remote_port} | куда реально подключились |
%{redirect_url} | куда указывает редирект (без -L) |
%{url_effective}, %{num_redirects} | итоговый URL и число переходов (с -L) |
%{ssl_verify_result} | результат проверки сертификата: 0 — успешно |
%header{имя} | значение заголовка ответа (curl 7.84+) |
%{exitcode}, %{errormsg} | код ошибки curl и её текст (curl 7.75+) |
%{json} | все переменные одним JSON-объектом |
Коды выхода
0 — ответ получен (любой, даже 500, если нет -f), 6 — не удалось разрешить имя, 7 — не удалось подключиться, 22 — HTTP-ошибка при -f, 28 — таймаут, 35 — ошибка TLS-рукопожатия, 47 — превышен --max-redirs, 60 — сертификат не прошёл проверку.
Примеры
# только код ответа — удобно в скриптах и проверках
curl -s -o /dev/null -w '%{http_code}\n' http://localhost/
# заголовки ответа без тела
curl -I http://localhost/
# заголовки и тело реального GET-запроса
curl -si http://localhost/
# куда и за сколько: DNS, подключение, первый байт, всё вместе
curl -s -o /dev/null -w 'dns=%{time_namelookup} conn=%{time_connect} ttfb=%{time_starttransfer} total=%{time_total}\n' http://localhost/
# скачать файл, следуя редиректам; ошибки показать, прогресс скрыть
curl -sSL -o archive.tar.gz https://example.com/archive.tar.gz
# POST с JSON
curl -s -H 'Content-Type: application/json' -d '{"name":"demo"}' http://localhost:8000/api/items
# GET с параметрами, закодированными как надо
curl -s -G --data-urlencode 'q=a b&c' -d page=2 http://localhost:8000/search
# загрузить файл формой
curl -s -F 'file=@report.pdf' -F 'title=Отчёт' http://localhost:8000/upload
# удалить ресурс
curl -s -X DELETE http://localhost:8000/api/items/1
# Basic-авторизация и свой заголовок
curl -s -u admin:secret -H 'Accept: application/json' http://localhost:8000/
# залогиниться, сохранив cookie, и пойти с ними дальше
curl -s -c cookies.txt -d 'user=demo&pass=demo' http://localhost:8000/login
curl -s -b cookies.txt http://localhost:8000/profile
# проверить виртуальный хост, не трогая DNS и /etc/hosts
curl -s --resolve example.com:80:127.0.0.1 http://example.com/
# HTTPS с самоподписанным сертификатом: лучше --cacert, чем -k
curl -s --cacert cert.pem https://localhost:8443/
# значение одного заголовка ответа
curl -s -o /dev/null -w '%header{content-type}\n' http://localhost/
# упасть с ненулевым кодом выхода, если сервер ответил ошибкой
curl -sSf --max-time 5 http://localhost/health || echo 'сервис не отвечает'
Подводные камни
- Без
-f код выхода 0 даже на 404 и 500. curl считает успехом сам факт ответа. Для проверок бери -f или явно сравнивай %{http_code}.
-s глотает и ошибки. curl -s на упавший сервер ничего не напишет. Добавляй -S, чтобы видеть причину (Couldn't connect to server, SSL certificate problem).
-d @файл убирает переводы строк. Для многострочного JSON, бинарных данных и вообще «отправить файл как есть» нужен --data-binary @файл.
-d без Content-Type уходит как application/x-www-form-urlencoded. API, который ждёт JSON, такой запрос не поймёт — добавь заголовок или используй --json.
-X POST не добавляет тело, а -d уже делает запрос POST. А -X вместе с -L применяется ко всем переходам — после 302 сервер ждёт GET, а получит снова POST.
- Без
-L редирект не выполняется: получишь 301 и короткую страницу-заглушку. Смотри -w '%{redirect_url}' или -i, куда ведёт переход.
-I — это HEAD, а не GET. Некоторые приложения отвечают на HEAD иначе. Чтобы увидеть заголовки настоящего GET, используй -i, -D - или -v.
-k отключает защиту целиком. Для своего сертификата лучше --cacert cert.pem: проверка останется, просто с твоим доверенным корнем.
- Спецсимволы в URL.
& и ? в URL без кавычек разрежет оболочка — бери URL в кавычки.
-O не работает, если URL кончается на / — имени файла нет. Тогда задай имя через -o.
См. также
nginx · openssl · jq · dig · ss