HTTPS 在 2017 年获得广泛的普及和支持,这里介绍一下 Apache Httpd 设置 HTTPS 并开启 HTTP/2 (以下会简称 h2)的教程。

不过呢,在目前的条件下,个人还是建议一些强 Apache Httpd 的环境例如 LAMP 可以考虑前面加一层 Nginx,来更灵活的实现 HTTPS + HTTP/2 的新特性,因为 Nginx 编译和配置HTTP/2协议,更加的简单,例如:目前 Apache Httpd 对 CHACHA20-POLY1305 的算法弄起来就灵活。

 

TLS 支持

在浏览器上使用 HTTP/2, 浏览器只在使用 TLS 连接(https:// 开头的 url)时才支持 HTTP/2。

如果你服务器上 TLS 库还没有实现 ALPN,客户端只能通过 HTTP/1.1 通信。

那么,可以和 Apache 链接并支持它的是什么库呢?OpenSSL 1.0.2 及其以后的版本。

 

Apache 编译、配置 HTTP/2

Apache Httpd ≥ 2.4.17 ,Apache Httpd 从 2.4.17 开始支持 mod_http2 ,并且编译时加入 --enable-http2

OpenSSL ≥ 1.0.2 浏览器客户端例如 Chrome 51(2016 年 5 月 31 日发布)之后,已经不支持对没有 ALPN 协议网站的 h2 支持,OpenSSL 1.0.2 起开始支持 ALPN 协议。

nghttp2 ≥ 1.12,其为是基于 C 实现的 HTTP/2 库,为实现 Apache Httpd 支持 h2 所必须

(编译、Win二进制)Apache Httpd ≥ 2.4.25 ,如果是自己编译或者 Win 采用二进制编译包的 Apache Httpd 并且开启 mod_http2 支持,版本必须大于 2.4.25,因为之前的版本存在 DDOS 漏洞

 

系统环境:

CentOS 7.x 系统

Apache/2.4.34

PHP 7.2.8

OpenSSL 1.0.2k-fips  26 Jan 2017

http/2 依赖 libnghttp2 库,需要先安装,此外还需要 openssl 版本大于 1.0.2

然后,重新编译 apache ,加上参数 --enable-http2

先安装依赖库:

yum -y install libnghttp2-devel  (默认路径安装)

再编译安装 Apache:

# cd /home/data/tool-server/APACHE/httpd-2.4.34
# ./configure --prefix=/usr/local/apache2
--enable-http2

注意:这里有一个技巧,少做改动,达到编译安装 http2 模块的目的

1)线上正式生产环境的 apache 目录:/usr/local/httpd/

2)重新编译安装apache,增加 http2 模块的 apache 临时目录:/usr/local/apache2/

添加了 http2 模块后,从临时目录 /usr/local/apache2/ 拷贝 http2 模块到正式生产线目录 /usr/local/httpd/

具体操作步骤如下:

1)编译安装apache到临时目录,增加 http2 模块

# cd /home/data/tool-server/APACHE/httpd-2.4.34
# ./configure --prefix=/usr/local/apache2
--enable-http2

2)对比临时目录与线上正式生产的apache目录

# diff /usr/local/apache2/modules/ /usr/local/httpd/modules/
Only in /usr/local/apache2/modules/: mod_http2.so

把临时目录(/usr/local/apache2/)新生成的 mod_http2.so 拷贝到线上正式生产的apache目录(/usr/local/httpd/

cp /usr/local/apache2/modules/mod_http2.so /usr/local/httpd/modules/

 

Apache 配置 HTTP/2

Apache 重新编译(强烈推荐使用临时目录来编译)新增 http2 模块后,需配置 httpd.conf 文件

vim /usr/local/httpd/conf/httpd.conf

1)添加 http2 模块

LoadModule http2_module modules/mod_http2.so

 

2)添加 http 和 https 的 http2命令

http 协议:添加一行 Protocols h2c http/1.1

https 协议:添加一行 Protocols h2 http/1.1

具体配置如下:

# blog
<VirtualHost *:80>
	Protocols h2c http/1.1
	DocumentRoot /usr/local/httpd/htdocs/mimvp_blog
	ServerName blog.mimvp.com
	......
</VirtualHost>

<VirtualHost *:443>
	Protocols h2 http/1.1
	RewriteEngine On
	DocumentRoot /usr/local/httpd/htdocs/mimvp_blog
	ServerName blog.mimvp.com
	......
