2026-06-06 11:06:40
早上习惯性刷 RSS 订阅,看到了宗宗酱发的一篇提醒:你的RSS订阅源有跳菠菜的风险
心里一惊,赶紧翻开自己博客的 Feed 页面扫了一眼。果不其然,根标签里赫然躺着这一行:

wellformedweb.org:黑产是如何盯上 WordPress Feed 的因为是个历史包袱,我就不细说了,你只要知道这个东西是上古时期用来“让读者在RSS阅读器里,能给博客发表评论”用的就行了,这是 RSS 规范的一部分,但这玩意对垃圾评论的防御力几乎是零,所以在十多年前就已经被时代无情淘汰,但没办法啊,为了绝对的向后兼容, WordPress 依然将这行代码原封不动地保留至今。。
最近 wellformedweb.org 域名过期了,就被黑产团伙抢注了。虽然 WordPress 只是把它写在“XML 命名空间”里,WordPress 本身在生成 Feed 时,绝对不会去这个网址下载任何代码。正常 RSS 阅读器也不会去访问这个网址,除非有人直接看你的 Feed 源码,然后手动去点这个网址(能不能点开都两说,毕竟正常情况下此处并不会被渲染为超链接)。
但是云服务商、网管部门、第三方安全插件会无差别提取页面内的所有 URL 链接(哪怕它只是个 xmlns 命名空间里的字符串),这一扫,就发现这个 URL 指向了一个博彩网站,搞不好就直接判定你的网站“包含低俗、赌博等违规内容”,进而触发自动化报警,轻则发邮件警告让你限期整改,重则直接判定违规封禁主域名或IP。
以及不排除未来这个 URL 会进入全球恶意欺诈黑名单,这会导致有些阅读器或者开启了严格安全保护的浏览器,因为探测到你的订阅源含有这个URL,直接给访客弹个“该网站包含危险欺诈内容”的红色标签页警告,这不是纯纯的无妄之灾嘛,所以这行代码最好还是尽早删掉。
看了看网上给出的普遍解法,都是让人“直接去修改 wp-includes/Feed-rss2.php 文件,删掉这一行”。
这就有点不够优雅了,动系统文件向来是慎之又慎的事情。一个不小心直接改炸了不说,而且一旦 WordPress 升级,修改搞不好就被直接覆盖了,属于治标不治本。
那既然不打算动核心文件,那就只能考虑动态移除。最显而易见的修改方案有两个:
所以我决定用 PHP 搞定问题。跑去问了下 AI 这个标签的钩子是啥。结果 AI 一摊手:硬编码,没钩子。
啊,这……行吧,既然你不给钩子,那就别怪我直接对输出缓冲区下手了。
在 Code Snippets 插件中新建一个代码段(选择在所有位置运行),或者写到你当前主题的 functions.php 文件中也行。利用 PHP 的 ob_start 在页面渲染完成、输出前的最后一刻,把这个 URL 给滤掉。
PS:更新,经评论区提醒,WordPress不仅会在 RSS 订阅源开头声明 wfw ,在后面每篇文章的结尾还有生成RSS评论区的代码:<wfw:commentRss>https://XXXXXX/feed</wfw:commentRss>。这部分也需要同步去除,不然只移除开头的命名空间,而不移除每篇文章的RSS评论区。 RSS 阅读器会一脸雾水,哪来的wfw啊,导致报错。
代码如下:
/**
* 移除 WordPress RSS2 中的 CommentAPI 命名空间及相关标签
*/
add_action('template_redirect', 'perfect_clean_rss2_buffer', 0);
function perfect_clean_rss2_buffer() {
// 仅对 RSS2 订阅源生效
if (is_feed('rss2')) {
ob_start('rss2_namespace_cleaner');
}
}
function rss2_namespace_cleaner($buffer) {
// 1. 匹配并移除开头的命名空间声明
$search_pattern = '/xmlns:wfw="http:\/\/wellformedweb\.org\/CommentAPI\/?"\s*/i';
$buffer = preg_replace($search_pattern, '', $buffer);
// 2. 匹配并移除每篇文章下的 <wfw:commentRss> 标签
$comment_pattern = '/<wfw:commentRss>.*?<\/wfw:commentRss>\s*/i';
$buffer = preg_replace($comment_pattern, '', $buffer);
return $buffer;
}
小提示:保存并启用代码后,如果刷新页面没变化,八成是有什么东西给你的Feed做了缓存,记得去后台刷新一下你的站点缓存
如果你不想动 PHP,且你的 Nginx 编译了 sub_filter 模块,也可以直接在 Nginx 配置文件中加入以下规则动态替换:(让AI写的,我没实际测试过)
location ~* /Feed/?$ {
sub_filter '' '';
sub_filter_once off;
sub_filter_types application/rss+xml text/xml;
try_files $uri $uri/ /index.php?$args;
}
不得不感叹一句,最近黑产真的越来越钟情于这种“历史遗留域名过期被恶意抢注(内容劫持)”的连带攻击方式了。
他们利用了对老域名的盲目信任,不需要入侵你的服务器,就能白嫖全球无数独立博客的连带流量和权重,不可谓不鸡贼。在 WordPress 官方正式移除这行代码之前,建议各位站长,赶紧去排查并手动移除自己的 /feed 隐患
2026-05-27 10:42:06
前几天,我重构魔改后的扩展 TabulaBili-Plus 正式通过了谷歌严苛的 MV3 审计,在 Chrome 应用商店成功上架。原本看着后台不断增长的活跃用户数还挺开心的,结果前天点开评价区,发现迎来了一个一星评价。
这位朋友言辞有些激烈,大意是说我“山寨上游项目,有能力改了项目 bug 却不给原作者提 PR 修复,反而改个相似的名字上架商店,是在搞开源社区的分裂,是在山寨,没遵守原作者的要求在 UI 里保留他的博客”。
看到这个评价,我脑子里闪过的全是以前吃开源社区各种“二开、另立门户、互撕”大瓜的画面,万万没想到,这次自己成了瓜里的主角😂。虽然能感受到这位老哥对开源生态的热心,但他可能平时看热闹居多,对开源协议和协作体系还缺乏深入了解。他既没有耐心读完长篇的项目介绍,也没注意到我在 GitHub 和文章评论区里与原作者的互动,甚至连扩展的 UI 界面都没仔细看——不然他就会发现,UI里「🌟灵感来源」按钮正链接着原作者的博客。他自以为是在维护正统的“开源正义”,却因为对现代开源社区的运作机制认知不全,直接血气上涌,A 了上来。
首先声明一下,我今天写这篇文章,绝对不是为了挂人或指责这位老哥(我向来不喜欢网络暴力和挂人,请大家权当看个趣闻,切勿寻找这位朋友)。相反,他给我送来了一个绝佳的博客选题:在看似自由的开源世界里,到底怎么做才算既符合开源协议的规则、又符合开源圈子的人情世故?
借着这个由头,科普一下:常见的开源协议是什么?如何正确地 Fork 二开?二开和原作者的责任边界在哪里?当你为自己的项目选择某个开源协议时,当你将软件推向泛大众使用时,你到底得到了什么,又放弃了什么?
那堆密密麻麻、翻译得像绕口令一样的开源协议文本,让人一看就头大。所以我就不干巴巴地列举条文了,而是用开发者对待项目的心态来做分节标题。方便大家写完工具准备开源时,只需要对照自己属于哪种心态,就能直接对号入座:
MIT 协议TabulaBili-Plus)、原项目(TabulaBili)、Vue、React。GPL 协议(GPL v2 / GPL v3)Linux 内核、Git、WordPress。💡 小科普:GPL v2、v3 和 AGPL 之间到底有什么区别?
- GPL v2 的漏洞(以 TiVo 化为例):当年机顶盒厂商 TiVo 非常遵守
GPL v2,大方开源了自己魔改的 Linux 内核。你确实能自由下载和修改代码,但当你把改好的固件刷回机顶盒时,硬件会检测数字签名,不是官方签名直接拒绝开机。 代码表面上开源了,但用户其实改不了一点,硬件直接锁死,形同虚设。- GPL v3 的反击:自由软件基金会(FSF)被这种“合规不合理”的行为激怒了。于是在
GPL v3中规定:如果你的硬件运行了GPL v3软件并面向消费者,你必须无条件提供修改该软件并使其在硬件上运行所需的全部密钥和数字签名。这也是为什么 Linux 之父 Linus 坚定让 Linux 留在 v2,而大厂对 v3 十分抗拒的原因。(比如最近拓竹的核心切片软件Bambu Studio,由于 Fork 自PrusaSlicer,其内部的bambu_networking模块却闭源,且作为软件与硬件正常运行中不可或缺的核心组件被强制捆绑,因而惹上了官司,深陷开源合规的舆论风波。)- 专利防御:
GPL v3还自带“专利反击盾”,规定任何贡献代码的人视同自动向后续用户授予相关专利使用权,防止有大厂玩“先开源项目、之后反手告用户专利侵权”的流氓套路。- 违规宽限期:
GPL v3拥有 30 天的“违规宽限缓刑期”,比 v2 的“一违规立刻永久剥夺授权”要温和得多。比如二开时不小心在 License 里写错了原作者的名字,在 v2 下你会被立刻永久剥夺授权,而在 v3 下你只要在被提醒后的 30 天内补上就行。- GPL 的漏洞与 AGPL 的反击:根据 GPL 的要求,当用户获得你的程序时,你必须让他们能够获得源码。但这并不代表你必须“无偿”提供程序本身(比如红帽 Red Hat)。其次,把 GPL 软件部署在自己的服务器上,通过网络接口给用户提供服务的话,因为根本没有把软件“分发”到用户的电脑里,就能完美绕过了 GPL 的开源约束。AGPL 专门堵的就是网络服务这个场景漏洞。
Apache 2.0 协议Android、TensorFlow、Apache。MIT 多了一层极其严密的官方专利授权保护条款:谁要是给项目贡献了代码,就视同他自动免费授权了贡献部分的专利使用权;同时它明确保护了作者的商标和专利,是很多商业大厂的首选。BSD 协议Go、Python、Nginx、PostgreSQL、React(早期)。BSD 和 MIT 非常相似,但它比 MIT 更在乎“名誉和责任”。BSD 多了一条“铁律”——绝对不允许用原作者或者原项目的名字来给你的衍生产品做商业背书或打广告(除非得到书面许可)。这就叫“二开归二开,各凭本事,别蹭劳资名气”。很多知名的独立开发者和开源组织会选择这个协议,以防品牌被滥用。| 开源协议 | 是否允许闭源/商业化 | 是否必须保留原作者署名 | 是否有严密的专利保护条款 | 是否允许二开带有原项目/作者名 | 核心特征描述 |
|---|---|---|---|---|---|
| MIT | 允许 (最佛系自由) |
必须保留 | 否 (仅提供基础免责护甲) |
允许 (拥有修改和更名权) |
“你爱咋用咋用,保留署名、出事别找我” |
| GPL | 绝对不允许 (强传染性) |
必须保留 | 是 (GPL v3 自带专利反击盾并防硬件锁死) |
允许 (但衍生项目必须无条件向全网开源) |
“谁也别想白嫖去闭源,沾上就必须开源” |
| Apache 2.0 | 允许 (最适合商业化) |
必须保留 | 是 (提供极其严密的官方专利授权保护) |
明确保护作者的商标与专利,不可滥用 | “可以商用,但谁贡献谁就得自动免费授权专利” |
| BSD | 允许 | 必须保留 (且修改文件必须写声明) |
否 | 严厉禁止 (除非得到明确许可) |
“二开归二开,各凭本事,绝对不准蹭劳资名气” |
回到开头那位老哥的指责:“改个相似的名字上架,是在搞山寨和破坏开源精神”。这恰恰暴露了他对开源世界中“责任划分”与“人情世故”的认知模糊。
原作者在 GitHub 仓库采用的是最自由的 MIT 协议。这意味着在法律层面上,任何人完全拥有修改、更名和独立分发的权利,无论下游用,亦或不用原项目的名字,都是完全合规的。但比合规更重要的,是现代协作中的“责任隔离”。
在这次重构中,为了解决极端弱网、冷启动以及状态同步等一系列边界坑,我引入了新的 content.js 自动化脚本、MutationObserver 弱网防误刷新机制、以及本地 Storage 状态持久化。虽然核心的 DHR 机制和界面几乎没变,但上层的运行机制、权限和逻辑已经被我魔改了一大半(从代码量评估,基本上是从 100 行扩展到了 200 行)。
如果我不改名字,还厚着脸皮叫原名 TabulaBili 往商店一扔,UI 里还用大字链接到原作者的仓库和博客,一旦 B 站将来大改版导致页面白屏或功能失效,成百上千根本不懂技术的普通用户,就会顺着这个名字摸到原作者的个人博客和 GitHub 仓库下面,给原作者的留言板和 Issues 区添堵、催更甚至责骂。 这对于本想安安静静写点代码、明确在 README 里声明了“没有上架应用商店计划”的原作者来说,简直是无妄之灾。这也是为什么我第一次尝试用原名上架谷歌商店时会被官方直接打回。因为在应用商店的规则里,二开项目不改名,反倒有可能会被判定为恶意仿冒和混淆的违规行为。
改名为 TabulaBili-Plus,在扩展界面底部感谢原作者的灵感,在项目介绍、商店介绍里全都特意致敬原作者,同时把反馈链接改到我自己的仓库——把所有因为二开带来的 Bug、吐槽和维护售后等“杂七杂八”的事务全揽在自己身上,把纯粹的感谢留给原作者。这不仅不叫分裂,反而是一种“责任切割”。 我发布的版本我来负责,大家可以去给原项目点 Star、留言感谢原作者的创意,但别因为我的二开中的错误去打扰原作者的清净。
虽然在 MIT 协议下,法律并不强求在扩展 UI 内保留作者博客信息,但本着开源圈子的“人情世故”,我依然认为应该在不让用户产生混淆的前提下,尽可能在推广和介绍里保证有致谢原作者的部分。
那位老哥另外执着的一点在于:“你既然改了 bug,为什么不给原作者提 PR 修复,非要自己独立搞一套?”
其实,提不提 PR(Pull Request),在软件工程里有着非常严密的逻辑边界。
以 TabulaBili 为例,原作者在 README 中表达得非常清晰:保持极致的轻量,不需要常驻后台进程,也不想在页面注入复杂 DOM。而我的 Plus 版为了彻底解决 B 站首屏 SSR 的推荐直出问题,必须引入 Content Script、MutationObserver 和 Storage 权限,往 B 站首页注入了脚本。
这就像我最近在魔改一个关于 GPT-image2.0 绘图的项目,为了让同事们能在工作中开箱即用,我加入了“后端内置 API KEY”和“后端内置图片反代”两个功能。而这两个设计,是原作者在 Issues 相关讨论中明确表示反对的。
如果我把这种与原项目设计理念明确冲突的代码打包成 PR 强行塞给原作者,无异于在逼着原作者去审核他本来就不想要的东西、如果他合并了,未来就为一部分他本不想要的代码承担未来的维护责任。这不叫开源奉献,这叫对原作者精力的道德绑架。
如果真的感觉自己的 PR 很有意义,但和项目现有理念冲突了,感觉最好还是先提个 issues 问问维护者的意见,直接一个 PR 怼上去就有点冒犯了,除非项目本身就建议直接提 PR。
至于老哥指责的“不给原作者提 PR”,事实上,我不仅提了 PR ,而且在重构之初就提交了。

