锱铢必较的 gzip 设置方法

在设置 web 服务器的时候,很多人会把 gzip 或是 brotil 打开,以期获得更快的传输速度——毕竟文件压缩了,那传输就变快了。

事实真的如此吗?

众所周知,gzip 或是 brotil 这样的压缩算法是需要消耗 CPU 的。如果文件没有经过预先处理,那多半是在访问时进行压缩的。在小流量的站点上这其实并非问题,但对于某些大流量站点这可能是问题。

网上有很多教程教你如何静态压缩文件,并使用 gzip_static 模块来提供服务——这很好,因此在这里我们不谈论这个,感兴趣的读者可以自己找来学习。

这里要来讨论的是 gzip 的另一个盲区问题——经过 gzip 压缩之后,文件真的变小了吗?

我们来观察一下 Google 首页加载的 logo 图片

浏览器请求 Google 首页 logo 的 DevTools 截图。

从 Google 服务器的响应上来看,可以很清楚地看到,服务器并没有使用 gzip 或是 brotil 对这张图片进行压缩。

我们将这张图片下载回来,然后在本地使用 gzip 命令对其进行压缩:

wget https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png
gzip -k googlelogo_color_272x92dp.png
ls -l googlelogo_color_272x92dp.png*

输出结果如下:

-rw-r--r-- 1 oott123 oott123 5969 Oct 23  2019 googlelogo_color_272x92dp.png
-rw-r--r-- 1 oott123 oott123 6022 Oct 23  2019 googlelogo_color_272x92dp.png.gz

可以看到,gzip 之后的文件大小为 6022 字节,甚至比原始的 5969 字节还要更大一些。

聪明的你肯定知道为什么:PNG 格式本身就使用和 gzip 如出一辙的算法 DEFLATE 压缩过了,再用 gzip 压缩一遍反而是徒增烦恼,即使文件没有变大,也不会变小太多。

事实上许多网络上传输的文件都是有内部有压缩实现的,例如 png, jpg, zip, rar, 7z, docx 。在 web 服务器上对这些文件进行压缩,起不到什么压缩的效果,反而浪费了压缩解压的时间和功。因此,合理地配置需要压缩的文件类型才是正确的选择,一股脑全部打开只是浪费——当然了,也浪费不了多少,几乎不会产生什么显著影响。

当然,总会有些人觉得压缩两次不舒服,比如我。

这次我使用的是 traefik 作为我的流量入口,因此找到了它的 gzip middleware 配置

可以看到,traefik 支持使用 excludedContentTypes 配置 gzip 排除名单。懒人如我,自然开始上网搜寻现成的——然而没找到,只找到一份需要用 gzip 压缩的 mime 名单。事实上我也觉得在 gzip 这件事情上用白名单比较好,因为需要压缩(且压缩率大)的那些文件其实很明显:html, css, javascript,有时候还有 json。可是 traefik 没有提供设置白名单的方法,所以只能自己动手整理一份黑名单了。

于是我打开 nginx 内置的 mime 列表 ,挑出来一些常见的、我知道带压缩的格式,放进了排除名单里。

这份名单提供在这里,供有需要的人参考:

image/jpeg
image/png
image/webp
audio/webm
audio/mpeg
video/webm
video/mp4
video/mpeg
video/mp2t
application/zip
application/x-zip-compressed
application/x-rar-compressed
application/x-7z-compressed
application/java-archive
application/vnd.openxmlformats-officedocument.presentationml.presentation
application/vnd.openxmlformats-officedocument.wordprocessingml.document
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
application/x-bzip
application/x-bzip2
font/woff
font/woff2

其中还藏着一个冷知识:TTF 几乎没有压缩,而 woff / woff2 分别采用了 zlib 和 brotil 进行压缩,因而体积更小,更适合在 web 上传播。

总而言之,如果你的网站流量并不是很大,而且也不怎么在意这点微不可察的加载时间差距,那么我建议你还是别折腾,直接 gzip on 就完事了。如果你真的是锱铢必较,或者有所谓的强迫症,那这篇文章还是有参考价值的。

nginx二级域名泛解析到子目录

