cURL传数组时自动urlencode为x-www-form-urlencoded格式,后端可直接解析为$_POST;传JSON需手动json_encode并设Content-Type: application/json。

PHP cURL 发送 POST 数组参数时,curl_setopt($ch, CURLOPT_POSTFIELDS, $arr) 会自动转成 application/x-www-form-urlencoded
这是最常用也最容易出错的方式:直接传 PHP 关联数组给 CURLOPT_POSTFIELDS。cURL 会帮你 urlencode 并拼接成 a=1&b[0]=x&b[1]=y 这种格式——后端(尤其是 PHP)能原样解析成 $_POST 数组。
但注意:这种行为仅在 $arr 是数组且未被 json_encode 时生效;如果传的是 JSON 字符串或对象,cURL 就不会自动处理键名嵌套规则。
- ✅ 正确:传递
['name' => 'foo', 'tags' => ['php', 'curl']]→ 后端$_POST['tags'][0]可取到'php' - ❌ 错误:先
json_encode($arr)再传入 → cURL 当作纯文本发,Content-Type默认仍是application/x-www-form-urlencoded,后端收不到$_POST - ⚠️ 注意:
curl_setopt($ch, CURLOPT_POSTFIELDS, $arr)会忽略你手动设置的Content-Type头,除非你显式调用curl_setopt($ch, CURLOPT_HTTPHEADER, [...])
要发 JSON 数组,必须手动设 Content-Type: application/json 并传字符串
当目标接口明确要求 JSON(比如 Laravel 的 Request::isJson() 或 Node.js 接口),就不能依赖 cURL 自动编码。必须自己 json_encode,并覆盖请求头。
$data = ['items' => [1, 2, 3], 'meta' => ['source' => 'php-curl']];
$ch = curl_init('https://api.example.com/submit');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Accept: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
- 不设
Content-Type→ 大部分服务端会当成普通表单解析,json_encode后的字符串进不了$_POST,可能进php://input - PHP 后端若想读 JSON,得用
json_decode(file_get_contents('php://input'), true),不是$_POST - 某些框架(如 ThinkPHP)对
php://input有读取限制,需确认是否开启always_populate_raw_post_data(PHP 5.6+ 已废弃)
嵌套数组深层键名失效?检查是否用了数字索引导致 key 被丢弃
cURL 对数组的 urlencode 行为遵循 PHP 的 http_build_query 规则。如果你传的是 [0 => 'a', 1 => 'b'],它会变成 0=a&1=b,而非 arr[]=a&arr[]=b —— 后端 PHP 收到的是两个独立变量,不是数组。
立即学习“PHP免费学习笔记(深入)”;
- ✅ 想让后端得到
$_POST['arr'] = ['a','b'],应传:['arr' => ['a', 'b']]或['arr[]' => ['a','b']] - ✅ 更稳妥:用
http_build_query(['arr' => ['a','b']], '', '&', PHP_QUERY_RFC3986)显式构造字符串再传给CURLOPT_POSTFIELDS - ⚠️ 特别注意:如果数组键含点号(
.)或空格,http_build_query会转义,但某些老版本 Nginx + PHP-FPM 会截断或忽略这类 key
调试时看不到真实发送内容?用 curl_setopt($ch, CURLOPT_VERBOSE, 1) 或抓包
光看代码很难判断最终发出的 body 和 header 是什么。尤其当数组结构复杂、header 覆盖失败、或服务端返回 400 却没提示具体哪错了。
- 临时加
curl_setopt($ch, CURLOPT_VERBOSE, 1),输出会包含完整请求头和 body(注意重定向可能干扰) - 更可靠:用
tcpdump或Wireshark抓本地回环(lo),过滤port 80 or port 443,确认实际发出的 Content-Type 和原始 payload - 快速验证:把 cURL 请求复制到 Postman,改用
form-data/x-www-form-urlencoded/raw JSON三种模式分别试,比对响应差异
数组 POST 看似简单,但键名编码规则、Content-Type 匹配、服务端解析逻辑三者稍有错位,就收不到预期数据。重点盯住「发出去的是什么」和「对方按什么规则解」这两头,而不是只看 PHP 侧怎么构造数组。