在不破坏原作者核心架构、且对原项目有益的通用改动上(比如对域名过滤逻辑的优化、对界面文字排版重叠的修复),我早已向上游提交了代码。但开源作者的精力和时间都是有限的,上游作者由于生活工作忙碌没有合并,或者主观上不想合并,这在开源世界里都是再正常不过的常态。我们不能躺在地上死等对方合并,或者无休止地去催促对方,“自己动手,丰衣足食”才是真正的开源效率。
PS:当然,如果一个有很多用户的项目,存在重大安全漏洞(比如最近经常发生的供应链攻击),那该催的时候也要催。
一个开发者,把自己的代码锁在本地自用、放到 GitHub 默默开源,以及将其包装后推向大众,这三者之间到底有着怎样的“得与失”?
在代码只属于自己的时候,是最自由的。代码写得再烂、逻辑再脏、全是硬编码的 API Key,都无所谓,只要能跑通就行。不需要考虑极端场景触发bug,因为可以注意不去触发,不需要写长篇的 README,只要适当留下注释能让未来的自己看懂就行,更不用去理会谷歌那严苛到掉头发的 MV3 审核,开发者模式下根本没人管你申请什么权限。这是最纯粹的效率,但代价是,自己的作品永远只是一个孤芳自赏的局部工具。
当你把代码上传到 GitHub,代码就有了生命。在这个阶段,你的受众主要是同行或折腾能力较强的极客。他们看得懂源码,能通过 Issues 给出建设性的 Bug 反馈,甚至会直接给你提 PR 帮你修代码。在这个阶段,开源是快乐的,圈子是小而精的,信息熵极高且充满善意。平台自身的门槛阻止了大部分低质量的讨论,虽然也有一些争吵,但基本都是互相摆证据的论述。但你的软件还是被局限在一个不太的的圈子里,除非你的项目能被大厂盯上,但大厂又九成会直接抄走不谢。
一旦你决定将它“彻底推向大众”,事情的性质就完全变了。
把代码放出来是“利他”的分享,但把它包装好推给大众,则是一场用个体精力去对抗系统复杂性的修行。你获得了影响力、技术成长甚至一些利益的同时,就必须让渡一部分清净,去接纳那些不那么完美的反馈。
以TabulaBili-Plus为例,这个扩展的上架后
回到最初的起点。既然把代码包装好推向大众需要经历这样的“修行”,那为什么我们依然对开源乐此不疲?
因为开源的魅力,恰恰在于基于规则和协议之上的“野蛮生长”与“无限可能”。如果没有人愿意分享,开源世界只是一片死水;而如果所有人分享后都选择偏安一隅、拒绝破圈,那好项目就永远无法惠及大众。
在这场协作里,原作者 @wangdaodao 凭借敏锐的直觉给出了极其优秀的底层拦截灵感,我遵循 MIT 协议接过接力棒,在此基础上完成了商店应用的工程化落地。这种“前人栽树,后人改造,各司其职,责任自负”的交接,正是开源最迷人的地方。(原作者 wangdaodao 做出了 TabulaBili ,在此基础上有了 TabulaBili-Plus,在Plus的基础上,又诞生了Firefox的TabulaBiliFirefox;还诞生了B站三方客户端 BiliPai 的插件,一个灵感就衍生出了至少4个开源项目)。
代码是自由的,协议是严肃的,责任是明确的,而人情世故是有温度的。开源社区从来不是靠在键盘上扣“正统”和“三方”的帽子发展起来的,而是靠一行行合规、合理、互相尊重的代码堆叠起来的。
最后,还是要再次感谢那位打一星的老哥。谢谢你用一个刺眼的差评,送给了我一个绝佳的文章选题,让我能和大家聊聊这些关于开源、规则与成长的小话题。
本文 别用“开源正义”道德绑架了!聊聊二次开发的开源协议、责任边界、人情世故 最初发表于 秋风于渭水。
2026-05-23 10:07:16
麻了已经,毁灭吧,赶紧的,要是安卓也能这么容易拿到 root 就好了,可惜这 5 个漏洞貌似对安卓都无效
好消息:目前除了Linux 第5漏洞: 「PinTheft」 暂时没补丁,其他都已经修复了,正常更新就可以打上补丁
坏消息: 估计有些前两天刚把集群的 Nginx 升级到1.31.0/1.30.1的运维,又要周末加班了。
# Ubuntu, Debian
sudo apt update && sudo apt upgrade -y
# Rocky, Alma, RHEL
sudo dnf upgrade --security -y
# Alpine
apk update && apk upgrade
sudo apt update && sudo apt --only-upgrade install nginx -y
# 进入你的 LNMP 安装包目录(以你下载的版本号为准)
cd /root/lnmp2.0/
# 运行升级脚本,指定升级目标为 nginx
sudo ./upgrade.sh nginx
# 脚本会告诉你现在的版本,并问你要升级到什么版本,正常输入稳定版的版本号就行,比如目前是1.30.2
Current Nginx Version: 1.30.1
Please input next Nginx Version: 1.30.2
# 哦,对了,如果你有第三方模块记得提前改`lnmp.conf`
2026-05-22 11:57:26
如今打开小破站,首页推荐的内容越来越精准,但也越来越让人感觉无趣了。
不小心点开了一个机械键盘的评测,好家伙,接下来起码一个星期,首页全是各种轴体、套件和客制化的评测和介绍视频。其实我只是为了看封面图里的那只猫才点进去的,我不想看键盘啊!——更让人哭笑不得的是,你刚看了一个 UP 主的视频,还不小心又多看了一个,算法就会贴心地认为你特别喜欢这个 UP 主,只是还没关注,于是主动把该 UP 主以及其他发过同类型、甚至观点大同小异的 UP 主的视频铺满你的首页。
这种看似“投其所好”的机制,在产品经理的口中叫作“精准用户画像”,但在用户眼里,它有一个更麻烦的名字——信息茧房。
| 推荐流类型 | 优点 | 缺点 |
|---|---|---|
| 个性化推荐 |
极其精准 算法会根据你几百条的浏览和播放历史,死死拿捏你的喜好,属于温水煮青蛙式的“真香”。 |
视野日益狭窄 你很难在首页接触到你认知以外的新世界,算法只让你看它以为你想看的东西。 |
| 公共热门流 |
充满未知与随机 是整个 B 站当下最真实的、没有任何用户画像过滤的公共热门内容。你能刷到平时根本不会主动搜索的领域。 |
缺乏针对性 因为不知道你的喜好,首页会混进一些你不怎么感冒的泛娱乐大热门(比如美妆、吃播、影视切片、玩抽象等)。 |
为了摆脱算法的精准推送,我以前经常用隐私模式去看 B 站,去看看有没有不一样的东西。但没登录的代价非常明显:低清画质、无法发弹幕、不能随手点赞收藏,页面滚着滚着就会弹窗叫我登录,浏览体验极差。
难道我们就不能既登录账号享受 1080P 高清画质,又能看一眼干净、随机、没有被历史记录污染过的公共热门流吗?
当然能。这就要请出我今天给大家安利的主角了——TabulaBili-Plus(初见哔哩增强版)。
这是一款被我无意中发现并试用,因不满现有功能而动手二次开发,最终历经波折成功上架 Chrome 应用市场的浏览器扩展。

