钉钉机器人 webhook 应通过环境变量安全传递,禁用硬编码;php 发送需设 content-type 为 application/json、用 json_encode 构造合法 json;text 适合纯告警,markdown 首行不可空且需转义变量。

钉钉机器人 Webhook 地址怎么安全传给 PHP?
直接把 webhook URL 写死在代码里是高危操作,尤其当项目要上 Git 或部署到多环境时。钉钉机器人密钥一旦泄露,任何人能往群里发任意消息。
推荐做法:通过环境变量注入,而不是硬编码或配置文件明文存储。
-
$_ENV['DINGTALK_WEBHOOK']或getenv('DINGTALK_WEBHOOK')读取(需确保 PHP 启用了variables_order包含E) - 部署时用
docker run -e DINGTALK_WEBHOOK="https://oapi.dingtalk.com/robot/send?access_token=xxx"传入 - 本地开发可用
.env文件 +vlucas/phpdotenv加载,但上线前必须切到系统环境变量 - 绝对不要把
access_token出现在日志、var_dump、错误页面或前端 JS 里
PHP cURL 发送 JSON 消息总被钉钉拒绝?
常见报错是 {"errcode":310000,"errmsg":"invalid signature"} 或直接 400,根本原因不是签名错了(普通 webhook 不需要签名),而是请求体或头不对。
钉钉机器人只接受 POST 请求,且必须满足三个条件:Content-Type 是 application/json、body 是合法 JSON、HTTP 状态码返回 200 才算成功。
- 用
json_encode()构造消息体,别拼字符串 —— 中文、特殊符号、空格都会导致 JSON 格式错误 - cURL 必须显式设置
Content-Type: application/json,否则钉钉当表单处理,直接拒收 - 加
CURLOPT_RETURNTRANSFER => true,否则curl_exec()返回true而非响应内容,你没法判断errcode - 示例关键段:
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($msg));<br>curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
text 和 markdown 类型消息的区别和选型
钉钉支持 text、markdown、link、actionCard 等类型,但 PHP 后端最常用的是前两种。选错类型会导致消息不渲染或被截断。
text 最简单,纯文本,适合告警摘要;markdown 支持加粗、列表、链接,但注意钉钉的 MD 解析很弱 —— 不支持表格、嵌套列表、HTML 标签,且首行不能空。
-
text消息结构极简:["msgtype" => "text", "text" => ["content" => "部署完成"]] -
markdown首行必须是标题或段落,不能以空行开头,否则整个消息被忽略 - 如果内容含变量(如
$env、$commit_id),先用htmlspecialchars()转义,再塞进 markdown —— 防止&、破坏格式 - 钉钉对 markdown 渲染有长度限制(约 2000 字符),超长会截断,此时不如拆成多条
text消息
为什么发消息没报错但群里没收到?
最常被忽略的是机器人权限和群设置。钉钉侧拦截不返回 HTTP 错误,PHP 层看到 200 就以为成功了,其实消息压根没进队列。
检查顺序必须是:先看钉钉群设置 → 再查机器人管理页 → 最后看 PHP 日志响应体。
- 确认该机器人在目标群「已启用」且「未被禁言」—— 群管理员可能悄悄关闭了机器人
- 登录钉钉 PC 端,打开群右上角「群机器人」→ 找到对应机器人 → 点「编辑」→ 检查「自定义关键词」是否开启,且你发的内容不含触发词(比如设了“告警”才放行,但你发的是“完成”,就被过滤)
- PHP 侧别只看
curl_exec()返回值,一定要解析响应 JSON:json_decode($res, true)['errcode'] === 0才算真成功 - 测试时用固定字符串发一次,别用动态变量 —— 排除数据为空、null、false 导致消息体非法的情况
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











