本文目录导读:

- 使用 cURL 实现简单反向代理
- 使用 Guzzle HTTP 客户端
- 使用 stream_context 简化版
- 高级配置 - 支持 WebSocket 的代理
- 使用 Nginx + PHP-FPM 配合
- 使用建议
在 PHP 中实现反向代理有多种方式,我来详细介绍几种常用方法:
使用 cURL 实现简单反向代理
<?php
class ReverseProxy {
private $targetHost;
private $headers;
public function __construct($targetHost) {
$this->targetHost = $targetHost;
$this->headers = $this->getRequestHeaders();
}
public function proxy() {
$url = $this->targetHost . $_SERVER['REQUEST_URI'];
$ch = curl_init();
// 设置请求方法
$method = $_SERVER['REQUEST_METHOD'];
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
// 设置请求头
$headers = [];
foreach ($this->headers as $key => $value) {
if (!in_array($key, ['Host', 'Content-Length'])) {
$headers[] = "$key: $value";
}
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
// 处理请求体
if (in_array($method, ['POST', 'PUT', 'PATCH'])) {
curl_setopt($ch, CURLOPT_POSTFIELDS, file_get_contents('php://input'));
}
// 设置返回为字符串
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_URL, $url);
// 执行请求
$response = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);
// 设置响应头
http_response_code($statusCode);
if ($contentType) {
header("Content-Type: $contentType");
}
// 输出响应体
echo $response;
}
private function getRequestHeaders() {
$headers = [];
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) == 'HTTP_') {
$headerName = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
$headers[$headerName] = $value;
}
}
return $headers;
}
}
// 使用示例
$proxy = new ReverseProxy('https://api.example.com');
$proxy->proxy();
?>
使用 Guzzle HTTP 客户端
<?php
require_once 'vendor/autoload.php';
use GuzzleHttp\Client;
class GuzzleProxy {
private $client;
private $targetBase;
public function __construct($targetBase) {
$this->targetBase = $targetBase;
$this->client = new Client([
'timeout' => 30.0,
'verify' => false, // 生产环境建议开启
]);
}
public function forward() {
$method = $_SERVER['REQUEST_METHOD'];
$path = $_SERVER['REQUEST_URI'];
$url = $this->targetBase . $path;
// 构建请求头
$headers = [];
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) == 'HTTP_') {
$headerName = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
$headers[$headerName] = $value;
}
}
// 移除可能引起问题的头
unset($headers['Host']);
unset($headers['Content-Length']);
try {
$options = [
'headers' => $headers,
'http_errors' => false,
];
// 处理请求体
if (in_array($method, ['POST', 'PUT', 'PATCH'])) {
$options['body'] = file_get_contents('php://input');
}
// 处理GET参数
if (!empty($_GET)) {
$url .= (strpos($url, '?') === false ? '?' : '&') . http_build_query($_GET);
}
// 发送请求
$response = $this->client->request($method, $url, $options);
// 设置响应头
foreach ($response->getHeaders() as $name => $values) {
foreach ($values as $value) {
header("$name: $value", false);
}
}
// 输出响应内容
echo $response->getBody()->getContents();
} catch (\Exception $e) {
http_response_code(502);
echo "Proxy Error: " . $e->getMessage();
}
}
}
// 使用示例
$proxy = new GuzzleProxy('https://api.target.com');
$proxy->forward();
?>
使用 stream_context 简化版
<?php
function simpleReverseProxy($targetUrl) {
// 获取请求路径
$requestPath = $_SERVER['REQUEST_URI'];
$fullUrl = $targetUrl . $requestPath;
// 创建上下文
$options = [
'http' => [
'method' => $_SERVER['REQUEST_METHOD'],
'header' => "",
'content' => file_get_contents('php://input'),
'ignore_errors' => true,
],
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
]
];
// 提取并设置请求头
$headers = [];
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) == 'HTTP_') {
$headerName = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
if (strtolower($headerName) != 'host') {
$headers[] = "$headerName: $value";
}
}
}
if (!empty($headers)) {
$options['http']['header'] = implode("\r\n", $headers);
}
// 创建流上下文
$context = stream_context_create($options);
// 执行请求
$response = @file_get_contents($fullUrl, false, $context);
// 获取响应头
if (isset($http_response_header)) {
foreach ($http_response_header as $header) {
if (strpos($header, 'HTTP/') === 0) {
$statusCode = explode(' ', $header)[1];
http_response_code($statusCode);
} else {
header($header, false);
}
}
}
// 输出响应
echo $response;
}
// 使用示例
simpleReverseProxy('https://api.example.com');
?>
高级配置 - 支持 WebSocket 的代理
<?php
class AdvancedProxy {
private $config;
public function __construct($config) {
$this->config = array_merge([
'target' => '',
'timeout' => 30,
'max_redirects' => 5,
'allowed_methods' => ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
'exclude_paths' => [],
'custom_headers' => [],
'ssl_verify' => false,
], $config);
}
public function handle() {
// 检查路径是否在排除列表中
$currentPath = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if (in_array($currentPath, $this->config['exclude_paths'])) {
return false;
}
// 处理 WebSocket 升级
if (isset($_SERVER['HTTP_UPGRADE']) && strtolower($_SERVER['HTTP_UPGRADE']) == 'websocket') {
return $this->handleWebSocket();
}
// 常规HTTP请求
return $this->handleHttp();
}
private function handleHttp() {
$method = $_SERVER['REQUEST_METHOD'];
// 检查请求方法是否允许
if (!in_array($method, $this->config['allowed_methods'])) {
http_response_code(405);
echo "Method Not Allowed";
return;
}
$cookies = $this->extractCookies();
$targeting = $this->buildTargetUrl();
$ch = curl_init();
// 基础配置
$curlOptions = [
CURLOPT_URL => $targeting,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => $this->config['max_redirects'],
CURLOPT_TIMEOUT => $this->config['timeout'],
CURLOPT_SSL_VERIFYPEER => $this->config['ssl_verify'],
CURLOPT_CUSTOMREQUEST => $method,
];
// 处理请求体
if (in_array($method, ['POST', 'PUT', 'PATCH'])) {
$curlOptions[CURLOPT_POSTFIELDS] = file_get_contents('php://input');
}
// 构建请求头
$headers = $this->buildHeaders();
if (!empty($headers)) {
$curlOptions[CURLOPT_HTTPHEADER] = $headers;
}
// 设置cookies
if (!empty($cookies)) {
$curlOptions[CURLOPT_COOKIE] = $cookies;
}
curl_setopt_array($ch, $curlOptions);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
http_response_code(502);
echo "Proxy Error: " . $error;
return;
}
http_response_code($status);
echo $response;
}
private function handleWebSocket() {
// WebSocket 代理实现需要更复杂的处理
// 这里提供基础的框架
$target = $this->buildTargetUrl();
$targetParts = parse_url($target);
$host = $targetParts['host'];
$port = isset($targetParts['port']) ? $targetParts['port'] : 80;
$path = isset($targetParts['path']) ? $targetParts['path'] : '/';
// 建立到目标的 socket 连接
$remote = fsockopen($host, $port, $errno, $errstr, 30);
if (!$remote) {
http_response_code(502);
echo "WebSocket connection failed";
return;
}
// 转发请求头
$request = "GET {$path} HTTP/1.1\r\n";
$request .= "Host: {$host}:{$port}\r\n";
$request .= "Upgrade: websocket\r\n";
$request .= "Connection: Upgrade\r\n";
$request .= "Sec-WebSocket-Key: " . $_SERVER['HTTP_SEC_WEBSOCKET_KEY'] . "\r\n";
$request .= "Sec-WebSocket-Version: " . $_SERVER['HTTP_SEC_WEBSOCKET_VERSION'] . "\r\n";
$request .= "\r\n";
fwrite($remote, $request);
// 转发响应
while (!feof($remote)) {
echo fread($remote, 8192);
flush();
}
fclose($remote);
}
private function buildTargetUrl() {
$scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
$host = $this->config['target'];
$uri = $_SERVER['REQUEST_URI'];
return rtrim($host, '/') . $uri;
}
private function extractCookies() {
$cookies = [];
foreach ($_COOKIE as $name => $value) {
$cookies[] = "$name=" . urlencode($value);
}
return implode('; ', $cookies);
}
private function buildHeaders() {
$headers = [];
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) == 'HTTP_') {
$headerName = str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))));
// 跳过特定的头
if (in_array(strtolower($headerName), ['host', 'content-length', 'connection', 'accept-encoding'])) {
continue;
}
$headers[] = "$headerName: $value";
}
}
// 添加自定义头部
foreach ($this->config['custom_headers'] as $name => $value) {
$headers[] = "$name: $value";
}
return $headers;
}
}
// 使用示例
$config = [
'target' => 'https://api.example.com',
'timeout' => 30,
'ssl_verify' => false,
'custom_headers' => [
'X-Proxy-Server' => 'PHP-Proxy',
'X-Client-IP' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
]
];
$proxy = new AdvancedProxy($config);
$proxy->handle();
?>
使用 Nginx + PHP-FPM 配合
虽然不是纯PHP方案,但这是生产环境中最常见的做法:
# nginx 配置示例
location /api/ {
proxy_pass http://backend-server:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
使用建议
- 性能考虑:如果代理流量较大,建议使用 nginx 或 HAProxy 等专门的代理服务器
- 安全考虑:确保验证所有输入,防止恶意请求
- 错误处理:添加适当的错误处理机制
- 日志记录:记录所有代理请求和响应
- 缓存策略:根据需求添加适当的缓存机制
选择哪种方式取决于你的具体需求、环境配置和流量规模,对于简单的API转发,cURL方案就足够了;对于高并发场景,推荐使用 nginx 配合 PHP-FPM。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播