在使用PHP的cURL扩展发送POST请求时,如果目标服务端返回3xx重定向响应,默认情况下cURL会自动跟随重定向,但会将请求方法从POST改为GET,这就导致原本提交的POST数据全部丢失,后续请求无法获取到预期参数。要解决这个问题,需要了解cURL的重定向相关配置,调整对应的选项参数。

问题产生的原因
cURL的默认行为中,当遇到301、302等重定向响应时,会自动跳转到新的地址,但默认不会保持原有的POST请求方法和请求体。这是因为早期的HTTP协议规范中,重定向后的请求方法可能被修改,cURL遵循了这个默认逻辑,没有主动保留POST相关的数据。
解决方案一:开启POST重定向保持选项
从cURL 7.19.1版本开始,提供了CURLOPT_POSTREDIR选项,用来控制重定向时是否保持POST请求方法和数据。该选项可以接收以下值:
- 1:仅对301重定向保持POST
- 2:仅对302重定向保持POST
- 3:对301和302重定向都保持POST
- 4:对303重定向保持POST
- 7:对301、302、303重定向都保持POST
以下是使用该选项解决数据丢失问题的示例代码:
<?php
// 初始化cURL会话
$ch = curl_init();
// 设置请求地址
curl_setopt($ch, CURLOPT_URL, "http://ipipp.com/redirect_endpoint");
// 设置请求方法为POST
curl_setopt($ch, CURLOPT_POST, true);
// 设置POST数据
$postData = [
'username' => 'test_user',
'password' => 'test_pass'
];
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
// 开启重定向跟随
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
// 设置最大重定向次数
curl_setopt($ch, CURLOPT_MAXREDIRS, 3);
// 设置重定向时保持POST方法和数据,这里设置为7表示301、302、303都保持
curl_setopt($ch, CURLOPT_POSTREDIR, 7);
// 获取响应内容
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// 执行请求
$response = curl_exec($ch);
// 检查是否有错误
if (curl_errno($ch)) {
echo "cURL错误:" . curl_error($ch);
}
// 关闭cURL会话
curl_close($ch);
// 输出响应结果
echo $response;
?>
解决方案二:手动处理重定向流程
如果使用的cURL版本较低,不支持CURLOPT_POSTREDIR选项,可以手动处理重定向逻辑,先获取重定向地址,再重新发送POST请求。示例代码如下:
<?php
function sendPostWithRedirect($url, $postData, $maxRedirect = 3) {
$redirectCount = 0;
while ($redirectCount <= $maxRedirect) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// 不自动跟随重定向,获取响应头
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, false);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($response, 0, $headerSize);
curl_close($ch);
// 判断是否为重定向响应
if (in_array($httpCode, [301, 302, 303])) {
// 从响应头中提取Location字段
preg_match('/Location:s*(.*?)s/i', $header, $matches);
if (isset($matches[1])) {
$url = trim($matches[1]);
$redirectCount++;
continue;
}
}
// 非重定向响应,返回响应体
return substr($response, $headerSize);
}
return "超过最大重定向次数";
}
// 调用函数发送请求
$result = sendPostWithRedirect("http://ipipp.com/redirect_endpoint", [
'id' => 1001,
'name' => '测试数据'
]);
echo $result;
?>
注意事项
在使用上述方案时,需要注意以下几点:
- 如果目标服务端重定向后要求使用GET方法,那么即使设置了保持POST的选项,也可能导致请求失败,需要和服务端开发人员确认重定向后的请求要求。
- 开启
CURLOPT_FOLLOWLOCATION选项时,要确保CURLOPT_MAXREDIRS设置了合理的值,避免无限重定向导致请求卡死。 - 手动处理重定向时,要注意处理相对路径的重定向地址,需要拼接上原请求的主域名和路径,避免请求地址错误。
如果cURL版本支持CURLOPT_POSTREDIR选项,优先使用第一种方案,代码更简洁,性能也更好。