php实现http代理
实现HTTP代理的基本原理
HTTP代理服务器充当客户端和目标服务器之间的中间人,接收客户端请求并转发给目标服务器,再将响应返回给客户端。PHP可通过Socket或cURL扩展实现这一功能。
使用cURL扩展实现简单代理
<?php
$targetUrl = $_GET['url'];
if (empty($targetUrl)) {
die('Missing URL parameter');
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $targetUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HEADER, true);
$response = curl_exec($ch);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);
foreach (explode("\r\n", $headers) as $header) {
if (!empty($header)) {
header($header);
}
}
echo $body;
curl_close($ch);
?>
使用Socket实现更完整的代理
<?php
$proxyPort = 8080;
$proxyHost = '0.0.0.0';
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, $proxyHost, $proxyPort);
socket_listen($socket);
while (true) {
$client = socket_accept($socket);
$request = socket_read($client, 1024);
preg_match('/Host: (.+)/', $request, $matches);
$host = trim($matches[1]);
$targetSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($targetSocket, $host, 80);
socket_write($targetSocket, $request);
$response = '';
while ($buffer = socket_read($targetSocket, 1024)) {
$response .= $buffer;
}
socket_write($client, $response);
socket_close($targetSocket);
socket_close($client);
}
?>
处理HTTPS请求的注意事项
对于HTTPS请求,需要处理CONNECT方法并建立隧道:
if (strpos($request, 'CONNECT') === 0) {
$parts = explode(' ', $request);
$target = trim($parts[1]);
list($host, $port) = explode(':', $target);
$targetSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($targetSocket, $host, $port);
socket_write($client, "HTTP/1.1 200 Connection Established\r\n\r\n");
// 双向转发数据
while (true) {
$data = socket_read($client, 4096);
if (!$data) break;
socket_write($targetSocket, $data);
$data = socket_read($targetSocket, 4096);
if (!$data) break;
socket_write($client, $data);
}
}
性能优化建议
使用stream_socket_server替代原生socket函数可获得更好性能:
$server = stream_socket_server("tcp://$proxyHost:$proxyPort");
while ($client = stream_socket_accept($server)) {
$request = fread($client, 8192);
// 处理请求逻辑
}
实现连接池管理重复使用的目标服务器连接,减少频繁建立连接的开销。考虑加入缓存机制对频繁请求的响应进行缓存。
安全注意事项
验证目标URL防止开放代理被滥用:
$allowedDomains = ['example.com', 'api.example.org'];
$parsed = parse_url($targetUrl);
if (!in_array($parsed['host'], $allowedDomains)) {
http_response_code(403);
die('Access to this domain is not allowed');
}
实现身份验证机制:
if (!isset($_SERVER['PHP_AUTH_USER']) ||
$_SERVER['PHP_AUTH_USER'] != 'proxyuser' ||
$_SERVER['PHP_AUTH_PW'] != 'proxypass') {
header('WWW-Authenticate: Basic realm="Proxy Auth"');
http_response_code(401);
die('Authentication required');
}
记录日志监控代理使用情况:
$log = sprintf("[%s] %s %s\n",
date('Y-m-d H:i:s'),
$_SERVER['REMOTE_ADDR'],
$targetUrl);
file_put_contents('proxy.log', $log, FILE_APPEND);






