抱歉,您的瀏覽器無法訪問本站
本頁面需要瀏覽器支持(啟用)JavaScript
了解詳情 >

最近網路環境變化,NextDNS 和 AdGuard DNS 的 DoH 443 埠被封,導致無法正常使用。
本指南教你如何通過 Cloudflare Workers 自建可靠的 DNS-over-HTTPS 服務。


📦 準備工作

  1. Cloudflare 帳戶
  2. 自有域名
    • 可在 Cloudflare 託管或使用免費域名(如 .tk/.ml)
  3. GitHub 帳戶

🔧 部署步驟

第一步:獲取項目代碼

  1. 訪問開源項目:https://github.com/tina-hello/doh-cf-workers
  2. 推薦做法:創建私有倉庫
    1
    2
    3
    # 1. Fork 到自己的GitHub帳戶
    # 2. 進入倉庫設置 → 修改為Private(私有)
    # 3. 這樣你的DNS地址就不會公開

第二步:連接 GitHub 和 Cloudflare

  1. 在 GitHub 配置 Action 權限

    • 進入你的私有倉庫 → Settings → Actions → General
    • 開啟 Read and write permissions(必須!)
    • 保存設置
  2. 創建 Cloudflare API 令牌

    1. 登錄 Cloudflare 控制台
    2. 右上角「我的個人資料」→「API 令牌」
    3. 創建令牌 → 使用模板 “編輯 Cloudflare Workers”
    4. 權限設定:
      • 帳戶:Workers 腳本 - 編輯
      • 區域:根據需要添加(綁定域名時需要)
    5. 生成令牌 → 立即複製保存(離開後無法查看)

第三步:配置 GitHub Secrets

在你的私有倉庫中:

  1. Settings → Secrets and variables → Actions
  2. 添加以下兩個機密:
    名稱
    CLOUDFLARE_ACCOUNT_ID 你的Cloudflare帳戶ID(控制台右上角獲取)
    CLOUDFLARE_API_TOKEN 上一步生成的API令牌

第四步:觸發自動部署

  1. 修改代碼後推送到倉庫:
    1
    2
    3
    git add .
    git commit -m "更新DNS配置"
    git push origin main
  2. 查看 Action 狀態:
    • 倉庫 → Actions → 查看部署日誌
    • 出現 ✅ Successfully published 表示成功

🔐 綁定自訂域名(可選但推薦)

避免使用默認的 *.workers.dev 地址,提升隱蔽性

  1. 在 Cloudflare 控制台:
    • Workers & Pages → 你的Worker → 觸發器 → 自訂域
  2. 添加你的域名(如 doh.yourdomain.com
  3. 在域名DNS設置中添加CNAME記錄:
    1
    2
    3
    4
    類型: CNAME
    名稱: doh
    內容: your-worker-name.your-account.workers.dev
    TTL: 自動

🛡️ 增強安全性:IP訪問控制

限制只有特定IP能訪問你的DNS服務

方法1:在Worker代碼中添加(推薦)

編輯 index.js,在頂部添加:

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
// 允許訪問的IP列表
const allowedIPs = [
'192.168.1.0/24', // 範例:家庭網路
'203.0.113.42', // 範例:辦公IP
'2001:db8::/32' // IPv6範例
];

export default {
async fetch(request) {
const clientIP = request.headers.get('cf-connecting-ip');

// IP檢查邏輯
if (!isIPAllowed(clientIP, allowedIPs)) {
return new Response('Access Denied', { status: 403 });
}

// 原有DNS處理代碼...
}
};

// IP檢查函數
function isIPAllowed(ip, allowedList) {
// 這裡實現IP檢查邏輯
// 可以使用ip-cidr等npm包
return true; // 範例
}

方法2:Cloudflare防火牆規則

  1. 控制台 → 安全 → WAF
  2. 創建新規則:
    • 欄位:IP源地址
    • 運算符:不在
    • 值:填入你的IP列表(每行一個)
    • 操作:阻止

index.js配置實例

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
138
139
140
141
142
143
144
145
146
147
// SPDX-License-Identifier: 0BSD

// 定義上游DNS伺服器列表(按優先度排序)
const dohUpstreams = [
// 一級:快速可靠的核心服務
'https://dns.cloudflare.com/dns-query',
'https://security.cloudflare-dns.com/dns-query',
'https://dns.google/dns-query',

// 二級:IPv6最佳化的備用服務
'https://[2606:4700:4700::1111]/dns-query',
'https://[2620:fe::fe]/dns-query',

// 三級:功能型服務
'https://dns.adguard-dns.com/dns-query',
'https://unfiltered.adguard-dns.com/dns-query',
'https://doh.opendns.com/dns-query',
'https://doh.quad9.net/dns-query'
];

const customPath = "/dns-query";
const timeoutMs = 3000; // 3秒超時
const maxRetries = dohUpstreams.length; // 嘗試所有伺服器

export default {
async fetch(request, env, ctx) {
return handleRequest(request);
},
};

// 帶超時控制的fetch
async function fetchWithTimeout(url, options) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);

