我们已经知道:Joomla 网站一旦开启 SSL 加密访问,就应该给服务器开启 http/2 。但是当我们用 VestaCP 搭建 web 环境之后,会发现它安装的 nginx 1.10.1(2016年9月份是这样,以后或许会更新)不支持 http/2,原因很简单:CentOS 7 自带的 openssl 版本是 1.0.1e,而 openssl 1.0.2 以上版本才支持 http/2。今天我们来介绍一下如何借助 OpenSSL 1.0.2+ 重新编译 VestaCP 安装的 NginX 服务器,以开启 http/2 支持。

http/2
http/2

首先确认你的 openssl 版本:

在 CentOS 的命令行或者 SSH 终端输入如下命令(注意 V 字母是大写的):

# nginx -V

输出的结果如下:

nginx version: nginx/1.10.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC) 
built with OpenSSL 1.0.1e-fips 11 Feb 2013
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-http_perl_module=dynamic --add-dynamic-module=njs-1c50334fbea6/nginx --with-threads --with-stream --with-stream_ssl_module --with-http_slice_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 --with-http_v2_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic'
 

这么多内容,实际上我们重点是看用来编译这个 nginx 的 OpenSSL 的版本,这里显示的是:

built with OpenSSL 1.0.1e-fips 11 Feb 2013

可以确定 openssl 的版本是 1.0.1e-fips ,显然低于 1.0.2,所以不支持 http2。

澄清一点:尽管其中有 --with-http_v2_module 这个参数,一般来说看到这个意味着 nginx 支持 http/2,但是实际上,这只能代表 VestaCP 在 CentOS 服务器上安装 NginX 的时候,添加了 --with-http_v2_module 这个编译参数,但是由于服务器本身提供的 openssl 版本不够 1.0.2,导致编译出来的 nginx 无法支持 http/2。

再澄清一点:我们并不需要升级系统自带的 openssl,因为随便升级 openssl 会带来安全风险。我们只下载 openssl 1.0.2 源码包,借助它来重新编译(相当于重新安装)服务器上的 NginX 即可。一旦成了 built with OpenSSL 1.0.2,就意味着能够支持 http/2 了。

补充更新:撰写完本文第一版之后,我有幸拜读了 @屈光宇 的文章《谈谈 Nginx 的 HTTP/2 POST Bug》,从而得知,必须使用 NginX v1.11 以上版本才能避开此 bug。因此,我们首先要升级 nginx 版本,然后再重新编译。

以下操作步骤在 CentOS 7 64bit + VestaCP 0.98-16 上面测试通过。建议你在测试服务器上先操作一遍,确认无误再到正式服务器上执行。鉴于此升级过程中需要暂停 nginx 服务,因此当你在正式服务器执行时,最好选择访客较少的深夜或凌晨进行,以减少对前台访问的影响。

第一步:升级现有 NginX 到 1.11 版本

由于 CentOS 7 默认使用 nginx 的“稳定版”,但是 nginx v1.11 属于“主线版”而非稳定版,因此我们需要修改 centos7 的 yum 源文件来改用 nginx 主线版。

编辑 nginx 的 yum 源文件:

# vim /etc/yum.repos.d/nginx.repo
 

将 baseurl 这一行修改为:

baseurl=https://nginx.org/packages/mainline/centos/$releasever/$basearch/
 

保存文件,然后分别执行下面命令:

# systemctl stop nginx
# yum clean all & yum upgrade nginx -y

系统会自动升级 nginx 到 1.11 版本。完成后检查版本得到:
 

# nginx -V
nginx version: nginx/1.11.4
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)
built with OpenSSL 1.0.1e-fips 11 Feb 2013
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-http_perl_module=dynamic --add-dynamic-module=njs-0.1.2/nginx --with-threads --with-stream --with-stream_ssl_module --with-stream_realip_module --with-stream_geoip_module=dynamic --with-http_slice_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 --with-http_v2_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic'

 

可以看到 nginx 版本已经升级到 1.11.4 了。不过还是 built with OpenSSL 1.0.1e-fips 。

另外请注意,前后两次输出结果中 configure arguments 部分的 --add-dynamic-module 选项的值不一样了:nginx 1.10.1 的是 njs-1c50334fbea6/nginx,而 nginx 1.11.4 的是 njs-0.1.2/nginx 。这个区别很重要。请复制 nginx 1.11.4 的输出结果中 configure arguments: 之后的内容,这就是“编译参数”。将上述编译参数保存到文本文件

提醒:你的输出结果或许略有不同,因此不要从我这个教程复制,应该从你的实际输出结果中完整复制。

 

第二步:安装依赖包

从源码编译安装 nginx 会用到很多依赖包,而一般我们搭建 web 服务器使用的 CentOS 操作系统都是简化版(minimal),未包含这些依赖包。所以,接下来安装一下依赖包,只需要一条命令,即可安装所有依赖包:

# yum install gc gcc gcc-c++ pcre-devel zlib-devel make wget openssl-devel libxml2-devel libxslt-devel gd-devel perl-ExtUtils-Embed GeoIP-devel gperftools gperftools-devel libatomic_ops-devel perl-ExtUtils-Embed dpkg-dev libpcrecpp0 libgd2-xpm-dev libgeoip-dev libperl-dev -y