如果你也厌倦了被算法反复投喂同样的内容,想找回当年第一次打开 B 站时那种“人生若只如初见”的探索感,这个小扩展绝对能帮到你。
TabulaBili-Plus。TabulaBili-Plus 文件夹解压到一个你喜欢的位置。chrome://extensions/ 进入扩展管理页面。TabulaBili-v1.*.* 文件夹)。

💡 小提示:下面就是我的一些技术实现细节和个人吐槽了,想要“省流”的读者可以直接跳到文末去下载体验啦!
B 站后端之所以知道你是谁、该给你推什么视频,全靠请求头里携带的身份凭证——Cookie(包含你的 SESSDATA 等身份认证标记字段)。本扩展其实就做了两件事:
当插件的开关处于 启用(ACTIVE) 状态时,它会通过 Chrome 的 Manifest V3 声明性网络请求(declarativeNetRequest,简称 DNR,去广告扩展也会使用这个特性)来过滤流量。
每当浏览器准备向 B 站的首页推荐接口(api.bilibili.com/.../rcmd)发起异步请求时,插件会在请求发出的最后一刻拦截请求,精准地把请求头里的 Cookie 字段直接“移除”。
这样一来,B 站的后端首页推荐接口收到请求时,会认为:“这个人没有携带任何身份信息,他是一个刚刚清除过浏览器缓存、或是第一次访问 B 站的全新访客!” 于是,B 站服务器就会老老实实地把最纯净的、根据热门算法排序的首页内容返回给浏览器。
B 站为了提升首页的打开速度,采用了 SSR(服务端渲染) 技术。当你输入网址或按 F5 刷新时,第一屏的前 10 个视频数据是跟着主站网页的 HTML 一起打包直出的。好处是能减少浏览器绘制网页的时间,但对于咱们来说就麻烦了——这前 10 个视频一定是个性化推送的。由于我们不能直接拦截主站的 Cookie(否则账号就会全局退出登录),所以每次刷新后的首屏前 10 个视频依然会遵循个性化推荐。
我的 Plus 版解法: 插件引入了一个基于 MutationObserver 监听机制的本地内容脚本(content.js)。每当你打开 B 站首页时,它会在后台静默监视页面结构,一旦发现首页那个“换一换”按钮被渲染出来,就会在几毫秒内自动模拟点击一下它!
因为点击“换一换”触发的是向 B 站首页推荐接口发起的增量请求,正好撞在前面 DNR 的拦截枪口上。Cookie 被抹除后,首页的前 10 个视频就会被顺利替换为热门算法排序的纯净版。
为了用户体验,我还做了一个“防误伤滚动拦截”:如果在弱网环境下网页加载太慢,页面上部分视频已经加载完成且用户已经向下滚动开始浏览,脚本就会主动放弃强制刷新,避免刷掉用户正在看的内容。
有人可能会问:你为什么要在“换一换”按钮被渲染出来后才刷新,直接上来就刷不行吗?答案是:不行。因为扩展无法判断页面当前的加载状态,在部分 JS 和 CSS 还没载入时就提前刷新,只会得到一个布局诡异的错乱页面。
其实,这是我第一款真正成功上架到 Chrome 应用市场的浏览器扩展。
当年为了解决一个自用痛点,我曾兴冲冲地写过另一款扩展,准备好开发者账号,结果刚准备提交,谷歌突然搞出了 Manifest V3(简称 MV3),强行把 MV2 停用。由于那款老扩展的某个核心逻辑在 MV3 严苛的安全沙箱机制下根本没有可替代的方案,最后只能无限期搁置,我的开发者账号也就跟着一起落了灰。
前阵子,我在 RSS 阅读器里偶遇了原作者 @wangdaodao 开源的 TabulaBili(初见哔哩) 项目(原作者博客)。
原作者的技术嗅觉非常敏锐。面对如何在不破坏B站首页的情况下展示热门流视频这个需求,我第一时间想到的是通过在前端注入 JS 重新去请求不带 Cookie 的内容,再利用返回数据修改前端界面。但这个过程极易出错,B 站稍微改点代码就会直接 GG。而他想到了直接从底层的网络拦截规则入手,这个精妙的点子和开源工作必须给一个大大的赞赏!
我下载试用后觉得体验很好,顺手 fork 了一份,改掉了使用中发现的一个小 Bug 并给作者提了 PR。随后我跑去小众软件写了个推荐贴,收到的头一条评论就是:“会上架商店不?” 确实,对于普通用户来说,在商店里一键安装比起开启开发者模式去手动加载要方便太多了。在原作者明确表示自己不会上架后,我决定重新唤醒我那个落灰多年的开发者账号。
在把原作者的源码重构、增加完善功能并提交给应用商店的这几天里,我算是彻底领教了谷歌开发者后台的折腾程度。体验了一圈下来,我心里只有一个想法:难怪原作者不上架。
在如今的 MV3 时代,谷歌对主机隐私权限和远程代码注入有着近乎偏执的防范。为了能够合规地匹配 B 站首页带参数的特殊 URL(如 www.bilibili.com/?*)以及确保冷启动时不漏掉任何一个 Cookie,我必须在后台提交长篇大论、极其严密的理由,解释我为什么需要请求这些隐私权限。
而且每一项技术理由,写短了不给过,写长了也不行(有 1000 个字符的限制)。为了快速通过审核,我还得写成中英双语的(只用中文写审核会变慢,因为需要转给懂中文的审核人员),逼得我最终放弃了自己硬憋,改用 AI 来帮我填写这些输入框。
商店上架必须要一张 1280x800 或 640x400 的高清界面截图。而我的扩展弹窗界面只有 320 像素宽。如果我直接截取「扩展界面 + B 站真实页面」来凑尺寸,又极易触发谷歌“侵犯第三方商标与版权”的红线。
为了搞出一张合规且好看的宣传图,我折腾了很久。先试着用 PS 搞定,结果自己美术水平不堪入目;接着尝试让 AI 画,结果 AI 总是篡改界面里的细节,最聪明的 Image 2 也在瞎搞。最后一琢磨,比起画图,写代码才是 AI 更擅长的领域,于是我让它写了一个虚假的静态 HTML 页面,并盯着它改了好几版后,才通过截图完成了审核用图的生成。
PS:我说怎么总在扩展商店里看到一些扩展的预览图长宽比严重失调、明显被错误拉伸,之前我还奇怪这些作者扩展都会写,怎么连截图都不会,合着都是被谷歌给逼的。
是的,往 Chrome 应用市场上传扩展是需要开发者掏钱的。谷歌会向开发者收取 5 美元的注册费,按照谷歌的说法,主要目的是防范垃圾信息和恶意软件。通过收取这笔象征性的费用(并要求绑定有效的支付方式),谷歌能够验证开发者的真实身份,防止恶意用户批量注册账号来分发欺诈性或带有病毒的扩展程序。
但对于个人独立开发者来说就有点蛋疼了。首先,我的扩展本来就是免费开源给大家用的,结果我自己还要掏钱上架;其次,Chrome 应用市场理论上不接受中国大陆地区的银联卡,需要使用境外银行卡或者 VISA、Mastercard 等卡组织的信用卡。如谷歌所愿,这 5 刀的注册费用,确实成功提高了开发者上架的门槛。
本来呢,我觉得自己对原项目没改太多东西,初版(V1.0.0)其实也就只改了一下域名匹配逻辑,解决了错误匹配到非 B 站页面的问题。既然原作者说自己不想上架,且项目开源协议是 MIT 协议,于是我就直接原样提交了,名字、图标、介绍和原项目完全一致。结果……谷歌直接给我打回了。
审核理由是:涉及仿冒其他项目(也就是上游项目),不能自称是“替原作者上架”。经过询问其他有上架经验的大佬,我修改了扩展名称和图标——原版叫 TabulaBili 并配有粉色 T 字图标,我改成了 TabulaBili-Plus 并配有绿色漏斗图标,随后才成功将 V1.1.0 版上架。(其实我自己反而觉得改完名后更像仿冒的了,有些搞不懂谷歌的脑回路 😂)
本扩展纯本地安全运行,借助 MV3 的 DNR 底层特性,加上超时主动退出机制和防误伤刷新机制,扩展的系统性能需求几乎为零。
本项目采用 MIT 开源许可证 (MIT License)
没有做Firefox、Safari版扩展的计划,如果二次开发,最好保留现有博客信息,但不保留也可。
我们秉持极度开放与自由的开源精神:
https://github.com/tjsky/TabulaBili
希望这款克制、纯粹的小工具,能帮你在喧嚣的大数据时代里,挣脱出一方能够自由呼吸、探索未知的纯净天地。
本文 拒绝算法绑架!「TabulaBili-Plus 」扩展:让 B 站个性化推荐算法“彻底失忆”一键回归纯净热门流 最初发表于 秋风于渭水。
2026-05-20 23:27:17
今天收到 CloudCone 的邮件通知,提醒我机房正在进行 IPv4 的变更,我名下的服务器 IP 需要进行更换:

