使用CloudFlare搭建反向代理 [2024年12月03日] CloudFlare更新 服务条款 2.2.1 禁止包括但不限于导致(直接或间接)您的 Cloudflare 代理域的流量发送到非 Cloudflare 为该域分配的 IP 地址 禁止使用服务提供虚拟专用网络或其他类似的代理服务
若任使用请知晓可能出现的风险, 包括但不限于: 暂停或终止您对cloudflare服务的使用或访问 等等。
FREE套餐CloudFlare每日提供 10w 请求的免费额度,个人用完全足够,但并不能保证很多人同时使用,强烈建议仅自用
准备
一个CloudFlare 账号,如果没有就去CloudFlare 注册 这不废话
一个可以添加到CloudFlare的域名(可选),主要是CloudFlare的一些域名被墙,导致无法直接访问
创建并配置Workers 创建 Workers 在 Cloudflare 的面板左侧点击 Workers 和 Pages 进入后点击创建 按钮
点击创建Workers 按钮
名字随便填,我这里就直接填proxy了,然后点击部署
点击编辑代码 进入代码编辑页面
在这里你有两个选择
通用反代(优点:可直接反代任意网站,缺点:容易寄,如果泄露被滥用的几率大)
指定反代(优点:稳定,不容易寄,缺点:需要一条条的配置需要反代的地址)
个人推荐使用指定反代 ,按需选择
指定反代 将以下代码覆盖掉左侧代码框的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 addEventListener ('fetch' , event => { event.respondWith (handleRequest (event.request )); }); const domainMappings = { "/steam-store" : "https://store.steampowered.com" , "/steam-api" : "https://api.steampowered.com" }; const specialCases = { "*" : { "Origin" : "DELETE" , "Referer" : "DELETE" } }; function handleSpecialCases (request ) { const rules = specialCases["*" ]; for (const [key, value] of Object .entries (rules)) { switch (value) { case "KEEP" : break ; case "DELETE" : request.headers .delete (key); break ; default : request.headers .set (key, value); break ; } } } async function handleRequest (request ) { const url = new URL (request.url ); if (url.pathname === "/" ) { return new Response (` <html> <head> <style> body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f9f9f9; } h1 { font-family: Arial, sans-serif; color: #333; text-align: center; } </style> </head> <body> <h1>Why don't you play Genshin Impact?</h1> </body> </html> ` , { headers : { "Content-Type" : "text/html" } }); } const basePath = Object .keys (domainMappings).find (path => url.pathname .startsWith (path)); if (!basePath) { return new Response ("Path not found in domain mappings" , { status : 404 }); } const targetBase = domainMappings[basePath]; const targetPath = url.pathname .slice (basePath.length ) + url.search + url.hash ; const targetUrl = new URL (targetPath, targetBase); const modifiedRequest = new Request (targetUrl, { headers : request.headers , method : request.method , body : request.body , redirect : 'follow' }); handleSpecialCases (modifiedRequest); const response = await fetch (modifiedRequest); const modifiedResponse = new Response (response.body , response); modifiedResponse.headers .set ('Access-Control-Allow-Origin' , '*' ); return modifiedResponse; }
可修改 域名映射表(domainMappings) 中的内容为自己想要的网站
1 2 3 4 5 const domainMappings = { "/steam-store" : "https://store.steampowered.com" , "/steam-api" : "https://api.steampowered.com" }
其中,前面的部分是路由,就是 https://你的域名/路由 也就如果
访问 https://你的域名/steam-store 反代的就是 https://store.steampowered.com 访问 https://你的域名/steam-api 反代的就是 https://api.steampowered.com
其他同理,你可以添加也可以减少
通用反代 将以下代码覆盖掉左侧代码框的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 addEventListener ('fetch' , event => { event.respondWith (handleRequest (event.request )); }); const specialCases = { "*" : { "Origin" : "DELETE" , "Referer" : "DELETE" } }; function handleSpecialCases (request ) { const rules = specialCases["*" ]; for (const [key, value] of Object .entries (rules)) { switch (value) { case "KEEP" : break ; case "DELETE" : request.headers .delete (key); break ; default : request.headers .set (key, value); break ; } } } async function handleRequest (request ) { const url = new URL (request.url ); if (url.pathname === "/" ) { return new Response (` <html> <head> <style> body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f9f9f9; } h1 { font-family: Arial, sans-serif; color: #333; text-align: center; } </style> </head> <body> <h1>Why don't you play Genshin Impact?</h1> </body> </html> ` , { headers : { "Content-Type" : "text/html" } }); } if (url.pathname .startsWith ("/" )) { let targetUrl = url.pathname .slice (1 ); if (!/^https?:\/\// .test (targetUrl)) { targetUrl = "https://" + targetUrl; } const modifiedRequest = new Request (targetUrl, { headers : request.headers , method : request.method , body : request.body , redirect : 'follow' }); handleSpecialCases (modifiedRequest); const response = await fetch (modifiedRequest); const modifiedResponse = new Response (response.body , response); modifiedResponse.headers .set ('Access-Control-Allow-Origin' , '*' ); return modifiedResponse; } return new Response ("Path not found" , { status : 404 }); }
直接在 https://域名 后面加反代的地址就行,也就是 https://域名/需要反代的网站 比如
访问 https://你的域名/https://store.steampowered.com 反代的就是 https://store.steampowered.com 访问 https://你的域名/https://api.steampowered.com 反代的就是 https://api.steampowered.com
部署 点击部署
在下方出现以下提示就可以啦
访问 https://你的域名 出现下面界面就成功啦
当然,有概率你是会出现网页无法访问 ,这就是CloudFlare的部分域名被墙了……
这就是需要下一步的原因
绑定域名 注册域名过程略
我们首先要将自己的域名 DNS 托管给 Cloudflare,返回在控制台选择“网站”选项卡,输入你注册的域名,点击继续
个人用选择FREE套餐即可,点击继续
点击继续前往激活
点击确认
将Cloudflare 给的名称服务器填在你注册域名的地方,这里不细说
填完后点击继续 等待名称服务器更新(等待时间在几分钟到几个小时之间,不要急,成功后Cloudflare会发送邮件到你的邮箱)
刷新后域名的状态变为活动 就可以了
使用域名 继续前往 Workers 和 Pages ,点击进入刚才创建的 Workers 服务
进入设置 ,在 域和路由 点击 添加
点击自定义域 输入任意的域名前缀+你的域名.例如proxy.example.com , 将example.com 替换为你的域名 点击添加域 即可
然后将 Cloudflare 的域名替换为自己的即可
拓展 即使指定反代依旧有泄露被滥用的风险,那就浅浅加个用户白名单吧(doge)
加了白名单的话通用反代方便一点
IP白名单 将下面的IP白名单 换成你自己的服务器IP即可,极大的保证了不被滥用以及稳定不被墙
1 2 3 4 5 6 7 const allowedIPs = [ '123.123.123.123' , '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ];
通用反代 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 addEventListener ('fetch' , event => { event.respondWith (handleRequest (event.request )); }); const specialCases = { "*" : { "Origin" : "DELETE" , "Referer" : "DELETE" } }; const allowedIPs = [ '123.123.123.123' , '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ]; function handleSpecialCases (request ) { const rules = specialCases["*" ]; for (const [key, value] of Object .entries (rules)) { switch (value) { case "KEEP" : break ; case "DELETE" : request.headers .delete (key); break ; default : request.headers .set (key, value); break ; } } } async function handleRequest (request ) { const url = new URL (request.url ); const userIP = request.headers .get ('CF-Connecting-IP' ) || request.headers .get ('X-Forwarded-For' ) || request.connection .remoteAddress ; if (!allowedIPs.includes (userIP)) { return new Response (` <html> <head> <style> body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f9f9f9; } h1 { font-family: Arial, sans-serif; color: #333; text-align: center; } </style> </head> <body> <h1>Caused by playing Genshin Impact</h1> </body> </html> ` , { headers : { "Content-Type" : "text/html" } }); } if (url.pathname === "/" ) { return new Response (` <html> <head> <style> body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f9f9f9; } h1 { font-family: Arial, sans-serif; color: #333; text-align: center; } </style> </head> <body> <h1>Why don't you play Genshin Impact?</h1> </body> </html> ` , { headers : { "Content-Type" : "text/html" } }); } if (url.pathname .startsWith ("/" )) { let targetUrl = url.pathname .slice (1 ); if (!/^https?:\/\// .test (targetUrl)) { targetUrl = "https://" + targetUrl; } const modifiedRequest = new Request (targetUrl, { headers : request.headers , method : request.method , body : request.body , redirect : 'follow' }); handleSpecialCases (modifiedRequest); const response = await fetch (modifiedRequest); const modifiedResponse = new Response (response.body , response); modifiedResponse.headers .set ('Access-Control-Allow-Origin' , '*' ); return modifiedResponse; } return new Response ("Path not found" , { status : 404 }); }
指定反代 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 addEventListener ('fetch' , event => { event.respondWith (handleRequest (event.request )); }); const domainMappings = { "/steam-store" : "https://store.steampowered.com" , "/steam-api" : "https://api.steampowered.com" }; const specialCases = { "*" : { "Origin" : "DELETE" , "Referer" : "DELETE" } }; const allowedIPs = [ '123.123.123.123' , '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ]; function handleSpecialCases (request ) { const rules = specialCases["*" ]; for (const [key, value] of Object .entries (rules)) { switch (value) { case "KEEP" : break ; case "DELETE" : request.headers .delete (key); break ; default : request.headers .set (key, value); break ; } } } function getUserIP (request ) { return request.headers .get ('CF-Connecting-IP' ) || request.headers .get ('X-Forwarded-For' ) || request.connection .remoteAddress ; } async function handleRequest (request ) { const url = new URL (request.url ); const userIP = getUserIP (request); if (!allowedIPs.includes (userIP)) { return new Response (` <html> <head> <style> body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f9f9f9; } h1 { font-family: Arial, sans-serif; color: #333; text-align: center; } </style> </head> <body> <h1>Caused by playing Genshin Impact</h1> </body> </html> ` , { headers : { "Content-Type" : "text/html" } }); } if (url.pathname === "/" ) { return new Response (` <html> <head> <style> body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f9f9f9; } h1 { font-family: Arial, sans-serif; color: #333; text-align: center; } </style> </head> <body> <h1>Why don't you play Genshin Impact?</h1> </body> </html> ` , { headers : { "Content-Type" : "text/html" } }); } const basePath = Object .keys (domainMappings).find (path => url.pathname .startsWith (path)); if (!basePath) { return new Response ("Path not found in domain mappings" , { status : 404 }); } const targetBase = domainMappings[basePath]; const targetPath = url.pathname .slice (basePath.length ) + url.search + url.hash ; const targetUrl = new URL (targetPath, targetBase); const modifiedRequest = new Request (targetUrl, { headers : request.headers , method : request.method , body : request.body , redirect : 'follow' }); handleSpecialCases (modifiedRequest); const response = await fetch (modifiedRequest); const modifiedResponse = new Response (response.body , response); modifiedResponse.headers .set ('Access-Control-Allow-Origin' , '*' ); return modifiedResponse; }