proxy / index.php
Openyx's picture
Update index.php
96401e0 verified
<?php
// By: Colime – PHP reverse proxy, tuned for Google
// Root page
if ($_SERVER['REQUEST_URI'] === '/' || $_SERVER['REQUEST_URI'] === '') {
header('Content-Type: text/html; charset=utf-8');
echo '<p>about:blank</p>';
exit;
}
// CORS preflight
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS');
header('Access-Control-Allow-Headers: *');
header('Access-Control-Max-Age: 86400');
exit;
}
// Build target URL from path + query string
$target = substr($_SERVER['REQUEST_URI'], 1); // strip leading /
$query = $_SERVER['QUERY_STRING'];
if ($query !== '') {
$target .= '?' . $query;
}
if (!preg_match('#^https?://#i', $target)) {
$target = 'http://' . $target;
}
$parts = parse_url($target);
if (!$parts || !isset($parts['host'])) {
http_response_code(400);
echo 'Invalid target URL provided.';
exit;
}
// Collect and clean request headers
$requestHeaders = getallheaders();
$forwardHeaders = [];
// Hop-by-hop headers that should NOT be forwarded (both request and response)
$hopByHop = [
'connection', 'keep-alive', 'proxy-authenticate',
'proxy-authorization', 'te', 'trailer', 'transfer-encoding',
'upgrade'
];
foreach ($requestHeaders as $name => $value) {
$lower = strtolower($name);
// Drop Cloudflare headers, real IP, and original Host
if (strpos($lower, 'cf-') === 0 || $lower === 'x-real-ip' || $lower === 'host') {
continue;
}
// Don't forward hop-by-hop request headers
if (in_array($lower, $hopByHop)) {
continue;
}
// Accept-Encoding is handled by cURL automatically; do not forward it
if ($lower === 'accept-encoding') {
continue;
}
$forwardHeaders[] = "$name: $value";
}
// Set the target host
$forwardHeaders[] = 'Host: ' . $parts['host'];
// Fallback User-Agent (only if client didn't send one)
$hasUserAgent = false;
foreach ($forwardHeaders as $h) {
if (stripos($h, 'User-Agent:') === 0) {
$hasUserAgent = true;
break;
}
}
if (!$hasUserAgent) {
$forwardHeaders[] = 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36';
}
// cURL request
$ch = curl_init($target);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $_SERVER['REQUEST_METHOD'],
CURLOPT_HTTPHEADER => $forwardHeaders,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HEADER => true, // include headers in output
CURLOPT_TIMEOUT => 30,
CURLOPT_ENCODING => '', // accept any compression, decompress automatically
CURLOPT_SSL_VERIFYPEER => true, // keep enabled (HF has CA certs)
]);
// Forward request body (if any)
$body = file_get_contents('php://input');
if ($body !== '' && $body !== false) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
$response = curl_exec($ch);
$error = curl_error($ch);
$info = curl_getinfo($ch);
curl_close($ch);
if ($error) {
http_response_code(502);
echo 'Upstream server fetch failed. Please check the URL and network.';
exit;
}
// Split headers and body
$headerSize = $info['header_size'];
$responseHeaders = substr($response, 0, $headerSize);
$responseBody = substr($response, $headerSize);
// Parse incoming response headers
$headersArr = explode("\r\n", $responseHeaders);
$statusCode = 200;
$outHeaders = []; // name -> value pairs (duplicates allowed, but we'll filter some)
foreach ($headersArr as $line) {
// Status line
if (stripos($line, 'HTTP/') === 0) {
preg_match('/\d{3}/', $line, $m);
$statusCode = (int)($m[0] ?? 200);
continue;
}
if (trim($line) === '') {
continue;
}
$colon = strpos($line, ':');
if ($colon !== false) {
$name = substr($line, 0, $colon);
$value = trim(substr($line, $colon + 1));
$lower = strtolower($name);
// Remove security headers that block iframes
if ($lower === 'content-security-policy' || $lower === 'x-frame-options') {
continue;
}
// Remove hop-by-hop response headers
if (in_array($lower, $hopByHop)) {
continue;
}
// cURL already decompressed the body – remove Content-Encoding
if ($lower === 'content-encoding') {
continue;
}
$outHeaders[] = [$name, $value];
}
}
// --- Send final response ---
http_response_code($statusCode);
// Always add CORS and no-cache headers
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: *');
header('Access-Control-Allow-Headers: *');
header('Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
// Forward upstream headers (except those we filtered out)
foreach ($outHeaders as $h) {
header($h[0] . ': ' . $h[1]);
}
echo $responseBody;