编程 MV3 的 declarativeNetRequest:四字段规则、匹配优先级与 webRequest 的取舍

2026-10-04 00:05:11

MV3 的 declarativeNetRequest:四字段规则、匹配优先级与 webRequest 的取舍

参考文档:MDN declarativeNetRequest、Chrome 官方文档。

阻塞式 webRequest 为什么被砍

MV2 的阻塞式 webRequest 要把每个请求回调到扩展进程里跑 JS 判断,扩展因此能看到完整 URL、header 和响应流。MV3 换成了 declarativeNetRequest:扩展只提交规则,请求由浏览器自己评估,不通知扩展单个请求。

请求不再经过扩展进程,后台页也就不再是拦截链路的必需项。代价是规则只能表达「条件 + 固定动作」,不能做任意编程式判断。Firefox 同样支持 DNR,但存在一些约束差异。

一条规则就四个字段

字段含义
idruleset 内唯一,>= 1,必填
priority规则优先级,>= 1,默认 1,决定哪条规则生效
condition触发条件
action匹配时的动作:block / redirect / modifyHeaders / allow / allowAllRequests / upgradeScheme

redirect 有个边界:如果 action 实际没有改变请求,或者重定向 URL 非法(例如 regexSubstitution 的结果不是合法 URL),就不会重定向,请求照常继续。

拦截来自 example.com、URL 中含 abc 子串的 script 请求:

[
  {
    "id": 1,
    "priority": 1,
    "action": { "type": "block" },
    "condition": {
      "urlFilter": "abc",
      "initiatorDomains": ["example.com"],
      "resourceTypes": ["script"]
    }
  }
]

urlFilter 的四种写法

urlFilter匹配不匹配
abchttps://abcd.com、https://example.com/abcdhttps://ab.com
abc*dhttps://abcd.com、https://example.com/abcxyzdhttps://abc.com
||a.example.comhttps://a.example.com/、https://b.a.example.com/xyzhttps://example.com/
|https*https://example.comhttp://example.com/、http://https.com

abc 是子串匹配,abc*d 中间的 * 代表任意字符,|| 锚定域名,| 锚定 URL 开头。

static / dynamic / session 三套 ruleset

static:在 manifest 的 declarative_net_request 键里声明,文件打包在扩展内,用 updateEnabledRulesets 启用或禁用。已启用的集合跨浏览器会话持久,但不跨扩展更新;安装或更新时哪些静态规则集启用,由 manifest 键的内容决定。

{
  "declarative_net_request": {
    "rule_resources": [
      { "id": "ruleset_1", "enabled": true, "path": "rules.json" }
    ]
  }
}

dynamic:用 updateDynamicRules 增删,跨会话、跨扩展更新持久。

session:用 updateSessionRules 增删,不跨浏览器会话持久。

静态规则非法时,只有在测试期才会看到错误和警告;正式安装的扩展里非法静态规则会被静默忽略。所以静态规则必须走一遍测试验证,不能靠肉眼看 JSON。

匹配优先级与歧义

先比 priority,默认 1 最低。priority 分不出胜负时,按 action 排序:allow(其余规则全部忽略)、allowAllRequests(仅 main_frame / sub_frame,且作用于该请求产生的后续子资源加载,含后代 frame)、block、upgradeScheme、redirect、modifyHeaders。

同 priority 同 action 类型时可能出现歧义:多个 block 无歧义;多个 redirect 只有一个生效,但可能形成链式重定向;多个 modifyHeaders 如果改的是不同 header 可以各自生效,改同一个 header 结果不确定。要控制顺序就设不同 priority。

Firefox 还会按规则所属 ruleset 排优先级:session > dynamic > static,这一点跨浏览器不可依赖(见 WECG issue 280)。多个扩展同时命中时,按 block > redirect/upgradeScheme > allow/allowAllRequests 排序;请求未被 block 或 redirect 时,再应用 modifyHeaders。

限额都用常量名表达

  • MAX_NUMBER_OF_STATIC_RULESETS:静态规则集数量上限。
  • GUARANTEED_MINIMUM_STATIC_RULES:启用静态规则总数上限。
  • MAX_NUMBER_OF_ENABLED_STATIC_RULESETS:启用静态规则集个数上限。
  • 全局上限会变化,用 getAvailableStaticRuleCount 查当前可用数。
  • MAX_NUMBER_OF_DISABLED_STATIC_RULES:禁用的静态规则另受此限制,但仍计入 GUARANTEED_MINIMUM_STATIC_RULES。
  • 动态 + session:Safari、Chrome <= 119、Firefox <= 127 用 MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES;Chrome 120、Firefox 128 起改用 MAX_NUMBER_OF_DYNAMIC_RULES 与 MAX_NUMBER_OF_SESSION_RULES。
  • MAX_NUMBER_OF_REGEX_RULES:正则规则单独限额。

测试 API 与权限

testMatchOutcome、getMatchedRules、onRuleMatchedDebug 需要 declarativeNetRequestFeedback 权限。Chrome 只在 unpacked 扩展里可用;Firefox 要把 extensions.dnr.feedback 设为 true(about:config,或 web-ext --pref)。

权限本身二选一:declarativeNetRequest 或 declarativeNetRequestWithHostAccess。前者会出现在权限提示里,后者不出现。declarativeNetRequest 允许在没有 host permissions 的情况下 block 和 upgrade 请求;要 redirect、改 header,或使用 WithHostAccess,则需要 host permissions。除导航请求(main_frame / sub_frame)外,还需要请求发起者(initiator)的 host permissions。

有几类请求不参与匹配:特权浏览器请求、受限域名、来自其他扩展的请求。

和 webRequest 的取舍

DNR 在浏览器内评估,比 webRequest 每个请求都在扩展进程里跑 JS 更高效;请求不再经过扩展进程,也就不再需要 background page;用 declarativeNetRequest 做 block / upgrade 不需要 host permissions;扩展不读取用户的网络请求,隐私更好。

行为上还有几个差异值得记:Chrome 下用 DNR 拦截的图片、iframe 会在 DOM 中自动折叠;DNR 优先于 webRequest 被评估(同步拦截),被 DNR 删掉的 header 对 webRequest 扩展不可见。webRequest 保留的优势是灵活性——可以编程式判断请求,DNR 表达不了的逻辑仍得靠它或别的机制。

另外,content scripts 访问不到页面 JS 作用域:共享 DOM 但不共享 window,需要 window.postMessage,或通过 web_accessible_resources 注入 script 标签来通信。

推荐文章

程序员茄子在线接单