需求:自己VPS上跑的是军哥的LNMP,不想装什么面板。但是由于还要给其它人开空间,为了省去绑定一大堆二级域名的功夫,决定研究一下如何在nginx下二级域名泛解析,绑定到子目录。

具体的要求如下:

域名*.example.com均解析到/home/wwwroot/example目录下,创建数个子文件夹如example.com、test.example.com
对应的域名能够直接解析到对应的目录,如访问test.example.com能够访问/home/wwwroot/example/test.example.com下的目录

在网上找了一堆,大部分是用正则匹配server_name然后set $www_root $1;root /home/wwwroot/exmaple/$www_root;这样的。我自己测试了好多次之后发现,index配置偶尔会失效。于是自己钻研琢磨,写成了下面这个样子:

server
        {
                listen       80;
                server_name example.com *.example.com;
                root  /home/wwwroot/dimpurr/$host/;
                #以下配置省略……
        }

完美解决。

在nginx下配置并使用ssl

嘛,具体的效果可以看https://best33.com,我已经部署了StartSSL的证书了。

首先我们需要一个SSL证书。可以看Freehao123上的文章StartSSL免费SSL证书成功申请-HTTPS让访问网站更安全,也可以在Linux服务器下自签名证书。至于为什么使用StartSSL,无非就是让访客访问的时候不提示“不受信任的安全连接”罢了。自签名的证书和经过CA认证的证书一样是能够保障连接安全的,只是它不被浏览器信任罢了。
Update:在火狐下,如果你的网页有除了https的其它内容,那么火狐仍然会显示你的网页,只是地址栏上没有小锁图标;IE或者某些其它浏览器下,非https的内容压根就不会显示。所以要保证站点引用的都是https的资源,包括css啦,js这些的都要是https的,这样才能显示小锁图标。
在Linux的VPS下要生成一个SSL证书也很简单。首先我们需要生成一个key:

openssl genrsa -des3 -out ssl.key 1024

生成的时候要输入一个密码。因为nginx加载证书的时候会需要输入这个密码,所以我们要把这个密码再去掉。执行:

openssl rsa -in xxx.key -out ssl.key

输入密码后,ssl.key的密码就被去掉了。然后我们用这个key去生成一个csr请求,执行:

openssl req -new -key ssl.key -out ssl.csr

这一步会要输入一大堆的东西。嘛……如果不介意的话一路回车下来就好了。
在这里我们得到了一个csr文件。执行cat ssl.csr就可以看到一堆的被编码的文件,在生成StartSSL的时候可以用到。(在输入密码那一步,点击skip然后粘贴进去。这样的好处就是生成的证书有详细的描述)
得到StartSSL的证书后,把它保存为ssl.crt放到同一个目录下即可。

如果没有StartSSL帮你签名的话,就自签名就好了。将csr请求和key一起生成我们要用到的crt证书,执行:

openssl x509 -req -days 3650 -in ssl.csr -signkey ssl.key -out ssl.crt

然后我们就会在当前目录下得到ssl.key和ssl.crt两个文件。刚刚的ssl.csr可以删掉。
Update:据说如果这个文件不和StartSSL的根证书合并的话,依然不能用。从网上下载StartSSL的根证书并合并到ssl.crt:

wget http://cert.startssl.com/certs/sub.class1.server.ca.pem
cat sub.class1.server.ca.pem >> ssl.crt

然后我们把这两个文件移动到一个合适的地方,比如/home/www/,并给与对应的权限:

mv ssl.* /home/www/
chown www:www /home/www/ssl.*

现在找到nginx的配置文件。我用的是军哥LNMP,那么执行:

vim /usr/local/nginx/conf/vhost/你的虚拟主机名

找到其中的server段,在listen 80;下方加入:

                #ssl config
                listen 443;
                ssl on;
                ssl_certificate /home/www/ssl.crt;
                ssl_certificate_key /home/www/ssl.key;
                ssl_protocols SSLv2 SSLv3 TLSv1;
                ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
                ssl_prefer_server_ciphers on;
                #end of ssl config.

注意把其中的路径换成你的路径。完事之后重启nginx,尝试访问你的https站点吧~