因为之前在其他服务商那里有过类似的换IP经验,通常流程非常自动化——在控制台确认切换后,服务器会自动做好临时映射,我进入系统确认无误后,在控制台选择重启即可。所以我是抱着轻松愉快的心情操作的,觉得是个几分钟就能搞定的小调整。

登录后台后发现,有两台部署了业务的 Ubuntu 24.04 服务器都在变更名单中(截图时我已经换了一台了)。

按照面板的提示,这个过程是完全自动化的,只需要点击「Auto Configure New IP」按钮,系统就会自动为我配置服务器内的新IPv4地址,并且在48小时内,旧的IPv4地址和新的IPv4地址都可以使用。这还有啥好说的,看起来是个无脑点按钮的操作,我点。

很快啊,很快,页面就提示我
新的IPv4地址已经在你的服务器内部配置好,并且很快就可以访问。如果从外部
ping不通新IP,请尝试通过旧IP连入VPS,执行ping 103.232.95.1
行,那我先通过尚未失效的旧 IP 连接进 SSH,在系统内部对新网关 103.232.95.1 进行了测试:
ping 103.232.95.1
PING 103.232.95.1 (103.232.95.1) 56(84) bytes of data.
64 bytes from 103.232.95.1: icmp_seq=1 ttl=255 time=0.882 ms
网关响应正常,延迟很低。说明上游路由策略服务商已经下发。但当我尝试在本地直接用新 IP 建立新的 SSH 连接时,终端却还是无响应,最终提示超时:
ssh: connect to host 103.232.95.122 port 22: Connection timed out
从内部能 ping 通网关,但是外部 ping 不通也连不上,既然现在旧 IP 的SSH连接依然在,那我从内部往外 ping 就没啥意义,现在的感觉是系统内部有地方阻断了新 IP 的入站。诶,这就有点头秃了,趁着旧 IP 还有 48 小时的回收缓冲期,开始排查网络状态。
首先那肯定是排查网卡怎么配置的。运行 ip route show 后,丝毫不意外呢:
default via 142.171.126.1 dev eth0 proto dhcp src 142.171.126.132
显示出来的只有旧网关,不是啥玩意啊,不是说自动换好 IP 了吗?我那么大的新 IP 去哪里了?得,还是要手动换,强行塞入新 IP(注意我的网卡名就是默认的 eth0, 请根据实际情况替换)先用 ip 命令绑个临时的配置试试水
# 给网卡绑上新IP
sudo ip addr add 103.232.95.122/24 dev eth0
# 把新网关也设置上
sudo ip route add 103.232.95.1 dev eth0
再运行ip route show看看情况
default via 142.171.126.132 dev eth0 onlink
default via 103.232.95.1 dev eth0 metric 10
103.232.95.0/24 dev eth0 proto kernel scope link src 103.232.95.122
142.171.126.132/26 dev eth0 proto kernel scope link src 142.171.126.132
我都不好吐槽 Ubuntu 这波算智能还是不智能了,新网关的路由虽然自动给我带了 metric 10,但原本由 cloud-init 生成的旧网关路由(via 142.171.126.132)并没有指定 metric 值啊。在 Linux 的缺省规则下,未指定 metric 的路由会是最高优先级(相当于 metric 0)。
这意味着当本地的连接请求到达新 IP 时,服务器尝试进行返回封包,但路由表强制将所有默认流量传给了旧网关。由于跨网段且上级链路已经变更,旧网关无法正确转发新 IP 的返回封包,导致网络表现为现在这样的状态,单向连通,外部连接超时。
明确原因后,首先在终端里删除旧的默认路由:
sudo ip route del default via 142.171.126.132 dev eth0
唯一的默认路由已正确指向新网关。按理说这时候 CloudCone 更换 IP 的系统层面操作就该结束了,可现实又给我浇了一盆冷水……在本地再次尝试 SSH 连接新 IP,依然提示超时。我去,这我就有点坐不住了,按说这时候服务器该配置的都配置了,为啥啊。
现在网卡的路由正确却依旧无法连通,一般是意味着流量在进入服务器之前就被拦截了。
难道我之前设置了什么奇怪的安全配置?我又检查了UFW防火墙和iptables规则,诶,也没啥毛病呀。
我这时候突然想起来,我其实根本没试过能不能用新IP作为出入口访问外网呢。
于是直接ping 8.8.8.8 完犊子,超时了,这可咋整……
诶,之前自动切换完IP时,后台页面提示过我,「如果从外部ping不通新IP,请尝试通过旧IP连入VPS,执行ping 103.232.95.1」他这个操作是在做什么?激活网关?让网关知道我的存在?
灵光一闪,懂了,这应该是想让我手动刷新上级路由吧,这时候虽然新 IP 已经被分配了,但机房上级交换机的 ARP 缓存可能并没有实时刷新,导致流量进入上级路由后,上级路由还不知道 103.232.95.122 此时对应哪一台主机。
为了强制刷新上级路由,最直接的选择就是从服务器内使用 -I 参数,强行指定新 IP 作为源地址持续向外发送数据包,督促上级路由:
ping -I 103.232.95.122 8.8.8.8
前两分钟里,数据包全部超时,直到持续运行到大概第 3 分钟左右,终端开始接收到来自 8.8.8.8 的返回封包了。
经过2分多钟的由内向外的持续请求,终于成功触发了上级交换机的路由刷新。现在从本地电脑再次测试 SSH 连接新 IP,顺利登录。
之前的改动都是通过 ip 命令临时生效的,如果此时直接重启,那配置就全部丢了。
由于CloudCone使用的是 cloud-init 动态接管,导致 /etc/netplan/ 目录下是空的,真实的动态配置文件躺在内存目录 /run/netplan/ 中。
为了让配置未来在重启后生效,需要写一个高优先级的静态配置文件:
sudo vim /etc/netplan/99-custom-config.yaml
将新 IP 和网关信息静态写入(我这里用的vim,你可以换成你喜欢的编辑器)。
network:
version: 2
renderer: networkd
ethernets:
eth0: # 注意替换为你实际网卡名称
dhcp4: no
addresses:
- 103.232.95.122/24
routes:
- to: default
via: 103.232.95.1
nameservers:
addresses: [8.8.8.8, 1.1.1.1]
由于 Netplan 对权限有严格限制,顺手修改权限为 600 以免应用时弹出安全警告,随后正式应用配置:
sudo chmod 600 /etc/netplan/99-custom-config.yaml
sudo netplan apply
执行完毕无报错,运行 sudo reboot 重启服务器。
系统重启后,新 IP 依然能够秒连。
搞定,网络层面的切换就算全部完成啦。
这次换个 IP 如此折腾,一次就踩了 Ubuntu Netplan 配置 和机房 ARP 缓存延迟 两个巨坑。
归根到底还是CC这家一贯的风格,便宜归便宜,但是遇到问题自己折腾去吧。
网络通了只是第一步,由于旧 IP 会在 48 小时内彻底失效,后续还需要迁移 Cloudflare 的 DNS 记录、更新 Docker 容器内部的网络绑定,以及修改第三方 API 和另一台服务器上 Telegram 机器人的 IP 访问白名单。明天起床再折腾吧,晚安。
【20260521更新:经过测试新 IP 的解锁流媒体能力下降了,但是网速快了…利好建站和备份用途,对做代理/解锁用途的用户就不太友好了】
顺带一提,CloudCone特价机部分机型补货了
HASHTAG-26-SSD-VPS-2+ 有货,位于 St. Louis, MO(美国中部)
配置 3核心、2GB内存、30GB SSD、4TB流量、1Gb/s带宽,1个 IPv4,$23.5/年(¥160/年)。
购买地址:https://app.cloudcone.com/vps/501/ (链接带AFF) 先充值,后下单(支持支付宝)
2026-05-18 01:15:02
距离上次CSDN搬运我的文章已经过去2年了,今天又遇到一个像素级搬运我的文章的人了,而且这位自己的版权协议,图片水印整的还挺全的,生怕别人抄他的文章,他自己倒抄我抄的挺开心啊,像素级搬运,连我最后的那句吐槽给抄走了。

