数据库结构实际存在哪里
这个服务所做的一切都在版本控制里。至少代码是。它运行其上的数据库是 32 张表,其中 15 张在仓库里任何地方都没有定义 — 没有 CREATE TABLE,没有结构文件,什么都没有。一份备份能把 32 张表连同结构一起还原;仓库里缺的,是这份结构的第二份、独立的副本。 这些数字来自把运行中的数据库与仓库里的 736 个文件、5,671,710 个字符做比对。它们不是估计。…
为什么我们的配置几乎全是注释
这台服务器上的 .htaccess 有 478 行。其中 98 行是规则。309 行是注释。 每一行做事的代码配三行解释。这个比例不是计划出来的;当写规则的规则是理由必须挨着它时,自然就会这样。 为什么一条重写规则需要一个段落…
我们如何检查每一份译文
这个博客有 64 篇文章,十一种语言:704 行,约 220 万字符。没有一篇文章在任何一种语言里缺失 — 这个说法只有在有东西强制它时才有意义。 确实有东西在强制。译好的一行写入之前,先与英文原文比对,不通过的行根本不会被写入 — 它会被报告出来,留给人看。 每项检查是干什么的 相同的链接目标。 内部路径在翻译时不能改变:/privacy 在每种语言里都还是…
我们怎么发现计数器坏了
计数器可能悄无声息地坏掉。站点改版,嵌入代码没能熬过搬家,而没有任何东西会宣告这件事——统计就是停了,站长几个月后才发现,如果发现得了的话。 一个显而易见的功能自己冒了出来:注意到沉默,然后发个通知。而搭建它的显而易见的办法,是从本来就有的数字里推断出故障。那个版本被搭出来、被测量、然后被扔掉了。 杀死它的那次测量…
数错自己日志的两种方式
这里一周的改动,建立在从一天的访问日志里数出来的一组数字上:一百万行,其中十万行是我们自己的。要知道这些改动有没有用,同一批数字过一阵子得再数一次——而手工数第二遍,永远不会和第一遍完全一样。机器人名单不同、日期范围不同、grep 不同。 所以这套计数搬进了一个工具,基线以常量的形式写死在里面。第一次运行,跑的正是那份基线所来自的同一天日志。每一行都应该对得上。…
网站目录里的备份就是源码泄漏
动一个大文件之前,很自然的一步是先复制一份。cp index.php index.php.改之前,然后开工。如果这个文件躺在 Web 服务器对外发布的目录里,整份源代码就变成了可以当纯文本下载的东西——数据库凭据、密钥,文件里碰巧有的一切。 Apache 让 index.php 走 PHP,是因为它以 .php 结尾。index.php.改之前 不以 .php 结尾。它以…
一个看起来像 Perl 的语言代码
2026年8月19日,我们在.htaccess里加了一条规则,用来拦截一类绝不应该被直接提供给浏览器的文件:编辑之后遗留在网站根目录里的源代码副本——比如index.php.bak、一个散落的config.py,或者任何带脚本扩展名、访客有可能以纯文本形式获取到源代码而不是让它运行起来的文件。…
每一次都把同一个文件压一遍
Apache 是即时压缩响应的。对一个每次都不一样的页面来说,这是唯一的办法。对一个一个月才改一次的样式表来说,这意味着服务器为每一位访客把同样的活儿重做一遍,一做几个星期,而且每次都把结果扔掉。这件事没有缓存。 2026 年 8 月 19 日在一条已建立的连接上实测,用的是本站自己的 style.css(124 kB)和 world.js(102 kB): 时间 字节…
比字段还长的页面地址
2026 年 8 月 9 日,有二十张计数器图片没能画出来。不是画错了,也不是画得慢——访客在本该有计数器的位置什么也没拿到。 原因是一个超过 255 个字符的页面地址。现在它在写入时就被截断,计数器图片不再取决于告诉它的那个地址有多长。 这条链 记录一次访问落在哪个页面上的那个字段是 varchar(255)。地址来的时候比这更长。插入以 SQLSTATE 22001…