今天想着把 Cloudflare 账单通知打开,然后把通知推到钉钉群里方便查看。
结果发现邮件通知正常收到了,钉钉那边却一点反应都没有。
排查之后发现是 Cloudflare 发出的 Webhook payload 和钉钉机器人要求的格式对不上。
于是搓了个 Cloudflare Worker 做中间层转换,把 Cloudflare 的 payload 转换成钉钉能识别的格式。
1. 前置条件
- 一个 Cloudflare 账号
- 一个钉钉群,已创建自定义机器人,安全设置选择加签模式
问题分析:为什么直接填 Webhook 没反应
Cloudflare 发送的 payload 格式:官方文档
json
{
"name": "string",
"text": "string",
"data": {},
"ts": 1136214245
}
钉钉机器人文本格式:官方文档
css
{
"msgtype": "text",
"text": {
"content": "xxxx"
}
}
2. 创建 Worker
在 Cloudflare 控制台进入 Workers & Pages ,选择 Create Worker:
- 选择 "Hello World" Worker
- 给 Worker 起个名字,比如
dingtalk-notify - 点击 部署
- 部署完成后点击编辑代码
把默认代码全部删掉,替换为下面这段:
javascript
// 生成钉钉加签
async function generateSign(timestamp, secret) {
const encoder = new TextEncoder();
const keyData = encoder.encode(secret);
const message = encoder.encode(`${timestamp}\n${secret}`);
const cryptoKey = await crypto.subtle.importKey(
"raw",
keyData,
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const signature = await crypto.subtle.sign("HMAC", cryptoKey, message);
const sign = btoa(String.fromCharCode(...new Uint8Array(signature)));
return encodeURIComponent(sign);
}
export default {
async fetch(request, env, ctx) {
if (request.method !== "POST") {
return new Response(
"This Worker only accepts POST requests from Cloudflare notifications.",
{ status: 405, headers: { "Content-Type": "text/plain" } }
);
}
try {
const text = await request.text();
if (!text) {
return new Response(JSON.stringify({ error: "Empty request body" }), {
status: 400,
headers: { "Content-Type": "application/json" },
});
}
const cfPayload = JSON.parse(text);
const title = cfPayload.name || "Cloudflare 通知";
const content = cfPayload.text || "";
const dingtalkBody = {
msgtype: "text",
text: { content: `${title}\n${content}` },
};
const timestamp = Date.now();
const sign = await generateSign(timestamp, env.DINGTALK_SECRET);
const webhookUrl = new URL(env.DINGTALK_WEBHOOK_URL);
webhookUrl.searchParams.set("timestamp", timestamp);
webhookUrl.searchParams.set("sign", sign);
const res = await fetch(webhookUrl.toString(), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(dingtalkBody),
});
const result = await res.text();
return new Response(result, { status: res.status });
} catch (err) {
return new Response(JSON.stringify({ error: err.message }), {
status: 500,
headers: { "Content-Type": "application/json" },
});
}
},
};
改完后点击 部署。
3. 设置环境变量
在 Worker 页面,点击顶部 设置 标签,然后点击左侧 变量和机密:
添加两个变量:
| 变量名 | 值 | 类型 |
|---|---|---|
DINGTALK_WEBHOOK_URL |
你的钉钉 Webhook 地址,如 https://oapi.dingtalk.com/robot/send?access_token=xxx |
密钥 |
DINGTALK_SECRET |
钉钉机器人后台加签 SECxxxxxx |
密钥 |
4. 修改 Cloudflare 通知目标
- 回到 Cloudflare 控制台的通知 Webhook 页面 目的地 -----> 创建
- 把 Webhook URL 改成你的 Worker 地址(格式类似
https://dingtalk-notify.xxx.workers.dev) - Secret 留空
- 点击 保存并测试
此时钉钉群里会收到一条测试通知。
5. 效果
配置完成后,Cloudflare 的通知就会通过 Worker 转发到钉钉群了

常见问题
- 钉钉机器人后台的安全设置必须是加签模式 (不是关键词模式),
SECxxxxxx才能生效。如果是关键词模式,要么改成加签,要么把 Worker 代码改成不带加签的版本。 - 浏览器访问 Worker 地址返回 405? 这是正常的,Worker 只接受 POST 请求,需要用 Cloudflare 的通知去触发。