try {
const response = await fetch(url, {
...options,
signal: controller.signal
});
return response;
} finally {
clearTimeout(timeoutId);
}
}

async function handleRequest(request) {
const { method, headers, url } = request;
const { searchParams, pathname } = new URL(url);
const now = new Date().toISOString();

if (pathname !== customPath) {
return new Response(`404 Not Found\n${now}`, { status: 404 });
}

// 健康檢查端點
if (method === 'GET' && !searchParams.toString()) {
return new Response(
`DNS Worker - Priority-based Failover\nTime: ${now}\nUpstreams:\n${dohUpstreams.join('\n')}`,
{ headers: { 'Content-Type': 'text/plain' } }
);
}

try {
// DNS-over-HTTPS 二進制查詢 (RFC 8484)
if (method === 'GET' && searchParams.has('dns')) {
const dnsParam = searchParams.get('dns');
let lastError = null;

// 按配置順序嘗試上游伺服器
for (let i = 0; i < maxRetries; i++) {
try {
const upstream = dohUpstreams[i];
return await fetchWithTimeout(`${upstream}?dns=${dnsParam}`, {
method: 'GET',
headers: { 'Accept': 'application/dns-message' }
});
} catch (e) {
lastError = e;
console.warn(`[Failover] Upstream ${i} failed (${dohUpstreams[i]}): ${e.message}`);
// 繼續嘗試下一個伺服器
}
}
throw lastError || new Error("All upstreams failed");

// DNS JSON API 查詢
} else if (method === 'GET' && (searchParams.has('name') || searchParams.has('type'))) {
const params = new URLSearchParams(searchParams);
let lastError = null;

// 按優先度嘗試JSON查詢端點
for (let i = 0; i < maxRetries; i++) {
try {
// 特殊處理Google的JSON端點
const baseUrl = dohUpstreams[i].includes('dns.google')
? 'https://dns.google/resolve'
: dohUpstreams[i].replace('/dns-query', '');

return await fetchWithTimeout(`${baseUrl}?${params}`, {
method: 'GET',
headers: { 'Accept': 'application/dns-json' }
});
} catch (e) {
lastError = e;
console.warn(`[Failover] JSON upstream ${i} failed: ${e.message}`);
}
}
throw lastError || new Error("All JSON upstreams failed");

// POST請求處理
} else if (method === 'POST' && headers.get('content-type') === 'application/dns-message') {
const body = await request.arrayBuffer();
let lastError = null;

for (let i = 0; i < maxRetries; i++) {
try {
return await fetchWithTimeout(dohUpstreams[i], {
method: 'POST',
headers: {
'Accept': 'application/dns-message',
'Content-Type': 'application/dns-message',
},
body: body
});
} catch (e) {
lastError = e;
console.warn(`[Failover] POST upstream ${i} failed: ${e.message}`);
}
}
throw lastError || new Error("All POST upstreams failed");
}

return new Response(
'Invalid DNS request format\nRefer to RFC8484',
{ status: 400, headers: { 'Content-Type': 'text/plain' } }
);

} catch (e) {
return new Response(`DNS Resolution Failed\nError: ${e.message}`, {
status: 503,
headers: {
'Content-Type': 'text/plain',
'Cache-Control': 'no-store, max-age=0'
}
});
}
}

參考

評論



Powered by Hexo | Theme keep Volantis

本站總訪問量 總訪客數 🌎