Как выполнить POST запрос через cURL в PHP: все способы и нюансы
Отправка POST-запросов через cURL в PHP: варианты и практические решения
Как отправить простой POST-запрос с данными формы через cURL?
Базовый способ - использовать curl_setopt с параметрами CURLOPT_POST и CURLOPT_POSTFIELDS. Этот вариант подходит для отправки данных в формате application/x-www-form-urlencoded - стандартный для веб-форм. Ниже приведён минимальный рабочий код.
$url = 'https://example.com/api';
$data = ['username' => 'test', 'password' => '123'];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
$response = curl_exec($ch);
curl_close($ch);
var_dump($response);
Curl x post php (отправка post через curl в php)
Объяснение шагов: функция curl_init создаёт сеанс; CURLOPT_POST включает метод POST; CURLOPT_POSTFIELDS принимает строку или массив. http_build_query преобразует массив в URL-кодированную строку. Результат запроса сохраняется в переменной $response.
Типичная ошибка: забыть установить CURLOPT_RETURNTRANSFER - тогда curl_exec выведет ответ напрямую и вернёт true. Это затрудняет обработку. Решение: всегда явно задавать этот параметр в true.
Другая проблема: если сервер ожидает данные в другом формате (например, JSON), код выше не сработает. Для JSON требуется изменить заголовки и формат POSTFIELDS.
Цель использования: отправка данных с формы, авторизация, работа с простыми REST API, где тело запроса - URL-кодированная строка. Это самый распространённый сценарий.
Как отправить JSON данные через cURL?
Для передачи JSON-объектов (например, при взаимодействии с современными API), нужно установить заголовок Content-Type: application/json и передать строку JSON в CURLOPT_POSTFIELDS.
$url = 'https://api.example.com/v1/login';
$jsonData = json_encode(['email' => 'user@example.com', 'pass' => 'secret']);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Content-Length: ' . strlen($jsonData)]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
Обратите внимание: CURLOPT_HTTPHEADER задаёт массив заголовков. В некоторых случаях достаточно указать только Content-Type. Поле Content-Length может быть опущено, но его добавление улучшает совместимость.
Ошибка: если сервер возвращает код 415 (Unsupported Media Type), значит неверно указан Content-Type. Проверьте, что ожидает API (часто application/json или text/json).
Также возможна проблема с кодировкой: PHP-функция json_encode по умолчанию работает с UTF-8, но если данные содержат нестандартные символы, стоит задать опции (JSON_UNESCAPED_UNICODE и т.д.).
Цель: отправка данных в формате JSON для RESTful API, микросервисов, веб-хуков.
Как отправить файл через POST (multipart/form-data)?
Для загрузки файлов используется составное тело запроса. В PHP cURL можно передать массив с путём к файлу, используя префикс @ (PHP 5.x) или объект CURLFile (рекомендован начиная с PHP 5.5).
$url = 'https://example.com/upload';
$file = '/tmp/photo.jpg';
$data = ['image' => new CURLFile($file, 'image/jpeg', 'photo.jpg'), 'description' => 'Загруженное фото'];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
При использовании CURLFile не требуется вручную указывать заголовок Content-Type: multipart/form-data - cURL устанавливает его автоматически, с правильной границей. Если передавать файл строкой через @ в старых версиях, это может работать, но лучше использовать современный класс.
Проблема: файл не найден - ошибка CURLFile: file not found. Убедитесь, что путь к файлу корректен и PHP имеет права на чтение.
Также возможна ошибка с большими файлами - превышение лимитов post_max_size или upload_max_filesize в php.ini. cURL не контролирует эти лимиты (они действуют на принимающем сервере), но если файл слишком велик, запрос может быть отвергнут.
Цель: загрузка изображений, документов, любых бинарных данных через формы.
Как выполнить PUT или DELETE запрос через cURL?
Для методов PUT, DELETE, PATCH используется параметр CURLOPT_CUSTOMREQUEST. Тело запроса можно передать через CURLOPT_POSTFIELDS (или другим способом, в зависимости от ожиданий сервера).
$url = 'https://api.example.com/items/123';
$jsonData = json_encode(['title' => 'Обновлённый заголовок']);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
Аналогично для DELETE - достаточно заменить 'PUT' на 'DELETE'. Тело для DELETE обычно пустое, но некоторые API требуют тело с идентификаторами для безопасного удаления.
Ошибка: сервер не принимает метод. Проверьте, что используемый тип HTTP-метода поддерживается конечной точкой. Если требуется метод PATCH, используйте 'PATCH'.
Забыли установить CURLOPT_POSTFIELDS? Для методов, отличных от POST, cURL не добавляет тело автоматически. Его нужно задавать явно.
Цель: обновление или удаление ресурсов через REST API.
Как добавить авторизацию (Basic Auth) к POST-запросу?
Для Basic Auth используется параметр CURLOPT_USERPWD в формате 'логин:пароль'. Это добавляет заголовок Authorization: Basic ....
$url = 'https://example.com/protected';
$data = ['action' => 'getInfo'];
$credentials = 'admin:12345';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_USERPWD, $credentials);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
Альтернативный способ - вручную задать заголовок Authorization через CURLOPT_HTTPHEADER с base64-кодированной строкой. Но использование CURLOPT_USERPWD проще и надёжнее.
Проблема: запрос идёт по HTTPS, но сертификат не проверяется по умолчанию. Для тестовых серверов можно отключить проверку, но в production этого делать нельзя. Используйте CURLOPT_SSL_VERIFYPEER и CURLOPT_CAINFO для корректной проверки.
Если требуется Bearer Token (например, JWT), задайте заголовок вручную: 'Authorization: Bearer ' . $token.
Цель: доступ к защищённым ресурсам, работа с API, требующим аутентификации.
Как настроить таймауты и обработку ошибок?
Для ограничения времени выполнения запроса используются CURLOPT_CONNECTTIMEOUT (таймаут соединения) и CURLOPT_TIMEOUT (таймаут всего запроса). Ошибки cURL можно получить через curl_error().
$url = 'https://example.com/slow';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'key=value');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // секунды
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$response = curl_exec($ch);
if ($response === false) {
$error = curl_error($ch);
// обработка: логирование, повторная попытка, сообщение пользователю
}
curl_close($ch);
Дополнительно можно получить HTTP-код ответа с помощью curl_getinfo($ch, CURLINFO_HTTP_CODE) и анализировать его (200 - OK, 4xx - клиентская ошибка, 5xx - серверная).
Типичная ошибка: запрос зависает из-за долгого ответа сервера. Решение - разумно выставить таймауты. Если сервер медленный, таймаут должен быть достаточно большим (30-60 секунд).
Ещё одна проблема: потеря соединения из-за нестабильной сети. Можно реализовать повторные попытки (retry) с экспоненциальной задержкой.
Цель: контроль времени выполнения, устойчивость скрипта к сетевым проблемам, правильная обработка ошибок.
Расширенные примеры отправки POST через cURL в PHP
В этом разделе приведены детальные примеры с пошаговым объяснением и демонстрацией результатов. Все примеры предполагают наличие рабочего тестового сервера (например, https://httpbin.org/post для отладки).
Пример 1: передача массива данных с различными полями (включая вложенные структуры)
Используем http_build_query и CURLOPT_POSTFIELDS для отправки вложенного массива.
$url = 'https://httpbin.org/post';
$data = [
'name' => 'Иван',
'age' => 30,
'address' => [
'city' => 'Москва',
'street' => 'Тверская'
],
'tags' => ['php', 'curl', 'post']
];
// http_build_query преобразует вложенные массивы в параметры вида address[city]=Москва&address[street]=Тверская&tags[0]=php...
$postFields = http_build_query($data);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Выведем HTTP-код и декодированный JSON-ответ
if ($httpCode === 200) {
$decoded = json_decode($response, true);
echo 'Формальные данные, которые получил сервер:' . PHP_EOL;
print_r($decoded['form']);
} else {
echo "Ошибка HTTP: $httpCode\n";
}
Результат выполнения:
Формальные данные, которые получил сервер:
Array
(
[name] => Иван
[age] => 30
[address[city]] => Москва
[address[street]] => Тверская
[tags[0]] => php
[tags[1]] => curl
[tags[2]] => post
)
Обратите внимание: вложенные массивы address и tags разворачиваются в плоскую структуру с квадратными скобками. Если сервер ожидает JSON, такой подход не подойдёт - надо использовать json_encode.
Пример 2: отправка JSON с авторизацией по токену и получение ответа
Демонстрация POST-запроса к https://httpbin.org/post с Bearer-токеном.
$url = 'https://httpbin.org/post';
$token = 'mysecrettoken123';
$payload = [
'action' => 'create',
'item' => [
'title' => 'Новый объект',
'price' => 100.50
]
];
$jsonPayload = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonPayload);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $token
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
echo 'HTTP-код: ' . $info['http_code'] . PHP_EOL;
echo 'Тело ответа (JSON):' . PHP_EOL;
echo $response . PHP_EOL;
Результат (пример):
HTTP-код: 200
Тело ответа (JSON):
{
"args": {},
"data": {
"action": "create",
"item": {
"title": "Новый объект",
"price": 100.5
}
},
"files": {},
"form": {},
"headers": {
"Authorization": "Bearer mysecrettoken123",
"Content-Type": "application/json",
...
},
...
}
Сервер httpbin.org возвращает отправленные данные в поле data. Обратите внимание на заголовок Authorization, который был передан.
Пример 3: загрузка файла с дополнительными полями формы
Используется CURLFile и массив POSTFIELDS. Сервер httpbin.org принимает файлы и возвращает их содержимое.
$url = 'https://httpbin.org/post';
// Создадим временный файл для примера
$tempFile = tempnam(sys_get_temp_dir(), 'test');
file_put_contents($tempFile, 'Это содержимое тестового файла');
$file = new CURLFile($tempFile, 'text/plain', 'example.txt');
$postData = [
'file' => $file,
'description' => 'Загрузка через cURL'
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$decoded = json_decode($response, true);
echo 'Полученные файлы:' . PHP_EOL;
print_r($decoded['files']);
echo 'Полученные поля формы:' . PHP_EOL;
print_r($decoded['form']);
// Удалим временный файл
unlink($tempFile);
Результат:
Полученные файлы:
Array
(
[file] => Это содержимое тестового файла
)
Полученные поля формы:
Array
(
[description] => Загрузка через cURL
)
Сервер получил файл и текстовое поле. CURLFile автоматически устанавливает MIME-тип (text/plain) и имя файла.
Пример 4: обработка перенаправлений (redirects) при POST
Иногда сервер после POST отвечает перенаправлением (301/302). cURL по умолчанию не следует за ними, если не включена опция CURLOPT_FOLLOWLOCATION. Но следует учесть, что при перенаправлении POST может превратиться в GET. Для сохранения метода используйте CURLOPT_POSTREDIR.
$url = 'http://example.com/redirect-after-post';
$data = ['action' => 'go'];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_POSTREDIR, 3); // 3 = CURL_REDIR_POST_ALL - разрешить замену метода на POST при редиректах
$response = curl_exec($ch);
$finalUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
curl_close($ch);
echo "Конечный URL после редиректов: $finalUrl\n";
echo "Ответ:\n$response\n";
Примечание: CURLOPT_POSTREDIR принимает битовую маску: 1 - только 301, 2 - только 302, 3 - оба. Это поведение доступно в PHP 5.3.2+ с libcurl 7.19.0+.
Пример 5: использование прокси-сервера для POST
Если требуется отправить запрос через HTTP/HTTPS прокси, задаются параметры CURLOPT_PROXY и CURLOPT_PROXYPORT. Для аутентификации - CURLOPT_PROXYUSERPWD.
$url = 'https://httpbin.org/post';
$proxy = 'proxy.example.com:8080';
$proxyAuth = 'user:pass';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'value=test');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PROXY, $proxy);
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyAuth);
// Если прокси требует NTLM-аутентификацию, можно указать:
// curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
$response = curl_exec($ch);
if ($response === false) {
echo 'Ошибка cURL: ' . curl_error($ch);
} else {
echo 'Ответ через прокси: ' . substr($response, 0, 200) . '...';
}
curl_close($ch);
Важно: не забывайте, что при использовании прокси все данные (включая POST-тело) проходят через него. Для чувствительных данных используйте HTTPS-прокси или шифрование на уровне приложения.
Пример 6: отправка POST с помощью потокового (streaming) тела
Для передачи больших объёмов данных без загрузки всей строки в память можно использовать callback-функцию CURLOPT_READFUNCTION. Пример ниже имитирует передачу данных порциями.
$url = 'https://httpbin.org/post';
$dataChunks = ['часть1 ', 'часть2 ', 'часть3'];
$index = 0;
$readCallback = function($ch, $fd, $length) use (&$dataChunks, &$index) {
if ($index >= count($dataChunks)) {
return '';
}
$chunk = substr($dataChunks[$index], 0, $length);
$dataChunks[$index] = substr($dataChunks[$index], strlen($chunk));
if ($dataChunks[$index] === '') {
$index++;
}
return $chunk;
};
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_PUT, false); // не используем PUT
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_READFUNCTION, $readCallback);
curl_setopt($ch, CURLOPT_INFILESIZE, array_sum(array_map('strlen', $dataChunks)) + strlen(implode('', $dataChunks))); // общий размер тела
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/octet-stream']);
$response = curl_exec($ch);
curl_close($ch);
echo 'Ответ: ' . $response;
Этот пример демонстрирует низкоуровневую работу. На практике для больших файлов проще использовать CURLFile или просто задать строку из файла через file_get_contents, но потоковая передача экономит память.
Пример 7: отправка POST с использованием мультисеансов (curl_multi)
Для параллельной отправки нескольких POST-запросов используется curl_multi_init и curl_multi_exec. Это ускоряет выполнение, когда нужно отправить данные на несколько серверов одновременно.
$urls = [
'https://httpbin.org/post',
'https://httpbin.org/post'
];
$data = ['key' => 'value'];
$mh = curl_multi_init();
$channels = [];
foreach ($urls as $i => $url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($mh, $ch);
$channels[$i] = $ch;
}
$active = null;
do {
$status = curl_multi_exec($mh, $active);
} while ($status === CURLM_CALL_MULTI_PERFORM || $active);
// Получение результатов
foreach ($channels as $i => $ch) {
$response = curl_multi_getcontent($ch);
echo "Ответ от запроса $i: " . substr($response, 0, 100) . "...\n";
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
curl_multi_close($mh);
Результат - два ответа от httpbin.org. Обратите внимание, что в реальной ситуации стоит обрабатывать ошибки каждого отдельного дескриптора.
Пример 8: отправка POST с пользовательскими опциями SSL
Если сервер использует самоподписанный сертификат или устаревший протокол, можно настроить проверку SSL.
$url = 'https://self-signed.badssl.com/post';
$data = ['test' => '1'];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Отключаем проверку сертификата (ТОЛЬКО ДЛЯ ТЕСТИРОВАНИЯ!)
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
// Альтернатива - указать путь к CA-пакету (рекомендуется)
// curl_setopt($ch, CURLOPT_CAINFO, '/path/to/cacert.pem');
$response = curl_exec($ch);
if ($response === false) {
echo 'SSL ошибка: ' . curl_error($ch);
} else {
echo 'Ответ получен (проверка SSL отключена) .\n';
}
curl_close($ch);
Предупреждение: отключение проверки SSL делает соединение уязвимым для атак man-in-the-middle. Используйте только в отладочных целях. Для production всегда указывайте валидный CA-пакет через CURLOPT_CAINFO.