You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP/Curl通过HTTP代理上传文件至FTP服务器被Zscaler拦截求助

通过HTTP代理(Zscaler环境)实现PHP FTP文件上传的解决方案

问题核心

你的PHP Curl代码未使用HTTP CONNECT隧道模式,导致FTP上传请求被Zscaler以Not allowed to use FTP over HTTP for non-GET/non-HEAD request拦截;而FileZilla正是通过CONNECT方法建立隧道直接传输FTP协议,才绕过了限制成功上传。此前开启CURLOPT_HTTPPROXYTUNNEL后超时,是因为FTP被动模式下的数据连接未正确通过代理隧道。

修正后的Curl实现代码

$filepath = 'path/to/your/local/file.txt';
$ftp_host = 'ftp-url';
$ftp_user = 'your-ftp-username';
$ftp_pass = 'your-ftp-password';
$proxy_url = 'proxy-url';
$proxy_port = 8080;

$fp = fopen($filepath, 'r');
if (!$fp) {
    die('无法读取本地文件');
}

$ftp_url = "ftp://{$ftp_user}:{$ftp_pass}@{$ftp_host}/test.txt";
$ch = curl_init();

// 基础FTP上传配置
curl_setopt($ch, CURLOPT_URL, $ftp_url);
curl_setopt($ch, CURLOPT_UPLOAD, true);
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($filepath));

// 关键:启用HTTP CONNECT隧道(对应FileZilla的代理设置)
curl_setopt($ch, CURLOPT_PROXY, $proxy_url);
curl_setopt($ch, CURLOPT_PROXYPORT, $proxy_port);
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, true);

// 配置FTP被动模式,解决隧道超时问题
curl_setopt($ch, CURLOPT_FTP_USE_EPSV, true); // 优先使用扩展被动模式
curl_setopt($ch, CURLOPT_FTPPORT, '-'); // 强制启用被动模式
curl_setopt($ch, CURLOPT_TIMEOUT, 30); // 延长超时时间适配隧道建立过程

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// 可选:开启调试模式排查问题
// curl_setopt($ch, CURLOPT_VERBOSE, true);
// $debug_log = fopen('php://temp', 'w+');
// curl_setopt($ch, CURLOPT_STDERR, $debug_log);

$result = curl_exec($ch);

if (curl_errno($ch)) {
    echo 'Curl错误: ' . curl_error($ch);
    // 输出调试日志
    // rewind($debug_log);
    // echo stream_get_contents($debug_log);
} else {
    echo '文件上传成功';
}

curl_close($ch);
fclose($fp);

关键修改说明

  • CURLOPT_HTTPPROXYTUNNEL=true:让Curl通过HTTP CONNECT方法建立到FTP服务器的专用隧道,所有FTP协议数据直接通过隧道传输,而非转换成HTTP请求,绕过Zscaler的FTP over HTTP限制。
  • 被动模式配置:确保FTP的数据连接也通过代理隧道建立,解决之前的超时问题。
  • 延长超时时间:隧道建立和FTP握手需要额外时间,避免因超时导致连接失败。

PHP原生FTP扩展的可行性

PHP原生ftp_*系列函数本身不直接支持HTTP代理,但可以通过手动建立HTTP CONNECT隧道的方式实现:

  1. 先建立代理隧道:
$proxy_socket = stream_socket_client("tcp://{$proxy_url}:{$proxy_port}", $errno, $errstr, 30);
if (!$proxy_socket) {
    die("代理连接失败: $errstr ($errno)");
}

// 发送CONNECT请求建立到FTP服务器的隧道
fwrite($proxy_socket, "CONNECT {$ftp_host}:21 HTTP/1.1\r\nHost: {$ftp_host}:21\r\n\r\n");
$response = fread($proxy_socket, 8192);

// 验证隧道是否成功建立
if (!strpos($response, '200 Connection Established')) {
    die("代理隧道建立失败: $response");
}
  1. 通过隧道socket使用FTP扩展:
// 用隧道socket初始化FTP连接(如需SSL则用ftp_ssl_connect)
$ftp_conn = ftp_connect('tcp://', 0, 30, FTP_AUTOSEEK, $proxy_socket);
if (!$ftp_conn) {
    die("FTP连接失败");
}

// 登录FTP服务器
if (!ftp_login($ftp_conn, $ftp_user, $ftp_pass)) {
    die("FTP登录失败");
}

// 启用被动模式
ftp_pasv($ftp_conn, true);

// 上传文件
if (ftp_put($ftp_conn, 'test.txt', $filepath, FTP_BINARY)) {
    echo '文件上传成功';
} else {
    echo '文件上传失败';
}

// 关闭连接
ftp_close($ftp_conn);
fclose($proxy_socket);

注意:该方式需要确保PHP的FTP扩展已启用,且代理服务器允许CONNECT到FTP的21端口及被动模式所需的高端口。

内容的提问来源于stack exchange,提问作者Xilukarim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 01:25:18