看这个头图熟悉不,哈哈哈哈😂,像素级抄袭了我的《藏了 13 年的 NGINX “上古漏洞” 一个问号就能远程拿下你的服务器》,连头图都给搬走了。
站内文章图片水印、原创声明整的全的很,可惜文章基本没有自己写的,
首页一眼看过去,全都熟悉的感觉,文章都在其他地方已经看到过了。
我随便找了4篇文章做验证,全都找到了文章的原作者(为了不给他提供流量,所以未给他的原文做可点的链接,大家想看就去他网站的 web.archive 镜像 去查看具体情况吧)
已经很久(两三年)没有到遇到如此像素级的抄袭了,上次还是 CSDN 的自动化搬运,不过 CSDN 那是自动化搬运,我还能理解下,毕竟污染AI语料最大的中文垃圾教程聚合地,挣钱嘛,不寒碜。
而且还挺好屏蔽的if ($http_user_agent ~* "java") { return 403; },直接拉黑 java 的默认 UA 就行了
我被搬知乎和搬B站的文章都有,我也懒得管,毕竟这些人搬走还加工了一下,不管有没有规范转载,我看到了还会顺手点个赞。
这位你搬走就算了,还给图片打水印,而且还找不到原图,搬个缩略图过去,而且头图我都说了是 geimini 生成的,意味着图片是有 SynthID 盲水印的啊,你光加个可见水印有什么用啊。
还把我最后一句吐槽「其实最近本来是打算写一个关于个人博客运营的文章的,结果写的太久,在各种摸鱼中,反倒是写了两篇关于漏洞的资讯」给搬走是做什么?放你博客里很突兀的好不。
让我生气(哭笑不得)的地方是啥,他其他文章都自己重新做了头图,凭啥我的配图就是直接搬运走的😂
去他的首页一眼看过去,好家伙,全屏都是强烈的既视感。这篇眼熟,那篇也在别处见过,各种即视感。合着这不是个人博客,是 RSS 订阅源的网页精选版啊?把微信朋友圈朋友们发的动态全偷了一遍,然后再发到自己的朋友圈里,主打一个「肥水不流外人田」是吧?
更让我绷不住的,是这位「大自然的搬运工」还极其希望自己的文章被看到,在自己网站上挂满了一大堆博客聚合站点,什么「开往」、「博客录」、「blogclub」、「Blog Finder」全给安排上了。加聚合站本来是为了拥抱独立博客圈子互相串门对吧?
不是,你抄这些文章,大家分分钟就看到了好不,尤其是我的阅读器还有去重的(这其实也是我为什么今晚发现他的原因,去重脚本提示有个反复重复的源,问我是不是要清理掉)
我是真的无法理解,现在搞独立个人博客早就不是什么风口了,这种全盘 Ctrl+C+V 的操作到底是图个啥?如果是为了流量变现,那靠搬运我们这些独立博客的文章能赚到几个广告费?这种像素级克隆,搜索引擎也不傻,连服务器的电费都回不了本吧。如果是为了记录技术和生活,那全盘复制别人的思考,甚至连别人的个人情绪和生活状态(多篇文章里都把原作者的碎碎念原封不动的端走)都作为自己的一部分,这网站还有什么「个人」可言?
更何况,直接把手伸向了聚合站里的左邻右舍的行为是啥思路啊,大家本来都在这些聚合站的圈子里混,抬头不见低头见的。抄得这么原汁原味,不仅连排版不改,连别人文章里明显带有个人标签的废话都保留,真当大家平时都不上网、不互相访问的吗?这种掩耳盗铃或者说把偷来的破锣使劲敲的做法,真的很容易让人一眼识破并且原地社死吧,完全看不懂啊。
写独立博客本来是为了在互联网上留点属于自己的印记,折腾服务器、折腾代码,为的都是那点属于「独立站长」的乐趣和灵魂。这种毫无感情的「像素级克隆」,除了证明你的复制粘贴快捷键用得很溜之外,真的是毫无意义,真 · 哭笑不得。
最让我啼笑皆非的,是这位「大自然的搬运工」的 GitHub 上贴满了标签,自称精通 React 和 Python、是一位追求“优雅代码与最佳实践”的全栈工程师老哥。

