先说结论:扩展被浏览器按灭这件事,跟商店里有没有它这个条目,是两条互不相干的链路。 唯一的交点是更新检查那一下。这几天「8 月 31 日清空 MV2」被转成「所以插件都不能用了」,中间那个「所以」是假的。
判据不在新闻里,在这行 JSON 里。打开 chrome://extensions-internals,找到那个灰掉的扩展,它的 disable_reasons 长这样:
json
{
"id": "............",
"state": 0,
"disable_reasons": ["DISABLE_UNSUPPORTED_MANIFEST_VERSION"],
"manifest": { "manifest_version": 2, "...": "..." }
}
state: 0 是停用,原因写得很直白:清单版本不受支持。它不是坏了,是被按版本判死的。 这个页面上没有任何时间字段,所以「哪天被按灭的」在这儿查不到------那得从浏览器更新那头倒推,这条口径首篇立过,不重复。
下面按报错 → 定位 → 解法 → 原理走。原理那一段是这篇的主体。
一、定位:三层,各在各的地方取证
| 层 | 发生在哪 | 怎么取证 | 判据 |
|---|---|---|---|
| 禁用 | 浏览器本地,加载扩展时 | chrome://extensions-internals 读 disable_reasons |
挂着 DISABLE_UNSUPPORTED_MANIFEST_VERSION 就是清单版本判死 |
| 下架 | 商店服务端,条目状态 | 条目还在不在 | 只影响拿包与拿更新,装上的照跑 |
| 装不上 | 你那条路 | 这条路依赖商店条目还是一个包文件 | 依赖条目的断,依赖包的还在 |
先把自己这台的底数摸清楚。扩展在磁盘上的落点是 <个人资料路径>/Extensions/<扩展 ID>/<版本号>_<序号>/,每个版本目录里一份 manifest.json。个人资料路径从 chrome://version 那一行读,别照抄默认串------多 profile、公司域重定向、Beta / Canary 各有各的根。
macOS / Linux:
bash
find "<个人资料路径>/Extensions" -maxdepth 3 -name manifest.json -exec python3 -c \
'import json,sys; d=json.load(open(sys.argv[1])); print(d.get("manifest_version"), sys.argv[1])' {} \;
Windows:
powershell
Get-ChildItem "<个人资料路径>\Extensions\*\*\manifest.json" |
ForEach-Object { "$((Get-Content $_ -Raw | ConvertFrom-Json).manifest_version)`t$($_.FullName)" }
说实话,这两条命令我给的只是写法和读法,不报「我这儿扫出几个」------这一轮没在三台机器上各跑一遍,没跑过的读数不写。
对了,顺手记个坑:想在输出里带一列扩展名字是带不出来的。manifest.json 里 name 多半是 __MSG_extName__ 这种占位符,真名在同目录的 _locales/<语言>/messages.json。所以上面只打路径,扩展 ID 拿去扩展页对更快。
二、原理一:禁用是本地的,下架是服务端的
禁用 发生在扩展加载的时候。浏览器读到 manifest_version 是 2,按当前版本的策略给这个扩展打上一个停用原因,写进 profile。这个过程从头到尾在你这台机器上完成,不发一个包出去。所以:断网也照样被按灭,商店条目好好的也照样被按灭。
下架 发生在商店那边。条目状态变了,影响的是两个动作------拿包 (安装 / 重装)和拿更新。装上的那份代码在你磁盘上,商店改不了它。
官方那三句原话恰好把这件事说全了:
All remaining Manifest V2 extensions are removed from the Chrome Web Store.
Previously installed MV2 extensions will remain installed...
...but will be unable to receive any updates and cannot be reinstalled.
第一句是货架,第二句是你的磁盘,第三句才是这一天真正的内容:拿不到更新,移除之后装不回来。
话说回来,时间线上真正的分水岭也不是这一天。官方那份 MV2 弃用时间线里:2024 年 10 月 9 日 Stable 开始逐周禁用;2025 年 3 月 31 日全渠道默认禁用,但用户还能临时开回来 ;2025 年 7 月 24 日,Chrome 138,全渠道禁用且开不回来,给企业留的那条策略随 139 一起消失。运行时那一层,一年多前就锁死了。
三、原理二:两条链路唯一的交点是 update check