</VirtualHost>

 

h2c 限制

h2c 的实现还有一些限制,你应该注意:

1)在虚拟主机中拒绝 h2c

你不能对指定的虚拟主机拒绝 h2c 直连。

连接建立而没有看到请求时会触发直连,这使得不可能预先知道 Apache 需要查找哪个虚拟主机。

2)有请求数据时的升级切换

对于有数据的请求,h2c 升级切换不能正常工作。那些是 PUT 和 POST 请求(用于提交和上传)。如果你写了一个客户端,你可能会用一个简单的 GET 或者 OPTIONS * 来处理那些请求以触发升级切换。

原因从技术层面来看显而易见,但如果你想知道:在升级切换过程中,连接处于半疯状态。请求按照 HTTP/1.1 的格式,而响应使用 HTTP/2 帧。如果请求有一个数据部分,服务器在发送响应之前需要读取整个数据。因为响应可能需要从客户端处得到应答用于流控制及其它东西。但如果仍在发送 HTTP/1.1 请求,客户端就仍然不能以 HTTP/2 连接。

为了使行为可预测,几个服务器在实现上决定不在任何带有请求数据的请求中进行升级切换,即使请求数据很小。

3)302 时的升级切换

有重定向发生时,当前的 h2c 升级切换也不能工作。看起来 mod_http2 之前的重写有可能发生。这当然不会导致断路,但你测试这样的站点也许会让你迷惑。

 

h2 限制

这里有一些你应该意识到的 h2 实现限制:

1)连接重用

HTTP/2 协议允许在特定条件下重用 TLS 连接:如果你有带通配符的证书或者多个 AltSubject 名称,浏览器可能会重用现有的连接。例如:

你有一个 a.example.org 的证书,它还有另外一个名称 b.example.org。你在浏览器中打开 URL https://a.example.org/,用另一个标签页加载 https://b.example.org/。

在重新打开一个新的连接之前,浏览器看到它有一个到 a.example.org 的连接并且证书对于 b.example.org 也可用。因此,它在第一个连接上面发送第二个标签页的请求。

这种连接重用是刻意设计的,它使得使用了 HTTP/1 切分sharding来提高效率的站点能够不需要太多变化就能利用 HTTP/2。

Apache mod_h[ttp]2 还没有完全实现这点。如果 a.example.org 和 b.example.org 是不同的虚拟主机, Apache 不会允许这样的连接重用,并会告知浏览器状态码 421 Misdirected Request。浏览器会意识到它需要重新打开一个到 b.example.org 的连接。这仍然能工作,只是会降低一些效率。

 

 

Nginx 编译、配置 HTTP/2

Nginx升级到了1.11.1,因为Nginx1.9.5版本以上开始支持http2,所以才能用上了http2

nginx 支持 http2,参见 Module ngx_http_v2_module,需要加上编译参数 --with-http_v2_module

Nginx 编译命令:

./configure --user=www --group=www --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module

完整编译命令:

创建www用户组及www用户,如果之前php-fpm没有创建,这里要创建。
# /usr/sbin/groupadd www
# /usr/sbin/useradd -g www www

# tar zxvf nginx-1.12.1.tar.gz
# cd nginx-1.12.1/
#./configure --user=www --group=www --prefix=/usr/local/nginx --with-pcre --with-http_stub_status_module --with-http_sub_module --with-http_ssl_module --with-stream_ssl_module --with-http_v2_module --with-http_realip_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_auth_request_module --with-mail --with-mail_ssl_module --with-threads --with-stream --with-stream_ssl_module

Nginx 格式化后的编译命令:

./configure \
--prefix=/usr/local/nginx \
--sbin-path=/usr/sbin/nginx \
--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=nobody \
--group=nobody \
--with-pcre \
--with-http_v2_module \
--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-mail \
--with-mail_ssl_module \
--with-file-aio \
--with-ipv6 \
--with-threads \
--with-stream \
--with-stream_ssl_module

 

Nginx 配置 HTTP/2

nginx.conf 配置文件中,修改一行如下:listen 443 ssl http2;

# HTTPS server
server {
	# listen          443 ssl;
	listen          443 ssl http2;
	server_name     mimvp.com;       # 域名可以有多个,用空格隔开 mimvp.com mimvp.cn
	....
}

提示错误:

the "listen ... http2" directive is deprecated, use the "http2" directive instead in /usr/local/nginx/conf/nginx_proxy.mimvp.com.conf:112

