├── LICENSE ├── README.md ├── _worker.js └── img.png /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2025, CMLiussss 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 📶 CF-Workers-DoH 2 |  3 | 4 | CF-Workers-DoH 是一个基于 Cloudflare Workers 构建的 DNS over HTTPS (DoH) 解析服务。它允许你通过 HTTPS 协议进行 DNS 查询,提高查询的安全性和隐私保护。 5 | 6 | > [!CAUTION] 7 | > **doh.cmliussss.hidns.co 已被GFW阻断,需自行部署使用。** 8 | 9 | > [!WARNING] 10 | > 如需搭建非公益服务,请务必添加`TOKEN`与`URL`变量,`URL`变量推荐为 **nginx** 即可! 11 | 12 | ## 🚀 部署方式 13 | 14 | - **Workers** 部署:复制 [_worker.js](https://github.com/cmliu/CF-Workers-DoH/blob/main/_worker.js) 代码,`保存并部署`即可 15 | - **Pages** 部署:`Fork` 后 `连接GitHub` 一键部署即可 16 | 17 | ## 📖 使用方法 18 | 19 | 假设你已部署成功,你的服务域名为:`doh.cmliussss.hidns.co` 20 | 21 | ### 1️⃣ DNS解析服务 (DoH) 22 | 23 | 将以下地址添加到支持DoH的设备或软件中: 24 | 25 | ```url 26 | https://doh.cmliussss.hidns.co/dns-query 27 | ``` 28 | 29 | - 还可使用 Cloudflare 回源端口 `2053`、`2083`、`2087`、`2096`、`8443`,例如 30 | ```url 31 | https://doh.cmliussss.hidns.co:2053/dns-query 32 | ``` 33 | 34 | - 如您设置了`TOKEN`变量为 **CMLiussss**,则 35 | ```url 36 | https://doh.cmliussss.hidns.co/CMLiussss 37 | ``` 38 | ### 2️⃣ 附加功能 IP信息查询 39 | 40 | #### 🔍 查询当前IP信息 41 | ```url 42 | https://doh.cmliussss.hidns.co/ip-info 43 | ``` 44 | 45 | - 如您设置了`TOKEN`变量为 **CMLiussss**,则 46 | ```url 47 | https://doh.cmliussss.hidns.co/ip-info?token=CMLiussss 48 | ``` 49 | 50 | #### 🔍 查询指定IP信息 51 | ```url 52 | https://doh.cmliussss.hidns.co/ip-info?ip=8.8.8.8 53 | ``` 54 | 55 | - 如您设置了`TOKEN`变量为 **CMLiussss**,则 56 | 57 | ```url 58 | https://doh.cmliussss.hidns.co/ip-info?ip=8.8.8.8&token=CMLiussss 59 | ``` 60 | 61 | #### 📝 **返回信息示例** 62 | ```json 63 | { 64 | "status": "success", 65 | "country": "美国", 66 | "countryCode": "US", 67 | "region": "VA", 68 | "regionName": "弗吉尼亚州", 69 | "city": "Ashburn", 70 | "zip": "20149", 71 | "lat": 39.03, 72 | "lon": -77.5, 73 | "timezone": "America/New_York", 74 | "isp": "Google LLC", 75 | "org": "Google Public DNS", 76 | "as": "AS15169 Google LLC", 77 | "query": "8.8.8.8" 78 | } 79 | ``` 80 | 81 | > [!NOTE] 82 | > 请将示例中的 `doh.cmliussss.hidns.co` 替换为你实际部署的域名 83 | 84 | ## 🔧 变量说明 85 | 86 | | 变量名 | 示例 | 必填 | 备注 | 87 | |--|--|--|--| 88 | | DOH | `dns.google` |❌| 设置上游DoH服务(默认:`cloudflare-dns.com`) | 89 | | TOKEN | `dns-query` |❌| 设置请求DoH服务路径(默认:`/dns-query`) | 90 | | URL | `https://www.baidu.com/` |❌| 主页伪装(设为`nginx`则伪装为nginx默认页面) | 91 | | URL302 | `https://t.me/CMLiussss` |❌| 主页302跳转(与`URL`变量同时存在时优先执行`URL302`)| 92 | 93 | > [!TIP] 94 | > 1. 使用 `dns.google` 或 `cloudflare-dns.com` 作为DoH上游时,**解析速度最佳**! 95 | > 2. 使用 `security.cloudflare-dns.com` 作为DoH上游时,可**阻止恶意软件**的DNS解析服务; 96 | > 3. 使用 `family.cloudflare-dns.com` 作为DoH上游时,可**阻止恶意软件**和**成人内容**的DNS解析服务; 97 | > 4. 已知 `doh.pub` **自带污染**,不适合作为DoH上游; 98 | > 5. 目前 `dns.alidns.com` 和 `doh.360.cn` 在**非中国大陆环境**请求DoH时,会下发干净DNS解析服务,也就是**可以作为CF-DoH的上游**,但是**解析速度不佳**。 99 | 100 | ## ⭐ Star 星星走起 101 | [](https://starchart.cc/cmliu/CF-Workers-DoH) 102 | 103 | ## 💡 技术特性 104 | - 基于 Cloudflare Workers 无服务器架构 105 | - 使用原生 JavaScript 实现 106 | 107 | ## 📝 许可证 108 | 本项目开源使用,欢迎自由部署和修改! 109 | 110 | ## 🙏 鸣谢 111 | [tina-hello](https://github.com/tina-hello/doh-cf-workers)、[ip-api](https://ip-api.com/)、Cloudflare、GPT -------------------------------------------------------------------------------- /_worker.js: -------------------------------------------------------------------------------- 1 | let DoH = "cloudflare-dns.com"; 2 | const jsonDoH = `https://${DoH}/resolve`; 3 | const dnsDoH = `https://${DoH}/dns-query`; 4 | let DoH路径 = 'dns-query'; 5 | export default { 6 | async fetch(request, env) { 7 | if (env.DOH) { 8 | DoH = env.DOH; 9 | const match = DoH.match(/:\/\/([^\/]+)/); 10 | if (match) { 11 | DoH = match[1]; 12 | } 13 | } 14 | DoH路径 = env.PATH || env.TOKEN || DoH路径;//DoH路径也单独设置 变量PATH 15 | if (DoH路径.includes("/")) DoH路径 = DoH路径.split("/")[1]; 16 | const url = new URL(request.url); 17 | const path = url.pathname; 18 | const hostname = url.hostname; 19 | 20 | // 处理 OPTIONS 预检请求 21 | if (request.method === 'OPTIONS') { 22 | return new Response(null, { 23 | headers: { 24 | 'Access-Control-Allow-Origin': '*', 25 | 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 26 | 'Access-Control-Allow-Headers': '*', 27 | 'Access-Control-Max-Age': '86400' 28 | } 29 | }); 30 | } 31 | 32 | // 如果请求路径,则作为 DoH 服务器处理 33 | if (path === `/${DoH路径}`) { 34 | return await DOHRequest(request); 35 | } 36 | 37 | // 添加IP地理位置信息查询代理 38 | if (path === '/ip-info') { 39 | if (env.TOKEN) { 40 | const token = url.searchParams.get('token'); 41 | if (token != env.TOKEN) { 42 | return new Response(JSON.stringify({ 43 | status: "error", 44 | message: "Token不正确", 45 | code: "AUTH_FAILED", 46 | timestamp: new Date().toISOString() 47 | }, null, 4), { 48 | status: 403, 49 | headers: { 50 | "content-type": "application/json; charset=UTF-8", 51 | 'Access-Control-Allow-Origin': '*' 52 | } 53 | }); 54 | } 55 | } 56 | 57 | const ip = url.searchParams.get('ip') || request.headers.get('CF-Connecting-IP'); 58 | if (!ip) { 59 | return new Response(JSON.stringify({ 60 | status: "error", 61 | message: "IP参数未提供", 62 | code: "MISSING_PARAMETER", 63 | timestamp: new Date().toISOString() 64 | }, null, 4), { 65 | status: 400, 66 | headers: { 67 | "content-type": "application/json; charset=UTF-8", 68 | 'Access-Control-Allow-Origin': '*' 69 | } 70 | }); 71 | } 72 | 73 | try { 74 | // 使用Worker代理请求HTTP的IP API 75 | const response = await fetch(`http://ip-api.com/json/${ip}?lang=zh-CN`); 76 | 77 | if (!response.ok) { 78 | throw new Error(`HTTP error: ${response.status}`); 79 | } 80 | 81 | const data = await response.json(); 82 | 83 | // 添加时间戳到成功的响应数据中 84 | data.timestamp = new Date().toISOString(); 85 | 86 | // 返回数据给客户端,并添加CORS头 87 | return new Response(JSON.stringify(data, null, 4), { 88 | headers: { 89 | "content-type": "application/json; charset=UTF-8", 90 | 'Access-Control-Allow-Origin': '*' 91 | } 92 | }); 93 | 94 | } catch (error) { 95 | console.error("IP查询失败:", error); 96 | return new Response(JSON.stringify({ 97 | status: "error", 98 | message: `IP查询失败: ${error.message}`, 99 | code: "API_REQUEST_FAILED", 100 | query: ip, 101 | timestamp: new Date().toISOString(), 102 | details: { 103 | errorType: error.name, 104 | stack: error.stack ? error.stack.split('\n')[0] : null 105 | } 106 | }, null, 4), { 107 | status: 500, 108 | headers: { 109 | "content-type": "application/json; charset=UTF-8", 110 | 'Access-Control-Allow-Origin': '*' 111 | } 112 | }); 113 | } 114 | } 115 | 116 | // 如果请求参数中包含 domain 和 doh,则执行 DNS 解析 117 | if (url.searchParams.has("doh")) { 118 | const domain = url.searchParams.get("domain") || url.searchParams.get("name") || "www.google.com"; 119 | const doh = url.searchParams.get("doh") || dnsDoH; 120 | const type = url.searchParams.get("type") || "all"; // 默认同时查询 A 和 AAAA 121 | 122 | // 如果使用的是当前站点,则使用 DoH 服务 123 | if (doh.includes(url.host)) { 124 | return await handleLocalDohRequest(domain, type, hostname); 125 | } 126 | 127 | try { 128 | // 根据请求类型进行不同的处理 129 | if (type === "all") { 130 | // 同时请求 A、AAAA 和 NS 记录,使用新的查询函数 131 | const ipv4Result = await queryDns(doh, domain, "A"); 132 | const ipv6Result = await queryDns(doh, domain, "AAAA"); 133 | const nsResult = await queryDns(doh, domain, "NS"); 134 | 135 | // 合并结果 - 修改Question字段处理方式以兼容不同格式 136 | const combinedResult = { 137 | Status: ipv4Result.Status || ipv6Result.Status || nsResult.Status, 138 | TC: ipv4Result.TC || ipv6Result.TC || nsResult.TC, 139 | RD: ipv4Result.RD || ipv6Result.RD || nsResult.RD, 140 | RA: ipv4Result.RA || ipv6Result.RA || nsResult.RA, 141 | AD: ipv4Result.AD || ipv6Result.AD || nsResult.AD, 142 | CD: ipv4Result.CD || ipv6Result.CD || nsResult.CD, 143 | 144 | // 修改处理Question字段的方式,兼容对象格式和数组格式 145 | Question: [], 146 | 147 | Answer: [...(ipv4Result.Answer || []), ...(ipv6Result.Answer || [])], 148 | ipv4: { 149 | records: ipv4Result.Answer || [] 150 | }, 151 | ipv6: { 152 | records: ipv6Result.Answer || [] 153 | }, 154 | ns: { 155 | records: [] 156 | } 157 | }; 158 | 159 | // 正确处理Question字段,无论是对象还是数组 160 | if (ipv4Result.Question) { 161 | if (Array.isArray(ipv4Result.Question)) { 162 | combinedResult.Question.push(...ipv4Result.Question); 163 | } else { 164 | combinedResult.Question.push(ipv4Result.Question); 165 | } 166 | } 167 | 168 | if (ipv6Result.Question) { 169 | if (Array.isArray(ipv6Result.Question)) { 170 | combinedResult.Question.push(...ipv6Result.Question); 171 | } else { 172 | combinedResult.Question.push(ipv6Result.Question); 173 | } 174 | } 175 | 176 | if (nsResult.Question) { 177 | if (Array.isArray(nsResult.Question)) { 178 | combinedResult.Question.push(...nsResult.Question); 179 | } else { 180 | combinedResult.Question.push(nsResult.Question); 181 | } 182 | } 183 | 184 | // 处理NS记录 - 可能在Answer或Authority部分 185 | const nsRecords = []; 186 | 187 | // 从Answer部分收集NS记录 188 | if (nsResult.Answer && nsResult.Answer.length > 0) { 189 | nsResult.Answer.forEach(record => { 190 | if (record.type === 2) { // NS记录类型是2 191 | nsRecords.push(record); 192 | } 193 | }); 194 | } 195 | 196 | // 从Authority部分收集NS和SOA记录 197 | if (nsResult.Authority && nsResult.Authority.length > 0) { 198 | nsResult.Authority.forEach(record => { 199 | if (record.type === 2 || record.type === 6) { // NS=2, SOA=6 200 | nsRecords.push(record); 201 | // 也添加到总Answer数组 202 | combinedResult.Answer.push(record); 203 | } 204 | }); 205 | } 206 | 207 | // 设置NS记录集合 208 | combinedResult.ns.records = nsRecords; 209 | 210 | return new Response(JSON.stringify(combinedResult, null, 2), { 211 | headers: { "content-type": "application/json; charset=UTF-8" } 212 | }); 213 | } else { 214 | // 普通的单类型查询,使用新的查询函数 215 | const result = await queryDns(doh, domain, type); 216 | return new Response(JSON.stringify(result, null, 2), { 217 | headers: { "content-type": "application/json; charset=UTF-8" } 218 | }); 219 | } 220 | } catch (err) { 221 | console.error("DNS 查询失败:", err); 222 | return new Response(JSON.stringify({ 223 | error: `DNS 查询失败: ${err.message}`, 224 | doh: doh, 225 | domain: domain, 226 | stack: err.stack 227 | }, null, 2), { 228 | headers: { "content-type": "application/json; charset=UTF-8" }, 229 | status: 500 230 | }); 231 | } 232 | } 233 | 234 | if (env.URL302) return Response.redirect(env.URL302, 302); 235 | else if (env.URL) { 236 | if (env.URL.toString().toLowerCase() == 'nginx') { 237 | return new Response(await nginx(), { 238 | headers: { 239 | 'Content-Type': 'text/html; charset=UTF-8', 240 | }, 241 | }); 242 | } else return await 代理URL(env.URL, url); 243 | } else return await HTML(); 244 | } 245 | } 246 | 247 | // 查询DNS的通用函数 248 | async function queryDns(dohServer, domain, type) { 249 | // 构造 DoH 请求 URL 250 | const dohUrl = new URL(dohServer); 251 | dohUrl.searchParams.set("name", domain); 252 | dohUrl.searchParams.set("type", type); 253 | 254 | // 尝试多种请求头格式 255 | const fetchOptions = [ 256 | // 标准 application/dns-json 257 | { 258 | headers: { 'Accept': 'application/dns-json' } 259 | }, 260 | // 部分服务使用没有指定 Accept 头的请求 261 | { 262 | headers: {} 263 | }, 264 | // 另一个尝试 application/json 265 | { 266 | headers: { 'Accept': 'application/json' } 267 | }, 268 | // 稳妥起见,有些服务可能需要明确的用户代理 269 | { 270 | headers: { 271 | 'Accept': 'application/dns-json', 272 | 'User-Agent': 'Mozilla/5.0 DNS Client' 273 | } 274 | } 275 | ]; 276 | 277 | let lastError = null; 278 | 279 | // 依次尝试不同的请求头组合 280 | for (const options of fetchOptions) { 281 | try { 282 | const response = await fetch(dohUrl.toString(), options); 283 | 284 | // 如果请求成功,解析JSON 285 | if (response.ok) { 286 | const contentType = response.headers.get('content-type') || ''; 287 | // 检查内容类型是否兼容 288 | if (contentType.includes('json') || contentType.includes('dns-json')) { 289 | return await response.json(); 290 | } else { 291 | // 对于非标准的响应,仍尝试进行解析 292 | const textResponse = await response.text(); 293 | try { 294 | return JSON.parse(textResponse); 295 | } catch (jsonError) { 296 | throw new Error(`无法解析响应为JSON: ${jsonError.message}, 响应内容: ${textResponse.substring(0, 100)}`); 297 | } 298 | } 299 | } 300 | 301 | // 错误情况记录,继续尝试下一个选项 302 | const errorText = await response.text(); 303 | lastError = new Error(`DoH 服务器返回错误 (${response.status}): ${errorText.substring(0, 200)}`); 304 | 305 | } catch (err) { 306 | // 记录错误,继续尝试下一个选项 307 | lastError = err; 308 | } 309 | } 310 | 311 | // 所有尝试都失败,抛出最后一个错误 312 | throw lastError || new Error("无法完成 DNS 查询"); 313 | } 314 | 315 | // 处理本地 DoH 请求的函数 - 直接调用 DoH,而不是自身服务 316 | async function handleLocalDohRequest(domain, type, hostname) { 317 | try { 318 | if (type === "all") { 319 | // 同时请求 A、AAAA 和 NS 记录 320 | const ipv4Promise = queryDns(dnsDoH, domain, "A"); 321 | const ipv6Promise = queryDns(dnsDoH, domain, "AAAA"); 322 | const nsPromise = queryDns(dnsDoH, domain, "NS"); 323 | 324 | // 等待所有请求完成 325 | const [ipv4Result, ipv6Result, nsResult] = await Promise.all([ipv4Promise, ipv6Promise, nsPromise]); 326 | 327 | // 准备NS记录数组 328 | const nsRecords = []; 329 | 330 | // 从Answer和Authority部分收集NS记录 331 | if (nsResult.Answer && nsResult.Answer.length > 0) { 332 | nsRecords.push(...nsResult.Answer.filter(record => record.type === 2)); 333 | } 334 | 335 | if (nsResult.Authority && nsResult.Authority.length > 0) { 336 | nsRecords.push(...nsResult.Authority.filter(record => record.type === 2 || record.type === 6)); 337 | } 338 | 339 | // 合并结果 340 | const combinedResult = { 341 | Status: ipv4Result.Status || ipv6Result.Status || nsResult.Status, 342 | TC: ipv4Result.TC || ipv6Result.TC || nsResult.TC, 343 | RD: ipv4Result.RD || ipv6Result.RD || nsResult.RD, 344 | RA: ipv4Result.RA || ipv6Result.RA || nsResult.RA, 345 | AD: ipv4Result.AD || ipv6Result.AD || nsResult.AD, 346 | CD: ipv4Result.CD || ipv6Result.CD || nsResult.CD, 347 | Question: [...(ipv4Result.Question || []), ...(ipv6Result.Question || []), ...(nsResult.Question || [])], 348 | Answer: [ 349 | ...(ipv4Result.Answer || []), 350 | ...(ipv6Result.Answer || []), 351 | ...nsRecords 352 | ], 353 | ipv4: { 354 | records: ipv4Result.Answer || [] 355 | }, 356 | ipv6: { 357 | records: ipv6Result.Answer || [] 358 | }, 359 | ns: { 360 | records: nsRecords 361 | } 362 | }; 363 | 364 | return new Response(JSON.stringify(combinedResult, null, 2), { 365 | headers: { 366 | "content-type": "application/json; charset=UTF-8", 367 | 'Access-Control-Allow-Origin': '*' 368 | } 369 | }); 370 | } else { 371 | // 普通的单类型查询 372 | const result = await queryDns(dnsDoH, domain, type); 373 | return new Response(JSON.stringify(result, null, 2), { 374 | headers: { 375 | "content-type": "application/json; charset=UTF-8", 376 | 'Access-Control-Allow-Origin': '*' 377 | } 378 | }); 379 | } 380 | } catch (err) { 381 | console.error("DoH 查询失败:", err); 382 | return new Response(JSON.stringify({ 383 | error: `DoH 查询失败: ${err.message}`, 384 | stack: err.stack 385 | }, null, 2), { 386 | headers: { 387 | "content-type": "application/json; charset=UTF-8", 388 | 'Access-Control-Allow-Origin': '*' 389 | }, 390 | status: 500 391 | }); 392 | } 393 | } 394 | 395 | // DoH 请求处理函数 396 | async function DOHRequest(request) { 397 | const { method, headers, body } = request; 398 | const UA = headers.get('User-Agent') || 'DoH Client'; 399 | const url = new URL(request.url); 400 | const { searchParams } = url; 401 | 402 | try { 403 | // 直接访问端点的处理 404 | if (method === 'GET' && !url.search) { 405 | // 如果是直接访问或浏览器访问,返回友好信息 406 | return new Response('Bad Request', { 407 | status: 400, 408 | headers: { 409 | 'Content-Type': 'text/plain; charset=utf-8', 410 | 'Access-Control-Allow-Origin': '*' 411 | } 412 | }); 413 | } 414 | 415 | // 根据请求方法和参数构建转发请求 416 | let response; 417 | 418 | if (method === 'GET' && searchParams.has('name')) { 419 | const searchDoH = searchParams.has('type') ? url.search : url.search + '&type=A'; 420 | // 处理 JSON 格式的 DoH 请求 421 | response = await fetch(dnsDoH + searchDoH, { 422 | headers: { 423 | 'Accept': 'application/dns-json', 424 | 'User-Agent': UA 425 | } 426 | }); 427 | // 如果 DoHUrl 请求非成功(状态码 200),则再请求 jsonDoH 428 | if (!response.ok) response = await fetch(jsonDoH + searchDoH, { 429 | headers: { 430 | 'Accept': 'application/dns-json', 431 | 'User-Agent': UA 432 | } 433 | }); 434 | } else if (method === 'GET') { 435 | // 处理 base64url 格式的 GET 请求 436 | response = await fetch(dnsDoH + url.search, { 437 | headers: { 438 | 'Accept': 'application/dns-message', 439 | 'User-Agent': UA 440 | } 441 | }); 442 | } else if (method === 'POST') { 443 | // 处理 POST 请求 444 | response = await fetch(dnsDoH, { 445 | method: 'POST', 446 | headers: { 447 | 'Accept': 'application/dns-message', 448 | 'Content-Type': 'application/dns-message', 449 | 'User-Agent': UA 450 | }, 451 | body: body 452 | }); 453 | 454 | } else { 455 | // 其他不支持的请求方式 456 | return new Response('不支持的请求格式: DoH请求需要包含name或dns参数,或使用POST方法', { 457 | status: 400, 458 | headers: { 459 | 'Content-Type': 'text/plain; charset=utf-8', 460 | 'Access-Control-Allow-Origin': '*' 461 | } 462 | }); 463 | } 464 | 465 | if (!response.ok) { 466 | const errorText = await response.text(); 467 | throw new Error(`DoH 返回错误 (${response.status}): ${errorText.substring(0, 200)}`); 468 | } 469 | 470 | // 创建一个新的响应头对象 471 | const responseHeaders = new Headers(response.headers); 472 | // 设置跨域资源共享 (CORS) 的头部信息 473 | responseHeaders.set('Access-Control-Allow-Origin', '*'); 474 | responseHeaders.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); 475 | responseHeaders.set('Access-Control-Allow-Headers', '*'); 476 | 477 | // 检查是否为JSON格式的DoH请求,确保设置正确的Content-Type 478 | if (method === 'GET' && searchParams.has('name')) { 479 | // 对于JSON格式的DoH请求,明确设置Content-Type为application/json 480 | responseHeaders.set('Content-Type', 'application/json'); 481 | } 482 | 483 | // 返回响应 484 | return new Response(response.body, { 485 | status: response.status, 486 | statusText: response.statusText, 487 | headers: responseHeaders 488 | }); 489 | } catch (error) { 490 | console.error("DoH 请求处理错误:", error); 491 | return new Response(JSON.stringify({ 492 | error: `DoH 请求处理错误: ${error.message}`, 493 | stack: error.stack 494 | }, null, 4), { 495 | status: 500, 496 | headers: { 497 | 'Content-Type': 'application/json', 498 | 'Access-Control-Allow-Origin': '*' 499 | } 500 | }); 501 | } 502 | } 503 | 504 | async function HTML() { 505 | // 否则返回 HTML 页面 506 | const html = ` 507 | 508 | 509 |
510 | 511 | 512 |正在查询中,请稍候...
949 |DNS-over-HTTPS:https://.../${DoH路径}
基于 Cloudflare Workers 上游 ${DoH} 的 DoH (DNS over HTTPS)
1000 | 解析服务
If you see this page, the nginx web server is successfully installed and 1563 | working. Further configuration is required.
1564 | 1565 |For online documentation and support please refer to
1566 | nginx.org.
1567 | Commercial support is available at
1568 | nginx.com.
Thank you for using nginx.
1571 | 1572 | 1573 | ` 1574 | return text; 1575 | } -------------------------------------------------------------------------------- /img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmliu/CF-Workers-DoH/ce067051421f8cbaccc54699cedcdff71895a69a/img.png --------------------------------------------------------------------------------