开启cURL详细调试需同时设置CURLOPT_VERBOSE和CURLOPT_STDERR(如php://temp),否则无输出;CURLOPT_HEADER仅控制响应体是否含响应头,不影响调试日志;要确认实际POST内容,须在curl_exec前手动记录$data,因日志只显示“sent n bytes”而不明文展示body。

用 curl_setopt 开启详细调试输出
PHP 的 cURL 扩展本身不直接打印请求/响应体,但可通过 CURLOPT_VERBOSE 和 CURLOPT_STDERR 把底层通信过程(包括请求头、响应头、重定向跳转)输出到指定位置。关键不是“看到 POST 数据”,而是让 cURL 把它自己发了什么、收到什么都吐出来。
常见错误是只设 CURLOPT_VERBOSE => true 却没配 CURLOPT_STDERR,结果什么也没看到——因为默认输出到 STDERR,而 CLI 下可能被忽略,Web 环境下更常被静默丢弃。
- 必须用
fopen('php://temp', 'w+')或临时文件句柄赋给CURLOPT_STDERR -
CURLOPT_HEADER设为true只影响响应体是否包含响应头,不影响调试日志 - 若需捕获原始请求体(比如你手动拼的
http_build_query($data)),得在curl_setopt($ch, CURLOPT_POSTFIELDS, $data)前单独var_dump($data)或记录
抓取真实发出的 POST 内容:绕过 cURL 封装看原始字节
cURL 调试日志里不会明文显示 POST body 的完整内容(尤其二进制或含特殊字符时),它只显示“sent n bytes”。要确认实际发了什么,最可靠方式是:在调用 curl_exec 前把准备好的数据存下来并格式化输出。
例如你传的是关联数组:$post_data = ['name' => '张三', 'file' => new CURLFile('/tmp/a.jpg')],cURL 会自动编码成 multipart/form-data,但你无法从日志里读出边界符和字段顺序。
立即学习“PHP免费学习笔记(深入)”;
- 对简单键值对,先用
http_build_query($post_data)输出再传入CURLOPT_POSTFIELDS - 对
CURLFile,改用字符串形式构造 multipart body(较重,仅调试必要时) - 避免依赖
curl_getinfo($ch, CURLINFO_REQUEST_SIZE)推断内容——它不含 header 长度,也不反映编码后真实字节数
用 curl_setopt_array 组合调试参数更稳妥
单个 curl_setopt 容易漏掉配套设置,导致调试信息不全。比如开了 CURLOPT_VERBOSE 却没关 CURLOPT_HEADER,响应体混着头信息一起输出,反而难定位。
推荐一次性配置好调试所需全部选项,尤其注意 CURLOPT_RETURNTRANSFER 必须为 true,否则 curl_exec 直接输出到页面,和调试日志搅在一起。
curl_setopt_array($ch, [
CURLOPT_URL => 'https://httpbin.org/post',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $data,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_VERBOSE => true,
CURLOPT_STDERR => fopen('php://temp', 'w+'),
]);执行后用 fseek 和 stream_get_contents 读取 STDERR 内容,再和你的 $data 对照。
替代方案:用内置 Web 服务接收并回显(适合复杂场景)
当 cURL 日志仍不足以还原问题(比如 SSL 握手失败、代理干扰、gzip 压缩异常),最直白的办法是起一个本地接收端,把请求原样打出来。
不用搭完整服务,一行命令即可:php -S localhost:8000 -t /dev/null 配合简单路由脚本,或直接用 nc -l 8000 抓原始 TCP 流。这时你把 POST 目标 URL 改成 http://localhost:8000,所有 header + body 就裸露在终端里。
- 此法能暴露 cURL 是否真的发出了 Expect: 100-continue、是否加了 User-Agent、是否用了 chunked transfer
- 注意关闭
CURLOPT_FOLLOWLOCATION,否则重定向会绕过你的监听端口 - 生产环境切勿用此法,仅限本机调试
调试真正卡住的地方,往往不在 POST 数据本身,而在 cURL 没告诉你它悄悄改了什么——比如自动添加 Host 头、强制升级 HTTP/2、或因证书问题根本没发出去。盯住 STDERR 输出里的第一行 “* Trying …” 和最后的 “











