本文完整阅读约需 13 分钟,如时间较长请考虑收藏后慢慢阅读~

在很多高校、组织、机构的网站里,我们经常会看到形如http://example.com/~john/index.html 的URL,其中的~符号异常显眼,但介于主流搜索引擎总是屏蔽特殊符号,且国内使用该URL规则的网站少之又少,想要搜索其源头异常困难。

0x01

在这里抛出两个示例URL:
http://math.ecnu.edu.cn/~jypan/Teaching/NA/
https://www.cs.vu.nl/~ast/brown/

可以发现,这两个URL都存在以下共性:
– 第一级目录以~符号开头
– 第一级目录除开~符号以外,是该主页作者的用户名或用户名缩写

这样的规律不禁令人猜测,这一目录结构是否有其特殊原因?

0x02

『波浪线』这一词汇,在英文中有着多种表示,此处最恰当的应该是tilde
于是我在Google中搜索tilde in url,得到了以下结果:

排行第一的链接中最高票回答揭解释了这一符号的用途:可用于定位用户的home目录。

尽管答主并未直接说明这一符号在URL中的由来,但我却突然被点醒,这又是一个与操作系统深度耦合的应用。

0x03

为什么我要说又,又为什么要说与操作系统深度耦合呢?

我先来卖个关子,拿Samba这个Linux下久负盛名的软件来说明。

Samba的配置过程中有两个很有意思的点:

  1. 默认配置下,每个用户都可以用自己的Linux用户名与密码登录到自己的home目录
  2. 如果需要设置一个共享目录,需新开一个用户,并将对应目录chown到该用户及其用户组上

这两点就是应用程序与操作系统深度耦合的一大典型。诸如此类的软件还有LDAP、FTP等。

我们在这里先不论深度耦合的正确与否,但这的确就是UNIX哲学的最大体现(即程序只做程序本身的事情,将用户管理的部分交给专门负责用户管理的模块负责),尤其是在『多用户』共享计算机资源当道的20世纪末期(现在的多用户其实已经成为了一个伪需求,大部分情况下计算机只有同时一个用户使用,其他的所谓『用户』执行者事实上都不是人,而是为了限制进程访问而作出的人为区分)。

而UNIX哲学的另一体现,则是早期的WebServer对目录的组织结构及其映射规则。

0x04

Apache甚至更早的WebServer都有一个配置叫做PerUser Directory,理解过来就是『如果我需要在这台服务器上跑一个网页,我不需要和网站管理员额外申请,只需要放文件在我的用户目录下就可以被访问到』,而上文所提到的~xxx就是这一特性的体现。

以Apache的配置为例:

  1. 首先我们需要引入相关配置:
Include conf/extra/httpd-userdir.conf
  1. 然后我们配置所有用户的Web目录:
UserDir public_html

配置结束后,通过诸如 https://example.com/~username/index.html 的方式就可以访问到public_html目录里的静态资源,也就实现了网站的搭设。

  1. 如果我们想更专业一些,再来一点Retro Style,我们还可以加入cgi-bin的执行配置:
<Directory "/home/*/public_html/cgi-bin/">
    Options ExecCGI
    SetHandler cgi-script
</Directory>

这样就可以通过诸如 https://example.com/~username/cgi-bin/index.pl 的方式访问到服务器中属于该用户的可执行脚本

以上配置均可以在Apache最新的官方文档中找到,不免让人感叹历史的有趣之处,也让人感叹成功的软件总是在兼容性方面给人惊喜。

0x05

上文我的推测与实验在《Your Unix: The Ultimate Guide》这本2001年(甚至更早)出版的图书中同样得到了验证:

截图来自于谷歌图书

这更说明了这一习惯由来已久。

0x06

但由于网络安全的重视、动态网站的推广、建站系统的出现、PC的普及,当用户不再需要共享一台计算机,而这台计算机也无需24小时开机以服务随时访问的用户时,用户目录的特性被逐渐弃用,这一点也能在WebServer后起之秀Nginx中有所体现。Nginx完全不提供对用户目录的支持,相反建议用户自行实现该功能:

location ~ ^/~(.+?)(/.*)?$ {
    alias /home/$1/public_html$2;
    index index.html index.htm;
    autoindex on;
}

0x07

关于~符号的起源、历史、作用就谈到这里为止了。但眼尖的读者似乎发现了一个问题:为什么同样是特殊符号,~符号不会被浏览器转义呢?

事实上,如果按照映射规则,~符号本应该被URLENCODE为%7e,即显示为例如https://example.com/%7euser/index.html的样式,而按照RFC1738中的相关要求,我们也应该对其进行转义,才能应用在URL中。

而当我们将上文例子中两个URL的~修改为%7e之后,会发现浏览器的行为是自动恢复成~符号,正常访问;而使用curl也是可以正常访问的,这说明实际上WebServer和浏览器都同时兼容这两种方案,这也是一种兼容性考量。

0x08

但我们也必须承认,这种未经转义在URL中传递特殊字符的方法是错误的。

首先,这种方法将Unix的习惯带入了Web,但当时第一批这样做的管理员们没意识到一个问题:Web会变得如此庞大。当不了解UNIX的用户输入这样『奇怪』的URL时,一定会感到困扰,更别提有很多的键盘在输入这一少见符号时的困难,尤其是在非英语国家,例如欧洲:

这是一把芬兰语键盘,你可以一眼看出如何输入~符号吗?

其次,这种方法url与实际目录的映射也是非常规的。当我们看到~username/index.html,而兴冲冲在UNIX Shell中键入cat ~username/index.html时候,大多会遇到No such file or directory错误,这是因为实际文件存放在~username/public_html/index.html,这与常识背道而驰,也不便于自动化脚本的执行(要在最后一个fragment前的/插入/public_html字符,我想就算是精通awk也要思考好一阵子才能想到如何实现)。

再者,在Linux尚还处于襁褓期、BSD只活在实验室与俱乐部里的上世纪90年代,使用~作为家目录简写(甚至可以说存在家目录这一概念)的也只有UNIX,而至于DOS、Windows NT、OS/2、Macintosh OS(Old Mac)等操作系统,则完全不存在这一个概念,这一特性可能会让Unix下的WebServer部署较为简单,但却为Web的普及以及其部署造成了很大影响,反而弊大于利。

这就是URL中~符号的故事,也是互联网早期各种有趣历史中的一小段,希望看到这里的读者能够领悟我的用意,能够和我一样,以后来者的眼光看待这段趣事,并能自然地会心一笑。