大数跨境

收藏!微信公众号草稿箱 API HTML 校验规范

收藏!微信公众号草稿箱 API HTML 校验规范 全球CTO俱乐部
2026-09-20
9

verify-article-structure-spec 完整规则(对应微信公众号草稿箱API HTML校验规范)

来源:微信开放社区《公众平台编辑器插件开发规范》,仓库 verify_article_structure.md 就是这份规范,是草稿接口上传前的预校验规则。 核心前提:所有样式只能写在元素 style="" 内联;禁止外部CSS、<style>、class/id 样式选择器

✅ 允许使用的 HTML 标签

推荐基础容器:<section>(公众号原生容器,优先用它代替div) 行内文本:<span><span leaf="">(专门行内叶子节点,只能放文本/行内元素,不能包块级标签) 语义标签:<p><strong><em><u><s><br><a><ul><ol><li> 媒体:<img><video><svg> 表格:<table><tr><td><th>

<div> 也可以用,但微信原生编辑器默认输出是 <section><pre> 不推荐(white-space:pre 会横向溢出)

❌ 直接禁止的标签(上传草稿会被直接删除)

<script><iframe><noscript><form><input><button><canvas><object><embed><style><link><meta><head><body><html>

✅ 允许的 CSS 属性(写在 style 内联)

文字:colorfont-sizefont-weightfont-styletext-alignletter-spacingline-heighttext-decoration 背景:background-colorbackground-imagebackground-repeatbackground-sizebackground-position 边框:border / border-width / border-color / border-styleborder-radius 盒模型:paddingmarginwidthheightoverflowoverflow-xoverflow-y 布局:displayinline/inline-block/block/flex ✅;grid ❌)、vertical-align 阴影:box-shadow 其他:opacitytransformtransform-originuser-select

注意:display:flex 可用,但 display:grid 是黑名单

❌ 禁止 / 不推荐 CSS 属性 & 值

  1. positionabsolute / relative / fixed / sticky 不推荐,容易破坏阅读顺序、dark mode 识别异常
  2. float:禁止
  3. z-index:禁止
  4. CSS 变量 var():禁止
  5. @keyframes@media、CSS动画:禁止(只能内联静态transform,不能css动画)
  6. text-align:start / end:禁止,跨终端对齐不一致,只能用 left/center/right
  7. caret-color:transparent:禁止,编辑器光标看不见
  8. !important:强烈不建议,会干扰深色模式自动转换
  9. font-family不建议写,公众号自带统一字体栈,自定义字体会iOS/安卓渲染不一致

📐 结构校验规则(这个仓库重点检测项)

1. DOM嵌套层数限制

连续同标签、无新增样式、只包含单个子节点的嵌套链路,不能超过10层,超过会被编辑器自动精简删除。

图片/svg/media标签不受该层数限制。

2. span[leaf] 强制规则

<span leaf=""> 是行内叶子节点,内部不能包裹块级元素(section/div/p),只能放文本、strong、img这类行内内容。

3. width 宽度检测(非常高频报错)

  • ❌ 硬写固定px宽度,会检测「大屏居中失效、移动端水平溢出」
  • ✅ 优先百分比 width:100% 自适应
  • 豁免:给节点加 data-ignore-width,跳过该节点及其子树的宽度溢出检测(横向滚动卡片常用)

4. height 高度检测

  • ❌ 文字容器 height:0:移动端直接空白看不见
  • ❌ 固定height且内容超出 → 内容被裁剪>

豁免:svg内部、纯图片容器的 height:0 不违规

5. line-height 行高检测

  • 多行文字:line-height 不能小于font-size → 文字重叠,直接报违规
  • ✅ 例外:容器没有文字,只放图片,line-height:0 用来消除图片间隙是允许的;单行文字也不受限制

🌑 Dark Mode 深色模式规则(仓库会检测对比度)

  • 文字和背景对比度不足会告警;可加 data-ignore-dm="low-contrast" 豁免对比度检查
  • data-no-dark:当前节点不参与深色模式颜色转换(仅本节点)
  • data-ignore-dm="text-bg-gradient":豁免文字背景渐变警告>

背景图上的文字,深色模式不会自动反转文字颜色,需要自己处理对比度

🧩 SVG 特殊规则

  • SVG 本身允许;SVG内部 animate 动画,如果监听touchstart,必须同时写clickbegin="touchstart; click",否则判定不兼容
  • SVG不参与Dark Mode自动变色,建议 fill="currentColor" 适配深浅色

