PHP用cURL发POST请求的关键是CURLOPT_POSTFIELDS值类型决定Content-Type:传数组或http_build_query结果→application/x-www-form-urlencoded,服务端用$_POST接收;传JSON字符串→须手动设Content-Type: application/json,否则需读php://input。

PHP 用 cURL 发起 POST 请求的基本写法
直接调用 curl_init() + curl_setopt() 是最常用、最可控的方式。关键不是“能不能发”,而是参数设对没设对——尤其是 CURLOPT_POSTFIELDS 的值类型,它直接决定服务端收到的是表单数据还是原始 JSON。
- 传数组(如
['name' => 'Alice', 'age' => 25])→ 自动设Content-Type: application/x-www-form-urlencoded,服务端用$_POST接收 - 传 JSON 字符串(如
json_encode(['name'=>'Alice']))→ 必须手动加Content-Type: application/json,否则 PHP 后端收不到$_POST,得读php://input - 传
http_build_query()结果 → 和传数组效果一致,但更显式,适合需要控制编码或含特殊字符的场景
curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/login'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, ['username' => 'test', 'password' => '123']); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch);
POST 请求中 header 设置不生效的常见原因
CURLOPT_HTTPHEADER 看似简单,但几个细节极易踩坑:一是 header 数组必须是字符串数组(['Content-Type: application/json']),不能是关联数组;二是如果用了 CURLOPT_POSTFIELDS 数组,cURL 会自动加 Content-Type,覆盖你手动设的;三是某些服务器(如 Nginx)会过滤掉带下划线的 header 名,比如 X-Request-ID 没问题,X_Request_ID 可能被静默丢弃。
- 发 JSON 时务必显式设置:
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']) - 要发自定义 header(如认证 token),统一放在
CURLOPT_HTTPHEADER数组里,不要拆开调用 - 调试时加
curl_setopt($ch, CURLOPT_HEADER, true)查看真实发出的请求头
超时、错误处理和连接复用的实际建议
线上环境不设超时等于裸奔,而只设 CURLOPT_TIMEOUT 不够——它控制整个请求周期,但 DNS 解析、TCP 连接、SSL 握手这些前期耗时可能卡死在 30 秒以上。真正健壮的做法是分开设 CURLOPT_CONNECTTIMEOUT_MS(毫秒级)和 CURLOPT_TIMEOUT_MS(推荐都用 *_MS 版本)。
- 避免每次请求都
curl_init()+curl_close():复用$ch句柄,用curl_reset($ch)清除上次状态,减少系统资源开销 - 检查
curl_exec()返回值:false表示出错,必须配合curl_error($ch)和curl_errno($ch)定位(比如CURLE_COULDNT_RESOLVE_HOST是 DNS 问题,不是网络不通) - 敏感接口(如支付回调)建议加
CURLOPT_SSL_VERIFYPEER和CURLOPT_SSL_VERIFYHOST为 true,但测试环境可临时关掉避免证书报错
替代方案:file_get_contents() 能不能用?
可以,但限制极多。它依赖 allow_url_fopen=On(很多生产环境禁用),且配置 header 和超时只能靠 stream_context_create(),写法冗长、错误提示模糊,连重定向都得手动处理。唯一优势是不用装 cURL 扩展。
立即学习“PHP免费学习笔记(深入)”;
- 仅限简单、无认证、无复杂 header 的内部接口调试
- 一旦出现
400 Bad Request或空响应,基本没法快速定位是 header 缺失、JSON 格式错,还是编码问题 - 别指望它支持 HTTP/2 或 Unix socket 等高级特性——cURL 是事实标准,file_get_contents 就是备用轮子
$opts = [
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n",
'content' => json_encode(['id' => 123])
]
];
$result = file_get_contents('https://api.example.com/item', false, stream_context_create($opts));
实际项目里,95% 的 POST 场景用 cURL 更稳。真正容易被忽略的,是 CURLOPT_POSTFIELDS 类型与 Content-Type 的严格对应关系——传错类型,后端就收不到数据,还查不出错在哪。











