true, CURLOPT_MAXREDIRS => 5, CURLOPT_CONNECTTIMEOUT => TIMEOUT, CURLOPT_TIMEOUT => TIMEOUT, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_ENCODING => "", // enable gzip/deflate and auto-decode ]); if ($ua !== '') curl_setopt($ch, CURLOPT_USERAGENT, $ua); $response = curl_exec($ch); if ($response === false) bad(502, 'cURL error: '.curl_error($ch)); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $ctype = curl_getinfo($ch, CURLINFO_CONTENT_TYPE) ?: ''; $resp_headers_raw = substr($response, 0, $header_size); $body = substr($response, $header_size); // Filter upstream headers $resp_headers = []; $content_length = null; $content_range = null; foreach (preg_split("~\r?\n~", $resp_headers_raw) as $line){ if (strpos($line, ':') !== false){ [$k,$v] = array_map('trim', explode(':', $line, 2)); $kl = strtolower($k); if (in_array($kl, ['connection','keep-alive','proxy-authenticate','proxy-authorization','te','trailer','upgrade'])) continue; if ($kl === 'transfer-encoding') continue; if ($kl === 'content-encoding') continue; // already decoded by CURLOPT_ENCODING if ($kl === 'content-length') { $content_length = $v; continue; } if ($kl === 'content-range') { $content_range = $v; continue; } $resp_headers[$k] = $v; } } header('Access-Control-Allow-Origin: *'); header('Accept-Ranges: bytes'); // HLS playlist rewrite: route segments/keys/MAP through this same file $is_m3u8 = preg_match('~(application/(vnd\.apple\.mpegurl|x-mpegURL))|\.m3u8(\b|\?)~i', ($ctype.' '.$url)); if ($is_m3u8){ header('Content-Type: application/vnd.apple.mpegurl; charset=utf-8'); $lines = preg_split("~\r?\n~", $body); $out = []; foreach ($lines as $ln){ if ($ln === '' || $ln[0] === '#'){ // Rewrite EXT-X-KEY URI if (sw($ln, '#EXT-X-KEY:')){ $ln = preg_replace_callback('~URI="([^"]+)"~', function($m) use($url,$ref,$org,$ua,$extra){ $abs = abs_url($url, $m[1]); $p = $_SERVER['PHP_SELF'].'?proxy=1&url='.rawurlencode($abs); if($ref!=='') $p.='&referer='.rawurlencode($ref); if($org!=='') $p.='&origin='.rawurlencode($org); if($ua!=='') $p.='&ua='.rawurlencode($ua); if($extra!=='') $p.='&x='.rawurlencode($extra); if(SECRET!=='') $p.='&sig='.hash_hmac('sha256', $abs.$ref.$org.$ua.$extra, SECRET); return 'URI="'.$p.'"'; }, $ln); } // Rewrite EXT-X-MAP (init segment) URI if (sw($ln, '#EXT-X-MAP:')){ $ln = preg_replace_callback('~URI="([^"]+)"~', function($m) use($url,$ref,$org,$ua,$extra){ $abs = abs_url($url, $m[1]); $p = $_SERVER['PHP_SELF'].'?proxy=1&url='.rawurlencode($abs); if($ref!=='') $p.='&referer='.rawurlencode($ref); if($org!=='') $p.='&origin='.rawurlencode($org); if($ua!=='') $p.='&ua='.rawurlencode($ua); if($extra!=='') $p.='&x='.rawurlencode($extra); if(SECRET!=='') $p.='&sig='.hash_hmac('sha256', $abs.$ref.$org.$ua.$extra, SECRET); return 'URI="'.$p.'"'; }, $ln); } $out[] = $ln; continue; } // Media/variant line (URI) $abs = abs_url($url, $ln); $prox = $_SERVER['PHP_SELF'].'?proxy=1&url='.rawurlencode($abs); if($ref!=='') $prox.='&referer='.rawurlencode($ref); if($org!=='') $prox.='&origin='.rawurlencode($org); if($ua!=='') $prox.='&ua='.rawurlencode($ua); if($extra!=='') $prox.='&x='.rawurlencode($extra); if(SECRET!=='') $prox.='&sig='.hash_hmac('sha256', $abs.$ref.$org.$ua.$extra, SECRET); $out[] = $prox; } echo implode("\n", $out); exit; } // Forward headers for non-m3u8 if (!headers_sent()){ if ($ctype) header('Content-Type: '.$ctype); if ($content_range) header('Content-Range: '.$content_range); if ($content_length) header('Content-Length: '.$content_length); foreach ($resp_headers as $k=>$v){ header($k.': '.$v); } } http_response_code($code); echo $body; exit; } // ==================================================================== // UI MODE (no ?proxy): serve the player page // ==================================================================== ?>
Note: Browsers can’t set Referer, Origin, or User-Agent via JS. This page injects them server-side through itself.