🔗 链接与图片

  • <a href="https://xxx"> 支持,但是仅支持公众号允许的域名,草稿API上传后微信还会二次校验链接
  • img src 必须是 https mmbiz.qpic 资源;外部图会被微信后台过滤

校验通过 ≠ 100% 草稿一定成功

  1. 这个工具做本地预校验;调用草稿接口提交时,微信后台还会再做一轮HTML清洗过滤
  2. 部分样式本地puppeteer渲染没问题,但公众号手机端渲染差异
  3. 校验无报错后,必须登录公众号后台打开草稿预览+手机预览

快速CLI命令

git clone [https://github.com/wechatjs/verify-article-structure-spec.git](https://github.com/wechatjs/verify-article-structure-spec.git)
cd verify-article-structure-spec/cli
npm install
# 检测HTML,输出可读报告
npm run check ./article.html
# JSON结构化输出,适合接入自动化流水线
npm run check ./article.html --json
# 自动清理冗余嵌套标签
npm run dedupe ./article.html --out=clean.html

白名单/黑名单JSON(可直接接入SpringBoot校验接口)

适配 verify-article-structure-spec 规则,用于后端预校验公众号草稿HTML

{
  "allowTags": [
    "section""span""p""strong""em""u""s""br""a",
    "ul""ol""li""img""video""svg""table""tr""td""th""div"
  ],
  "denyTags": [
    "script""iframe""noscript""form""input""button",
    "canvas""object""embed""style""link""meta""head""body""html"
  ],
  "allowCssProps": [
    "color""font-size""font-weight""font-style""text-align",
    "letter-spacing""line-height""text-decoration",
    "background-color""background-image""background-repeat""background-size""background-position",
    "border""border-width""border-color""border-style""border-radius",
    "padding""margin""width""height""overflow""overflow-x""overflow-y",
    "display""vertical-align""box-shadow""opacity""transform""transform-origin""user-select"
  ],
  "denyCssProps": [
    "position""float""z-index""var""@keyframes""@media",
    "caret-color""font-family"
  ],
  "denyCssValues": {
    "display": ["grid"],
    "text-align": ["start""end"]
  },
  "specialAttrAllow": [
    "leaf""data-ignore-width""data-ignore-dm""data-no-dark""href""src"
  ],
  "maxNestingDepth": 10,
  "rulesNote": {
    "important""不推荐使用!important",
    "position""absolute/relative/fixed/sticky虽能解析,但强烈不建议,深色模式异常风险高",
    "lineHeight""多行文字line-height不能小于font-size,防止文字重叠",
    "imgSrc""必须为mmbiz.qpic.cn的https地址,外部图片微信后台会过滤"
  }
}

极简JS核心校验函数(复刻核心规则,无需Puppeteer,可嵌入SpringBoot通过NodeJS调用,或前端预校验)

/**
 * 公众号草稿HTML简易校验(复刻 verify-article-structure-spec 核心规则)
 * @param {string} html 待检测html片段(只传文章主体,不带<html><body>)
 * @returns {Object} {pass:boolean, errors:Array, warnings:Array}
 */
function checkWechatArticleHtml(html) {
  const allowTags = new Set(["section","span","p","strong","em","u","s","br","a","ul","ol","li","img","video","svg","table","tr","td","th","div"]);
  const denyTags = new Set(["script","iframe","noscript","form","input","button","canvas","object","embed","style","link","meta","head","body","html"]);
  const allowCssProps = new Set(["color","font-size","font-weight","font-style","text-align","letter-spacing","line-height","text-decoration","background-color","background-image","background-repeat","background-size","background-position","border","border-width","border-color","border-style","border-radius","padding","margin","width","height","overflow","overflow-x","overflow-y","display","vertical-align","box-shadow","opacity","transform","transform-origin","user-select"]);
  const denyCssProps = new Set(["position","float","z-index","var","caret-color","font-family"]);
  const errors = [];
  const warnings = [];

  // 1. 检测禁止标签
  for(const tag of denyTags){
    const reg = new RegExp(`<${tag}[ >]`, 'i');
    if(reg.test(html)){
      errors.push(`禁止标签 <${tag}>,公众号草稿接口会直接删除`);
    }
  }

  // 2. 提取内联style属性,校验css属性
  const styleReg = /style="([^"]+)"/g;
  let styleMatch;
  while((styleMatch = styleReg.exec(html)) !== null){
    const styleStr = styleMatch[1];
    const decls = styleStr.split(';').filter(s=>s.trim());
    for(const decl of decls){
      const [prop, val] = decl.split(':').map(x=>x.trim());
      if(!prop) continue;
      const propRaw = prop.replace(/!important/i,'').trim();
      if(denyCssProps.has(propRaw)){
        errors.push(`禁止CSS属性:${propRaw}`);
      }
      if(propRaw === 'display' && val.toLowerCase() === 'grid'){
        errors.push(`display:grid 不支持`);
      }
      if(propRaw === 'text-align' && ['start','end'].includes(val.toLowerCase())){
        errors.push(`text-align:${val} 不支持,仅允许left/center/right`);
      }
      if(decl.includes('!important')){
        warnings.push(`不建议使用 !important,深色模式适配异常风险`);
      }
    }
  }

  // 3. 简易嵌套深度检查(简化版,完整深度检测建议用dom解析库如cheerio)
  const tagStack = [];
  const tagReg = /<(\/)?([a-zA-Z0-9]+)[ >]/g;
  let tagMatch;
  while((tagMatch = tagReg.exec(html)) !== null){
    const isClose = !!tagMatch[1];
    const tagName = tagMatch[2].toLowerCase();
    if(isClose){
      const last = tagStack[tagStack.length-1];
      if(last === tagName) tagStack.pop();
    }else{
      tagStack.push(tagName);
      if(tagStack.length > 10){
        errors.push(`DOM嵌套层级超过10层,最大允许10层,当前${tagStack.length}`);
      }
    }
  }

  // 4. span leaf 简单校验:<span leaf> 内部不能包含块级标签
  const spanLeafReg = /<span leaf="