解决方案:

在nginx 1.25版本之后,需把 listen 443 ssl http2; 改为如下两行

server 
    listen      127.0.0.1:443 ssl;
    http2       on;

 

检测网站是否支持 HTTP/2

1)Firefox 浏览器查看:开发者模式 —> Network  —> 网页头信息 Headers —> Version: HTTP/2.0

2)Safari 浏览器查看:开发者模式 —> Resources  —> 网页头信息  —> Resource  —> Protocol  HTTP/2

3)Chrome 浏览器查看:开发者模式 —> Network  —>  右键标题勾选 Protocol —> 查看 Protocol 为 h2

4)Chrome 浏览器查看:开发者模式 —> Console  —>  输入 window.chrome.loadTimes() —> 结果中查看 connectionInfo: "h2"

 

5)浏览器插件:

Chrome 插件:HTTP/2 and SPDY indicator ,HTTP Indicator

Firefox 插件:HTTP/2 Indicator ,Web Site HTTP/2.0 detector

 

6)第三方网站检测:

https://tools.keycdn.com/http2-test

https://tool.bnxb.com/http2test

 

7)curl 查看 http 1.1 和 http 2

http 1.1 命令:/usr/local/php5/bin/curl --head 'https://proxy.mimvp.com'

http 2 命令:/usr/bin/curl --head --http2 'https://proxy.mimvp.com'

$ /usr/local/php5/bin/curl --head 'https://proxy.mimvp.com'
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 13 Feb 2020 15:23:21 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
Set-Cookie: MIMVPSESSID=u38sgefj5ohtk17ifqaoujcm3d; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Vary: proxys-zjk1


$ /usr/bin/curl --head 'https://proxy.mimvp.com'        
HTTP/2 200 
server: nginx
date: Thu, 13 Feb 2020 15:19:39 GMT
content-type: text/html; charset=UTF-8
vary: Accept-Encoding
set-cookie: MIMVPSESSID=5jr831703btlacrlg2hpuc2s2n; path=/
expires: Thu, 19 Nov 1981 08:52:00 GMT
cache-control: no-store, no-cache, must-revalidate
pragma: no-cache
vary: proxys-zjk1

$ /usr/bin/curl --head --http2 'https://proxy.mimvp.com'
HTTP/2 200 
server: nginx
date: Thu, 13 Feb 2020 15:19:43 GMT
content-type: text/html; charset=UTF-8
vary: Accept-Encoding
set-cookie: MIMVPSESSID=dg2454ta6m692ksi4kud940obh; path=/
expires: Thu, 19 Nov 1981 08:52:00 GMT
cache-control: no-store, no-cache, must-revalidate
pragma: no-cache
vary: proxys-zjk1

 

8)Chrome 查看链接:chrome://net-internals/#http2 (已废弃)

图片效果请见米扑博客:Let’s Encrypt 加密SSL证书并强制启用HTTPS访问

 

示例演示

访问米扑代理:https://proxy.mimvp.com

1、Firefox 浏览器查看 (推荐

开发者模式 —> Network  —> 网页头信息 Headers —> Version: HTTP/2.0

 

2、Chrome 浏览器查看 推荐

开发者模式 —> Network  —>  右键标题勾选 Protocol —> 查看 Protocol 为 h2

 

HTTP 速度测试对比

 

 

参考推荐:

Let’s Encrypt 在2018年1月发行通配符域名https证书

LNMP(CentOS+Nginx+Mysql+PHP)服务器环境配置

HTTP返回状态值详解

HTTP协议中POST、GET、HEAD、PUT等请求方法总结

Apache 和 Nginx 编译、配置、验证 HTTP/2 协议

十大免费SSL证书:网站免费添加HTTPS加密

https网站引用http路径的js和css失效解决办法

HTTP 协议的历史进化演变 (推荐

HTTPS 和 SSL/TLS 协议

HTTPS 和 HTTP 站点的优劣

https SSL 免费证书服务申请 (推荐

https 免费证书申请与安装

httpd使用ssl模块配置HTTPS

Nginx使用ssl模块配置HTTPS

Nginx 配置文件添加 http 授权

Apache 强制 HTTP 全部跳转到 HTTPS

Mac 安装Nginx with-http_ssl_module

Apache Httpd 开启 HTTPS 和 HTTP/2 (第三方)

如何在 Apache 中启用 HTTP/2 (第三方)