看他的 GitHub 主页,各种彩色 Label 挂满,又是 React 又是 Python,又是 AWS 又是 Docker,满嘴都是“追求优雅代码、最佳实践”。
结果呢?一个自称精通前端 JavaScript 的全栈,在这个都在卷 WebP/AVIF 极致压缩的年代,这位老哥反向冲锋,把我原本优化好的 JPG/WebP 配图,全部手动转换成了体积暴涨数倍的 PNG。(有一张图直接体积涨了快10倍)
从他手中的域名 whois 记录看,25年5月初注册了0037.ins域名,不过只买了一年,到期后26年分别在05-06和04-07购买了0037.us与0037.pw,博客没广告,没引流,看起来搬运也不是基于脚本的。再结合他这个用力过猛、极力包装自己的 GitHub 主页,他的真实动机几乎呼之欲出了:他大概率是在为自己刷简历,或者在打造一个虚假的技术人设。
可能正处于找工作、转行或者渴望在同行面前证明自己的阶段。他知道面试官或同行可能会看他的 GitHub 和个人博客,但他自己又写不出东西,于是采取了最笨也最激进的办法:
他以为独立博客圈是个“小透明”聚居地,大家各写各的、互不相干,但他压根没想到,这个圈子因为很小,大家平时是真的会互相串门、互相订阅,互相看文章的。这场模仿秀演得挺大,但可惜细节全是破绽。
现在搞个人独立博客早就是夕阳红产业了,既没流量也没变现空间,你以为安安静静写点东西能出什么事?但现实是,只要小破站稍微在搜索引擎里露点苗头,各路奇葩、伸手怪和黑产脚本就会像闻到味儿的苍蝇一样围上来。
这位搬运工,好歹还知道把文章“搬”到他自己的垃圾站里,看起来还自己手动Ctrl+C+V 了一下,这两年还流行更恶心的套路——《近期大量个人博客被自动化繁体镜像搬运》大家又是往前端塞检测JS,又是在后端检测伪装爬虫的。结果防住了自动化镜像,却防不住这种连原作者摸鱼吐槽都打包端走的“像素级搬运工”。
只要你用的是常见的 CMS 框架。你的站点在搜索引擎有一些权重,那你的后台登录页基本就是黑产脚本的打卡圣地。虽然只要你的密码够长够复杂,你有 2FA(两步验证)它们不可能通过暴力枚举密码进入后台,但这玩意极其消耗服务器资源。每一次恶意的密码验证请求,前端的 web服务器 都要转发给 PHP-FPM,后端都要启动核心、去查询数据库并执行哈希计算,机器配置拉跨点,能直接把空闲的 CPU 资源硬生生耗尽。于是在强迫症下,又搞了《用 Nginx + PHP 隐藏登录页》
总有些搞 PCDN 设备赚钱或刷 PT 站积分的家伙,为了防止自己的宽带因为“上行流量比例过高”而被运营商宽带限速或销户,便专门跑去访问一些高质量、内容稳定,速度比较好有CDN的独立博客。通过疯狂下载网页数据来制造“正常浏览”的假象,从而平衡掉他们自己的上行流量。于是还要《对抗刷流攻击》
随着各大 AI 开启“联网搜索”功能,现在的 AI 爬虫怕不是都疯了。只要有用户在前端向 ChatGPT 或 Gemini 提问,AI 认为需要参考我的教程,各家大厂的 Bot 就会在几分钟内疯狂发起上千次抓取。不仅抓目标文章,还会把首页、侧边栏里的所有关联技术页面一口气全部并发拉起,感觉在他们眼中,服务器的配置要是小于4H8G的完全不存在。还有某些数据公司的弱智爬虫,坚持多年、每秒数次地去轰炸本站已经不存在了十几年的图片目录,不是,一个URL都 404 5、6年了,你还请求个什么劲啊,爬虫服务器资源用不完是吧。更别说还有一帮 AI 评论 SPAM 持之以恒的试图往我的评论区塞 💩 于是我既要《阻止爬虫访问已经不存在的目录》还要《防垃圾评论纯代码方案》。
从 linux 系统本身的漏洞,到基础环境N ginx,MySQL 的漏洞,再到博客程序 WordPress,Hexo 的漏洞,再到博客本身插件扩展的各种漏洞,主打一个层出不穷,无穷无尽,我都数不清光我自己遇到的,并写出来的有多少了……
折腾这么多年,有时候真觉得挺心累的。工作时间摸鱼写文章之余,还要跟这些搞全盘反代、后台爆破、甚至连摸鱼吐槽都一起端走的像素级搬运工斗智斗勇,感觉自己越来越不像个写博客的,倒像个天天给破网打补丁的业余网管。
但仔细想想,这大概就是今天坚持做独立博客不得不接受的代价吧。在这个到处都是算法推送和 AI 洗稿的中文互联网里,想要任性地留下一块完全属于自己、有点个性和生活碎碎念的数字自留地,本来就要付出这些额外的成本吧,不然现在可以发言的平台那么多,为啥咱们选择了独立博客这条路呢。
本文 连我的摸鱼吐槽都抄?围观独立博客圈最奇葩的“像素级搬运工” 最初发表于 秋风于渭水。