既然禁用在本地、下架在服务端,它们在哪儿碰头?在更新检查。
Chrome 会周期性地拿扩展的 update_url 发请求,带上扩展 ID 和当前版本;服务端回一份 gupdate XML,里面 codebase 指向新版 crx 的地址、version 是新版号。商店托管的扩展,这个 update_url 指向的是 Google 那个 service/update2/crx 端点。
条目没了,这个请求就永远拿不到新版本,版本从此冻结。 这就是「unable to receive any updates」那句话在协议层的样子。
关键在于:自托管走的是同一套协议。 你自己写一份 update.xml 挂在内网上就行:
xml
<?xml version='1.0' encoding='UTF-8'?>
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
<app appid='你的扩展ID'>
<updatecheck codebase='https://内网地址:8443/xxx.crx' version='1.4.2' />
</app>
</gupdate>
所以在这五条路里,策略强制安装是唯一一条你能自己当上游的路。首篇那个 Go 写的十行静态服务就是干这个的:
go
mime.AddExtensionType(".crx", "application/x-chrome-extension")
http.Handle("/", http.FileServer(http.Dir("./dist")))
log.Fatal(http.ListenAndServeTLS(":8443", crt, key, nil))
证书用公司内网 CA 签。mime.AddExtensionType 那行不能省------不加的话 crx 被当成 application/octet-stream 发出去,浏览器那头不认。
这里得交代我踩过的最蠢的一个坑,因为它跟这段代码绑在一起:FileServer 的根目录挂到了 ./dist,于是我请求 /update.xml 的时候,它去找的是 ./dist/update.xml。我盯着 404 想了很久是不是策略没下发。
还有一条至今没结论 的:策略文档说 update URL 支持 http / https / file,但自托管那份文档要求 codebase 指向的 crx 走 HTTPS。我这边全 http 那次没跑通,怀疑卡在 codebase 这一层,不过我没补跑过,所以现在也不给结论。8 月 31 日之后这条路的分量涨了------商店那头拿不到包,自己当上游就从「优雅」变成了「唯一」------所以它值得谁去补一次。我这边补上了再写。
顺便说一句,别忘了未打包扩展的 ID 是拿目录绝对路径算出来的,挪目录就等于换 ID(算法那篇我上周写过,这里不重讲)。留包的时候连目录一起留,不然你留下来的东西装回去是另一个扩展。
四、原理三:Edge 那边多一个不随迁移消失的取值
该盯的其实是 Edge。微软 8 月 7 日那篇讲扩展生态的博客写得很清楚:
Beginning in August 2026, Microsoft Edge will start the consumer transition away from Manifest Version 2 (MV2) extensions.
先 Canary / Dev / Beta,随后几个月推到 Stable,目标年底之前收完消费者侧;企业侧顺延到 2027 年初。同一篇给了三个数:加载项站上头部 MV2 扩展 95% 已迁 MV3,仍有可观使用量的 MV2 只剩 58 个 ,其中只有 3 个没有公开可用的 MV3 版本。
这不是断供,是收尾。 58 个里 55 个换个条目重装一次就完事,真正没退路的是那 3 个。
真正的增量在策略这边。Edge 控制 MV2 可用性的那条企业策略有四个取值:
| 值 | 名字 | 效果 |
|---|---|---|
| 0 | Default |
跟浏览器默认行为走 |
| 1 | Disable |
禁止安装 MV2,已装的停用 |
| 2 | Enable |
放行 MV2 |
| 3 | EnableForForcedExtensions |
只放行被强制安装清单推下来的 MV2,其余一律停用 |
第 3 个取值下面,文档写了这么一句:
The option is always available regardless of the manifest migration state.
对照 Chrome 侧那条随 139 消失的同名策略,这句的分量就出来了:Edge 这个取值,文档明写它不随迁移进度消失。
落地就是两处。Windows 走注册表:
makefile
路径: SOFTWARE\Policies\Microsoft\Edge
键名: ExtensionManifestV2Availability
类型: REG_DWORD
值: 0x00000003
macOS 走同名 preference key:
xml
<key>ExtensionManifestV2Availability</key>
<integer>3</integer>
配套那份强制安装清单,条目格式是 <扩展ID>;<update_url>,update_url 就填你上面那份 update.xml 的地址。
免责照搬首篇,一个字不改:策略这条路我只在自己那套内网证书环境、已经加了域的机器上验过,公司和公司之间域策略、MDM 差别很大;个人 Mac 和没加域的 Windows 走不通。上面这个 Edge 取值我没在生产环境上验过,只读了文档------写的是「文档这么写」,不是「我试过能行」。
五、解法:五条路,靠条目还是靠包

