/ Nginx

给后台人员的前端优化

作为一名后台开发,听到前端优化(不涉及后端 Web Server、数据库等),我大概了解的词有这些:主要是减小体积、减少请求、合理布置页面原素等,再具体些就是,开启 Gzip 压缩、合并 css 文件、合并 js 文件、长链接、减少 DNS 查询、使用 cookie-free 域名、js 放页面最下面、指定缓存时间、ETag、延迟加载、异步加载……

作为一个没什么访问量的小网站,费心思搞这些东西干什么呢?一方面服务器本来就没什么压力,另一方面访问者能感受到的速度差异能有多少?结果我还是搞了一下,因为有个方便的工具,Google 出品的 PageSpeed,不用我自己操那么多心。

这货不是被 Google 关了吗?如果是说 Google 提供的带 PageSpeed 功能的 CDN 服务,对,是关了。但是实现这个功能的模块 PageSpeed Module,还是可以自己拿来用的。它是作为 Web Server 的模块来使用的,Web Server 有了它,就能自动优化网站了。目前有提供 Apache 和 Nginx 的模块,对于 Apache,模块名是 mod_pagespeed 还提供各个平台编译完打好包的二进制文件,对于 Nginx,模块名是 ngx_pagespeed, 需要自己去编译。

因为我只用过 Nginx,所以自然是向 ngx_pagespeed 下手了。

编译安装

ngx_pagespeed 这个模块编译进 Nginx,官方有个简单明了的编译步骤 Build ngx_pagespeed From Source,很清晰。

1. 先装依赖的库

RedHat, Centos, or Fedora

sudo yum install gcc-c++ pcre-devel zlib-devel make unzip

Ubuntu or Debian

sudo apt-get install build-essential zlib1g-dev libpcre3 libpcre3-dev unzip

2. 然后下载 ngx_pagespeed

cd
NPS_VERSION=1.9.32.10
wget https://github.com/pagespeed/ngx_pagespeed/archive/release-${NPS_VERSION}-beta.zip
unzip release-${NPS_VERSION}-beta.zip
cd ngx_pagespeed-release-${NPS_VERSION}-beta/
wget https://dl.google.com/dl/page-speed/psol/${NPS_VERSION}.tar.gz
tar -xzvf ${NPS_VERSION}.tar.gz  # extracts to psol/

这一步里后面下载的 psol 的那个包比较大。

3. 最后下载 nginx 进行编译

cd
# check http://nginx.org/en/download.html for the latest version
NGINX_VERSION=1.8.0
wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz
tar -xvzf nginx-${NGINX_VERSION}.tar.gz
cd nginx-${NGINX_VERSION}/
./configure --add-module=$HOME/ngx_pagespeed-release-${NPS_VERSION}-beta
make
sudo make install

通常在编译 nginx 之前,我会执行 nginx -V 看一下当前的 nginx 的编译参数,把这些编译参数加到 ./configure 这一条命令的参数里(又加了别的参数,就可能又需要安装别的依赖,最笨的方法,看缺库的报错信息见招拆招就行)。

配置使用

在每个要启动 PageSpeed 的 server 块中加入如下代码:

pagespeed on;

# Needs to exist and be writable by nginx.  Use tmpfs for best performance.
pagespeed FileCachePath /var/ngx_pagespeed_cache;

# Ensure requests for pagespeed optimized resources go to the pagespeed handler
# and no extraneous headers get set.
location ~ "\.pagespeed\.([a-z]\.)?[a-z]{2}\.[^.]{10}\.[^.]+" {
  add_header "" "";
}
location ~ "^/pagespeed_static/" { }
location ~ "^/ngx_pagespeed_beacon$" { }

这一行配置 pagespeed FileCachePath /var/ngx_pagespeed_cache; 的注释里说了,设置的这个目录要存在,而且 nginx 进程对它有写权限。

配置 filter

这才是进行优化的重头戏。

把 filter 翻译成“过滤器”容易让人迷惑,实际上这些 filter 的作用是改变原本内容,原本内容从 filter 过一遍,变成另外的样子。nginx 的很多模块都属于 filter。

为了实现开头说的各种优化,PageSpeed 提供了多种多样的 filter,通过 pagespeed EnableFilters xxx,xxx,xxx 来启用,EnableFilters 后面可以跟一个或多个以逗号分隔的 filter 名字,与之对应的 DisableFilters 用来停用 filter。为了方便人民群众,PageSpeed 给了三种预设的模式,通过启用各种不同的 filter 进行不同目的的优化。

  • PassThrough
  • CoreFilters
  • OptimizeForBandwidth

pagespeed RewriteLevel PassThrough 指定 PassThrough 模式,顾名思义,这样配置就相当于什么也不做。Corefilters 是默认的模式,开启了不少 filter,对网站内容一般来说是安全的。OptimizeForBandWidth 更保守些,更安全。这里的安全是指对原始内容的改变是否会影响页面的展示、效果、功能等等。

Enabling, Disabling, And Forbidding Specific Filters 这里有 CoreFiltersOptimizeForBandWidth 所启用 filter 的详细列表,以及对各个 filter 的简介,也可以从列表里进到每个 filter 的详情页查看。

RewriteLevelEnableFilters 可以组合起来用。比如

pagespeed RewriteLevel PassThrough;
pagespeed EnableFilters combine_css,extend_cache,rewrite_images;
pagespeed EnableFilters rewrite_css,rewrite_javascript;

这样就只开启了后面两行指定的 5 个 filter。

或者这样用

pagespeed RewriteLevel CoreFilters
pagespeed DisableFilters rewrite_css,rewrite_javascript;

开启 CoreFilters 模式,但是停掉 rewrite_cssrewrite_javascript

这是我的配置,仅供参考。每项内容在官网都有详细解释。

pagespeed on;

pagespeed FileCachePath /var/ngx_pagespeed_cache;

location ~ "\.pagespeed\.([a-z]\.)?[a-z]{2}\.[^.]{10}\.[^.]+" {
	add_header "" "";
}
location ~ "^/pagespeed_static/" { }
location ~ "^/ngx_pagespeed_beacon$" { }

pagespeed XHeaderValue "Powered By ngx_pagespeed";

pagespeed RewriteLevel CoreFilters;
pagespeed EnableFilters collapse_whitespace,remove_comments;
pagespeed EnableFilters local_storage_cache;
pagespeed EnableFilters in_place_optimize_for_browser;
pagespeed EnableFilters inline_javascript,inline_css,inline_images;
pagespeed EnableFilters dedup_inlined_images;

pagespeed AvoidRenamingIntrospectiveJavascript off;
pagespeed CriticalImagesBeaconEnabled false;