Как выполнить POST запрос через cURL в PHP: все способы и нюансы

Раздел: Веб-разработка на PHP -> Отправка запросов через cURL

Отправка 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.

Отправка POST через cURL в PHP - comments

En
Curl x post php (php)