?"[^>]*>([\s\S]*?)<\/span>/gi;
  let spanMatch;
  const blockTags = new Set(['section','div','p','ul','ol','table']);
  while((spanMatch = spanLeafReg.exec(html))!==null){
    const inner = spanMatch[1];
    for(const bt of blockTags){
      if(new RegExp(`<${bt}[ >]`,"
i").test(inner)){
        errors.push(`<span leaf> 内部禁止包含块级标签 <${bt}>`);
      }
    }
  }

  return {
    pass: errors.length === 0,
    errors,
    warnings
  }
}

// 使用示例
/*
const html = `<section><p style="
color:#333;display:flex;">测试</p></section>`;
const ret = checkWechatArticleHtml(html);
console.log(ret);
*/

注意:JS这个是轻量简化版;如果要和仓库校验完全一致,后端建议引入 cheerio 做DOM解析,替代正则解析DOM(正则处理嵌套HTML有边界缺陷)。

公众号草稿API可用最小HTML模板,直接提交草稿content

<section>
  <p style="font-size:16px;color:#333;line-height:1.7;text-align:left;">
    正文第一段文字测试,<strong>加粗文字</strong>,<em>斜体文字</em>,<u>下划线</u>,<s>删除线</s>。
  </p>
  <p style="font-size:16px;color:#333;line-height:1.7;">
    <a href="[https://www.example.com](https://www.example.com)" style="color:#0066cc;text-decoration:underline;">测试超链接</a>
  </p>
  <ul style="padding-left:20px;">
    <li style="font-size:16px;line-height:1.7;color:#333;">列表项1</li>
    <li style="font-size:16px;line-height:1.7;color:#333;">列表项2</li>
  </ul>
  <p>
    <img src="[https://mmbiz.qpic.cn/xxx/xxx.jpg](https://mmbiz.qpic.cn/xxx/xxx.jpg)" style="width:100%;border-radius:8px;display:block;">
  </p>
</section>

要点:

  1. 根节点用<section>
  2. 全部样式写内联style,无class;
  3. img使用mmbiz.qpic域名图片;
  4. 无禁止标签、无grid、position等高危样式。


【声明】内容源于网络
0
0
全球CTO俱乐部
CTO俱乐部,一个汇集CTO、CIO、首席技术官、技术总监、研发总监、IT总监、技术专家等人脉平台,涉及人工智能AI、跨境电商、出海、低空经济、新能源、软件服务、IT服务、人才外包等行业,官网:www.ctoclub.com
内容 209
粉丝 0
全球CTO俱乐部 CTO俱乐部,一个汇集CTO、CIO、首席技术官、技术总监、研发总监、IT总监、技术专家等人脉平台,涉及人工智能AI、跨境电商、出海、低空经济、新能源、软件服务、IT服务、人才外包等行业,官网:www.ctoclub.com
总阅读4.3k
粉丝0
内容209