判据一句话:这条路依赖的是商店条目,还是一个包文件。
| 路径 | 首次耗时 | 依赖 | 8-31 之后 |
|---|---|---|---|
| A Edge 加载项商店 | 40 秒 | 条目 | 不受影响;但它自己年底到期 |
| B 官网离线包侧载 | 3 分 20 秒 | 包 | 照走。但 MV2 的包装得上跑不起来 |
| C 策略强制安装 | 首台 25 分钟 / 第二台 4 分钟 | 包 + 策略 | Chrome 侧随 139 已死;Edge 侧见上一节 |
| D 国产扩展中心 | 1 分钟 | 别人的条目 | 跟上游走,版本可能永久停在旧的 |
| E 第三方 crx 站 | 2 分钟 | 别人存的包 | 价值涨,风险同涨(抽 6 个有 2 个跟官网原包对不上) |
| F Brave 自托管 | 没走过,不给数 | Brave 存的包 | 只有四个名字:AdGuard AdBlocker / NoScript / uBlock Origin / uMatrix |
B 那行的加粗值得单说一句:包能拿到不等于装上能跑。禁用发生在运行时那一层,跟包是从哪儿来的一点关系都没有------你从官网下的原包,在 138 之后的 Chrome 上照样起不来。
顺手把回本账重算。首篇立的口径是分子分母任一个变了就得重算,不许保留结论值偷换分母。原式:策略路径首次 25 分钟 ÷ 离线包每月维护 5 分钟 = 5 个月。现在对 MV2 来说,每月那 5 分钟买不到「它还活着」------分母不是变小了,是没了,一笔买不到东西的支出不能当回本的分母。所以 5 个月这个结论只对 MV3 保留;对 MV2,Chrome 上 B 和 C 一起归零,Edge 上只剩策略那条。这笔账在这儿作废重立,不是改个数接着用。
六、速查:四个症状,四种判法
| 你看到的 | 大概率是哪一层 | 下一步查什么 |
|---|---|---|
| 扩展页卡片灰着,开关点不亮 | 禁用 | extensions-internals 的 disable_reasons 有没有 DISABLE_UNSUPPORTED_MANIFEST_VERSION |
| 卡片是亮的,但功能不响应 | 都不是,是运行时故障 | 走故障归属那套,不是这篇的题 |
| 商店搜不到,本地还在跑 | 下架 | 什么都不用做,但重装路径归零,去留包 |
| 装的时候直接失败 | 装不上 | 你这条路依赖条目还是包,对照上面那张表 |
第二行不属于本篇:卡片亮着但不干活,那是运行时的事,判法另有一套。
收尾
扩展页上灰掉的那几张卡片,把上面那行提示文案原样贴到评论区------尤其是 Edge 上的,它跟 Chrome 的措辞不一样,我手上只有 Chrome 那版。下次更新按出现次数排一排收进表里。
update.xml 全 http 那条悬案,谁在自己内网上补跑过的,把 codebase 那行和结果一起贴过来,我一直没结论。