通用反代的代码 使用方法 访问时,只需在对应入口后拼接目标地址即可使用。 方式一:拼接完整 URL 示例: https://yx.emby-ct.ccwu.cc:443/https://emby.com 方式二:直接拼接域名 示例: https://yx.emby-ct.ccwu.cc:443/emby.com /** * ============================================================ * Emby / Jellyfin Worker v4.5 Final * * 功能: * 1. 动态反向代理 * 2. Emby / Jellyfin 播放优化 * 3. 视频流不缓存 * 4. 静态资源缓存 * 5. Range断点播放 * 6. WebSocket支持 * 7. 防止源站跳转泄露 * * 支持: * /https://example.com * /example.com * /example.com/Users/xxx * * ============================================================ */ addEventListener( "fetch", e=>e.respondWith(proxy(e.request,e)) ); async function proxy(req,event){ // ============================= // 基础信息 // ============================= const url=new URL(req.url); const country= req.cf?.country|| req.headers.get("cf-ipcountry"); // ============================= // 中国大陆限制 // ============================= if( country && country!=="CN" ){ return tip( "🌏 访问限制", "当前服务仅允许中国大陆访问" ); } // ============================= // 解析目标服务器 // ============================= const target= parseTarget( url.pathname.slice(1) ); if(!target){ return tip( "⚠️ 地址错误", "请添加Emby服务器地址\n\n示例:\n/https://emby.example.com" ); } const dest= new URL( target.base+ target.path+ url.search ); // ============================= // 请求头处理 // ============================= const headers= new Headers(req.headers); // 必须修改Host // 否则部分Nginx/Caddy/宝塔会拒绝 headers.set( "Host", dest.host ); // 删除可能冲突的CF头 headers.delete( "CF-Ray" ); headers.delete( "CF-IPCountry" ); headers.delete( "CF-Visitor" ); // 保留真实客户端IP const ip= req.headers.get( "CF-Connecting-IP" ); if(ip){ headers.set( "CF-Connecting-IP", ip ); headers.set( "X-Forwarded-For", ip ); headers.set( "X-Real-IP", ip ); } headers.set( "X-Forwarded-Proto", "https" ); // ============================= // Range播放优化 // ============================= if( req.headers.has("Range") ){ headers.set( "Accept-Encoding", "identity" ); headers.delete( "If-Range" ); } try{ // ============================= // WebSocket支持 // ============================= if( req.headers .get("upgrade") ?.toLowerCase() ==="websocket" ){ return fetch( new Request( dest, { method:req.method, headers, body:req.body, redirect:"manual" } ) ); } // ============================= // 请求源站 // ============================= const res= await fetch( new Request( dest, { method:req.method, headers, body: req.method==="GET"|| req.method==="HEAD" ? undefined : req.body, redirect:"manual" } ) ); // ============================= // 重定向处理 // // 防止源站域名暴露 // // 例如: // Location: // https://emby.com/web // // 改成: // /https%3A%2F%2Femby.com%2Fweb // ============================= if( [ 301, 302, 303, 307, 308 ] .includes(res.status) ){ const h= new Headers(res.headers); const loc= h.get("location"); if( loc && /^https?:\/\//i.test(loc) ){ h.set( "location", "/"+ encodeURIComponent(loc) ); } return new Response( null, { status:res.status, headers:h } ); } const type= res.headers.get( "content-type" )||""; // ============================= // 视频流处理 // // 视频: // mp4 // mkv // m3u8 // ts // mpd // // 永不缓存 // ============================= if( isVideo(target.path)|| type.startsWith("video/") ){ const h= new Headers(res.headers); h.set( "Cache-Control", "no-store,no-cache,must-revalidate" ); h.set( "Accept-Ranges", "bytes" ); return new Response( res.body, { status:res.status, headers:h } ); } // ============================= // 静态资源缓存 // // 图片: // jpg/png/webp // // 前端: // js/css // // 字体: // woff // ============================= if( canCache( target.path, type ) ){ const cache= caches.default; const key= new Request( url.origin+ target.base+ target.path ); const hit= await cache.match(key); if(hit) return hit; const h= new Headers(res.headers); h.set( "Cache-Control", "public,max-age=86400" ); const result= new Response( res.body, { status:res.status, headers:h } ); event.waitUntil( cache.put( key, result.clone() ) ); return result; } // ============================= // API默认不缓存 // ============================= const h= new Headers(res.headers); h.set( "Cache-Control", "no-store" ); return new Response( res.body, { status:res.status, headers:h } ); }catch(e){ return tip( "❌ Emby连接失败", `无法连接源站: ${dest.hostname} 可能原因: 1. 源站离线 2. DNS异常 3. 防火墙限制` ); } } // ============================================================ // 地址解析 // ============================================================ function parseTarget(str){ if(!str) return null; try{ str= decodeURIComponent(str); }catch(e){} if( str.startsWith("http://")|| str.startsWith("https://") ){ const m= str.match( /^(https?:\/\/[^\/]+)(\/.*)?$/ ); return m? { base:m[1], path:m[2]||"" } : null; } const m= str.match( /^([^\/]+)(\/.*)?$/ ); return m? { base:normalize(m[1]), path:m[2]||"" } : null; } // ============================================================ // 自动判断协议 // ============================================================ function normalize(host){ const port= host.match(/:(\d+)$/) ?.[1]; if( port==="443"|| port==="8920"|| port==="8443" ){ return "https://"+host; } if(port){ return "http://"+host; } return "https://"+host; } // ============================================================ // 获取文件后缀 // ============================================================ function ext(path){ return path .split("?")[0] .split(".") .pop() .toLowerCase(); } // ============================================================ // 视频类型判断 // ============================================================ function isVideo(path){ return [ "mp4", "mkv", "avi", "mov", "m4v", "ts", "m3u8", "mpd", "webm" ] .includes( ext(path) ); } // ============================================================ // 静态缓存判断 // ============================================================ function canCache(path,type){ const e= ext(path); return [ "jpg", "jpeg", "png", "gif", "webp", "svg", "ico", "woff", "woff2", "ttf", "css", "js" ] .includes(e) || type.startsWith("image/") || type.includes("javascript") || type.includes("css"); } // ============================================================ // 错误页面 // ============================================================ function tip(title,msg){ return new Response( `${title} ${msg} ---------------- Emby Worker v4.5 `, { status:400, headers:{ "Content-Type": "text/plain;charset=utf-8", "Cache-Control": "no-store" } } ); }
@亦心 #0 谢谢分享,加鸡腿
通用反代的代码
使用方法
访问时,只需在对应入口后拼接目标地址即可使用。
方式一:拼接完整 URL
示例:
https://yx.emby-ct.ccwu.cc:443/https://emby.com
方式二:直接拼接域名
示例:
https://yx.emby-ct.ccwu.cc:443/emby.com
@亦心 #42
403错误
@yl0919 #44 把域名设置直连,不要走代理
@亦心 #45 佬,我改了还是403