周三下午,我写完一篇关于分布式系统设计的新文章,打算同步发到X、LinkedIn和Instagram。像过去几年里的每一次一样,我打开新标签页,找到我们的产品后台,登录,从文章页面复制URL,粘贴进输入框,勾选三个平台账号,点击发送。整个过程切换了四五次页面,手离开键盘好几回,明明文章写完只需要一瞬间的分享冲动,却被这些琐碎步骤生生打断。我盯着屏幕,想:能不能就一个按钮?
大多数人电脑里都装着几个甚至十几个浏览器扩展。一个普通的Chrome用户平均安装了八到十二个,真正每天用的不过两三个。广告拦截器、密码管理器,还有一个三年前为某个特定任务装的,从来没更新过,也从来没删掉。扩展这种东西,价值就在于消灭上下文切换——你不用打开新标签、找到服务、登录、粘贴链接,而是点一下图标,事情直接在当前页面完成。少了那五六个小步骤,省下的不是时间,是注意力。没装的时候觉得没什么,一旦用上就回不去。
我每天写文章然后在多个社交账号上分发,这个流程已经重复了成百上千次。于是我决定自己做一个扩展:点一下图标,自动抓取当前页面的URL,在弹出的窗口里写上推文,选择要发送的账号,一键发布,全程不离开当前页面。某种意义上,这就是一个把后台裁剪到只剩一个按钮再塞进浏览器工具栏的产品。整个扩展打包完只有十七千字节——比一张中等画质的手机照片还小。
但发布的过程远比写代码本身“精彩”。在随后大约两周时间里,我们把这个十七KB的压缩包先后投递到三个浏览器的扩展商店,收到了两份拒绝、一场HTTP 400导致的团队锁死,以及一次秒级批准。下面就是这段旅程的完整记录,包括我们收到的审核反馈原文。
这个扩展的技术骨架非常简单:Manifest V3规范、一个弹出的窗口页面、少量用于身份认证和发布请求的逻辑。当用户点击图标时,扩展从我们产品已登录的标签页中读取令牌,用它交换一个API密钥,然后存进chrome.storage,这样用户不用手动复制粘贴任何东西。我们最终的manifest文件也就这么几行:声明版本3,写上名字和版本号,请求storage权限,再指定popup页面。没了。甚至没有声明activeTab,因为我们不需要读取当前页面的内容,只需要标签页的URL——而那是所有浏览器默认就提供的。
更有意思的,是那些没写在manifest里的东西。比如说,我们没有申请任何一个远程请求权限,也没有写后台脚本。整个扩展没有任何持续运行的逻辑,所有的网络请求都只在用户点开窗口并且主动点击发送时才发生。换言之,它比市面上大多数天气查询扩展还要“安静”。
第一次提交给Chrome网页商店后,我们很快就收到了审核结果:拒绝。原因分类写着“垃圾信息和展示位置 / 过度使用关键字”(Spam and Placement / excessive keywords)。当时我愣了一下——这个扩展总共才17KB,连像样的界面都没有,上哪儿塞关键词去?打开开发者后台仔细一看,问题出在商店详情页的描述里。我们在描述中老老实实地列出了这个扩展支持的全部十个社交平台:LinkedIn、X、Instagram、Facebook、Reddit、Mastodon、Threads、Bluesky、TikTok、Pinterest。初衷很简单,让用户一眼就能知道“你可以通过这个按钮发到哪些地方”,这是一种对产品能力最直白的交代。
但Chrome的自动化审核系统不这么看。根据Chrome网上应用商店的开发者政策,关键字堆砌被定义为“在描述中堆砌不相关或过多的关键字,试图操纵搜索结果排名”。在纸面上,我们的描述完美踩中了这个定义:十个响当当的品牌名挨着排在一个短描述里,无论你的本意多么天真,在算法眼里都像一次拙劣的SEO尝试。当一个产品真真切切支持十个平台的时候,把你支持的平台名字诚实地写在一起,看起来却和关键字刷子没什么两样。
这件事让我重新理解了“关键字堆砌”这条规则的实际边界。一家创业公司的产品覆盖了十个主流社交网络,这本身就是它的卖点,而卖点在描述中的自然呈现,竟然与垃圾信息共用同一组视觉特征。审核系统没有上下文,它只看模式;而我们的模式,恰好是一个“试图用品牌名劫持搜索流量”的典型案例该有的样子。于是我们改写了描述,把那十个具体的平台名换成了“你所有已连接的社交账号”这样一个概括性说法,然后重新提交。
当时我们乐观地以为,问题解决了。但很快,第二封拒绝邮件就来了。这次的理由依然和描述有关,审核团队指出我们仍在用某些方式暗示功能的广度,哪怕我们已经把品牌名全部剔除。后端的提示很简洁:“描述中包含暗示与多个知名平台集成的夸大内容”。这意味着不是关键字本身,而是“集成的大平台数量”这种表达本身也被视为一种擦边的推广形式。这次我们彻底明白了:在一个以极简主义为哲学、用最少权限干最聚焦的事情的扩展世界里,甚至你说话的广度都会触动警报。我们不得不再次修改描述,将对功能的预期缩小到“分享当前页面的链接到你绑定的账号”,连“多个平台”都不再提及。
就在第二次修改提交后的那个下午,团队里一个同事突然发现自己打不开Chrome的开发者仪表盘了。紧接着第二个同事也遇到了同样的问题:访问Chrome网络商店开发者的审核页面时,系统返回的是一个光秃秃的HTTP 400 Bad Request,没有更多解释。尝试清除缓存、换浏览器、切换Google账号,全部无效。我们有一半的开发人员被锁在了审核流程之外,连提交更新都做不到。那一刻整个团队就像被关在一间没有窗户的房间里,不知道外面发生了什么,只知道门锁了。我们在谷歌的社群支持论坛上找到无数条类似的反馈,看起来HTTP 400并不是偶发故障,只是谷歌不觉得这是个需要公开回应的问题。
与Chrome形成鲜明对比的,是Firefox。我们在解决Chrome第二次拒绝的同时,顺手把同样的包提交到了Firefox附加组件中心(Firefox Browser Add-ons)。提交表格填起来和Chrome差不多,只是流程更短。当天晚上,我们收到了审核通过的邮件,耗时不到七个小时。没有关键字审查,没有描述警告,甚至没有对权限做任何追问。审核团队只是确认了扩展确实只使用了storage权限且没有执行远程代码,然后就放行了。整个过程快得让人产生一种自己写的代码其实已经被一个更宽容的平行宇宙接收的错觉。
这七个小时的对比,让我不由得去看了一眼各个商店的体量。根据公开数据,截至2026年7月18日,Chrome网上应用店拥有283,585个扩展,比今年6月中旬Microsoft Edge的30,509个多出了将近十倍。Firefox这边,今年早些时候的统计口径大约有83,000个。如果按活跃用户数来估算,Chrome的扩展数量是Edge的近十倍,是Firefox的三倍多。体量越大,审核压力自然越大,自动化的过滤机制也越严苛。然而,体量并不能完全解释:一个没有远程权限、存储只用来放API令牌、界面简单到只有一个文本框和几个勾选框的扩展,为什么会被反复判定为“垃圾信息”。
更能说明问题的或许是扩展生态
热门跟贴