命令末尾的 -y 开关表示无需再次询问“是否开始安装”,直接用 y 表示 yes 。不论你位于哪个路径,执行上面命令结果都一样。

 

第三步:切换到源码目录

由于我们将通过源码编译,所以现在进入源码目录,命令是:

# cd /usr/local/src/

 

第四部:下载所需的源码文件

逐条执行下列命令:

# wget https://www.openssl.org/source/openssl-1.0.2j.tar.gz
# tar -xzvf openssl-1.0.2j.tar.gz

# NGINX_VERSION=1.11.4
# wget https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz
# tar -xvzf nginx-${NGINX_VERSION}.tar.gz

# wget https://hg.nginx.org/njs/archive/0.1.2.zip
# unzip 0.1.2.zip

请注意最后一组命令中的 0.1.2 这个字串,是从前面保存的编译参数中“--add-dynamic-module=njs-0.1.2/nginx” 这一句提取的。这个字串不能随便自己编造,必须与原始编译参数完全一致。如果你的编译参数中该条字串不是这个,对照修改即可。

现在进入 nginx 源码目录:

# cd nginx-${NGINX_VERSION}/
 

第五步:修改编译参数

在第一步复制保存的编译参数末尾,添加上我们刚刚解压的 openssl 1.0.2 源码的路径:

--with-openssl=/usr/local/src/openssl-1.0.2j

注意这个参数以两个连字符(英文字符)开始,并且必须与前面的参数之间至少间隔一个空格。

 

然后,由于我们下载的动态模块(dynamic module)源码已经有了新的路径,所以修改原来编译参数中这一行:

--add-dynamic-module=njs-0.1.2/nginx 

将它改成: 

--add-dynamic-module=/usr/local/src/njs-0.1.2/nginx

 

现在,我们的编译参数部分变成了:

--prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-http_perl_module=dynamic --add-dynamic-module=/usr/local/src/njs-0.1.2/nginx --with-threads --with-stream --with-stream_ssl_module --with-stream_realip_module --with-stream_geoip_module=dynamic --with-http_slice_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 --with-http_v2_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic' --with-openssl=/usr/local/src/openssl-1.0.2j

 

将这部分内容保存到一个文本文件中备用。

 

第六步:编译构建 NginX

首先需要停止 nginx 服务,执行此命令:

# systemctl stop nginx

确认当前位于 nginx 1.11.4 的源码目录中,然后执行 ./configure 命令,在 ./configure 命令后面加上前面保存的全部编译参数,即:

# ./configure  --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-http_perl_module=dynamic --add-dynamic-module=/usr/local/src/njs-0.1.2/nginx --with-threads --with-stream --with-stream_ssl_module --with-stream_realip_module --with-stream_geoip_module=dynamic --with-http_slice_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 --with-http_v2_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic' --with-openssl=/usr/local/src/openssl-1.0.2j

 

执行完毕会自动返回命令行提示符,注意查看执行结果最后几行,如果没有报错,就分别执行下面两条命令:

# make
# make install

其中 make 命令执行过程需要几分钟,你可以起来走动走动,舒展一下身体,或者喝杯咖啡。当 make install 执行完毕,就表示 nginx 已经编译安装成功了。

 

第七步:检查结果

现在重新检查 nginx 版本就看到变成了 built with openssl 1.0.2j :

# nginx -V

输出的结果是(这里只摘取前4行):

nginx version: nginx/1.11.4
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)
built with OpenSSL 1.0.2j  26 Sep 2016
TLS SNI support enabled

在 nginx 配置文件中开启 http/2 选项:

假设你的域名是 joomlagate.com ,在 vestacp 中管理该网站的用户名是 admin,那么执行下面的命令编辑针对该域名的 nginx 配置文件:

# vim /home/admin/conf/web/snginx.conf

在配置文件开头部分,在 server { 之后,可以看到已经配置了 SSL 访问:

listen      xx.xx.xx.xx:443 ssl;

这里的 xx.xx.xx.xx 代表你的域名对应的 IP 地址,请根据实际情况操作。

现在只需要在 ssl 后面再加上 http2 选项即可,变成:

listen      xx.xx.xx.xx:443 ssl http2;

这样,就开启了 nginx 的 http2 功能。不过,别忘了需要重启 nginx 才能生效。所以还要执行一条命令:

# systemctl restart nginx

现在你可以通过谷歌 Chrome 浏览器(版本必须高于 v41.0)来访问你的网站前台,按下 F12(或点击菜单启动“开发者工具”),点击 network 一栏,然后刷新网页之后,在 protocol 一列中就能看到很多 h2(如下图所示),代表网页是通过 http2 协议传输的(如果你只看到 http/1.1 而没有 h2,说明失败了,你可以到我们论坛发帖提问,我们一起来解决一下)。

Google Chrome 浏览器开发者工具检测 http/2 已生效
Google Chrome 浏览器开发者工具检测 http/2 已生效

总评分 (0)

0 (满分5分)

免费下载最新Joomla!核心中文版

扫描此二维码,立即开始下载 Joomla 3 核心中文版

付费下载汉化版扩展

付费后即可下载独家海量

Joomla! CMS 扩展汉化版

了解付费会员制度

点击这里给我发消息

了解 joomlagate.com 网站的付费会员制度