以 Ubuntu 14.04 为例,由于系统版本的原因及后续对 ALPN 的支持需要等原因,需要先升级 Nginx、OpenSSL。Nginx 在1.9.5后支持 HTTP/2,OpenSSL 在1.0.2后支持 ALPN,可以使用 nginx -vopenssl version 来查看当前版本。

升级 Nginx 和 OpenSSL


为了 Nginx 编译时候能够使用新版 OpenSSL,后者应优先升级。

添加以下 apt 仓库:

# add-apt-repository -y ppa:ondrej/php

更新资源列表:

# apt-get update

升级 OpenSSL:

# apt-get upgrade openssl  

删除旧版 Nginx,请记得备份配置文件:

# apt-get autoremove --purge nginx nginx-common

添加新版本 Nginx 的源,创建/etc/apt/sources.list.d/nginx.list文件并加入以下内容:

deb http://nginx.org/packages/mainline/ubuntu/ trusty nginx
deb-src http://nginx.org/packages/mainline/ubuntu/ trusty nginx

添加 Nginx 签名 PGP:

# wget -q -O- http://nginx.org/keys/nginx_signing.key | apt-key add -

同步源索引,更新资源列表:

# apt-get update

安装新版本 Nginx:

# apt-get install nginx

使用 nginx -V 命令来查看是否有 --with-http_v2_module 这个支持 HTTP/2 的模块。

因为目前浏览器只支持 HTTP/2 Over TLS,所以要开启 HTTP/2,必须先启用 HTTPS。由于 Chrome 51 版本之后不再支持 TLS 的 NPN(Next Protocol Negotiation) 扩展,只支持 ALPN(Application-Layer Protocol Negotiation),因此如果服务器不支持 ALPN,这些浏览器就无法支持 HTTP/2,仍然将使用 HTTP/1.1。

申请 SSL 证书


*证书不同类型费用不等,有免费的证书,也有昂贵的 EV 证书,需要去 CA 机构申请。基本的证书只支持一个 Common name,即单名称证书。比如 www.example.com 和 example.com 只需购买 www.example.com 的证书,若要支持更多子域名需要购买带多 SAN 的证书或带 Wildcard(通配符)的证书,可以支持 example.com 和 .example.com。一般来说,若你拥有超过 9 个子网域时,带通配符的证书较为经济;否则,可以只购买 1 个或更多单名称证书。

在服务器上需要先生成一个 CSR(Certificate Signing Request),以下会产生一个 2048 位的RSA密钥组,随着时间推移,电脑处理成本变得更低之后,密钥大小也必须增加。2048 位是目前的最佳取舍。

# openssl genrsa -des3 -out example.com.key 2048
# openssl req -new -key example.com.key -out example.com.csr

以上过程会提示输入一个 Passphrase 和一些基本地区和个人信息。其中 Common Name 一项最重要,请填写你的 FQDN

之后需要移除私钥的 Passphrase,因为尽管在某些时候这有助于提高安全性,但在重启 Nginx 的时候会产生错误并且一直需要重新输入 Passphrase 来恢复 Nginx 启动。

# cp example.com.key example.com.key.org
# openssl rsa -in example.com.key.org -out example.com.key

现在将 example.com.csr 里的内容粘贴到 CA 机构要你填写的 CSR 栏内提交,视 CA 类型,发送 CSR 给他们的方式也有所不同。然后选择一种验证方式,如 E-mail 或 DNS 等,然后等候处理。

处理成功后,会收到一个 .crt 文件,就是证书了。还有一个 .ca-bundle 用于证书链,这个后面会提到。现在把这两个文件上传到服务器上。

Nginx 配置


有些浏览器不接受那些众所周知的证书认证机构签署的证书,而另外一些浏览器却接受它们。这是由于证书签发使用了一些中间认证机构,这些中间机构被众所周知的证书认证机构授权代为签发证书,但是它们自己却不被广泛认知,所以有些用户端不予识别。针对这种情况,证书认证机构提供一个证书链的包裹,用来声明众所周知的认证机构和自己的关系,需要将这个证书链包裹与服务器证书合并成一个文件。在这个文件里,服务器证书需要出现在认证方证书链的前面:

# cat example.com.crt ca-bundle.crt > example.com.chained.crt

如果服务器证书和认证方证书链合并时顺序弄错了,Nginx 就不能正常启动。

生成 2048 位赫尔曼密钥:

# openssl dhparam -out dhparam.pem 2048

Nginx 配置:

server {
    listen 80;
    listen [::]:80;
    server_name www.example.com example.com;
    return 301 https://$host$request_uri; #将所有 HTTP请求网域名称转址到HTTPS。
    }
server {
    listen 443 ssl http2 default_server; #开启 HTTP/2。
    listen [::]:443 ssl http2 default_server;
    server_name www.example.com example.com;
    #......
        
    add_header Strict-Transport-Security \"max-age=31536000; proload\"; #HSTS,告诉浏览器在指定时间内通过 HTTPS 访问该站,防止降级攻击。
    
    
    ssl_certificate /path/to/example.com.chained.crt;
    ssl_certificate_key /path/to/example.com.key;
    ssl_dhparam /path/to/dhparams.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #只使用安全的协议。
    ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:DES-CBC3-SHA; #禁用不安全的加密。
    ssl_session_cache shared:SSL:5m;
    ssl_session_timeout 1h;
    ssl_prefer_server_ciphers on;
    ssl_stapling on;
    ssl_stapling_verify on;
    }

重启 Nginx:

# service nginx restart

更多安全的配置详细,参考这里。测试网站安全性能 - SSL Labs

This site

如果配置正确,HTTPS 和 HTTP/2 就会生效启用了。

HTTP/2

除了使用浏览器的开发者工具查看协议以外,还可以使用一些 CLI 工具检测,但因 ALPN 支持程度有别,最终以浏览器为准。查看当前 Chrome 使用 HTTP/2 的连接: chrome://net-internals/#http2

# npm install -g is-http2-cli
# is-http2 kickshaw.me
> ✓ HTTP/2 supported by kickshaw.me
> Supported protocols: h2 http/1.1

CDN 支持


以 Cloudflare 为例:

CF_SSL

如果开启 Flexible SSL 模式,则用户端到 CDN 是 HTTPS 连接,而 CDN 到服务器则是 HTTP 连接(不需要服务器上有证书)。Full SSL 模式则 CDN 到服务器也是 HTTPS 连接,但是不验证证书。Full SSL(strict) 模式则会验证证书。因此,如果 Nginx 定义了 HTTP 301 网域名称转址到 HTTPS,使用 Flexible 模式会陷入 301 无限网域名称转址循环之中。若要使用 Flexible 模式,请使用 Cloudflare 的 Page Rule 功能 Always Use HTTPS 取代 Nginx 的 301 重网域名称转址。在我们配置了服务器证书的情况下,推荐使用 Full SSL(strict) 模式。

Refference:

Fixing redirect loops when using Flexible SSL

Recompile NGINX with OpenSSL 1.0.2+ for HTTP/2 via ALPN - Ubuntu 14.04

Nginx - Configuring HTTPS servers

Tools for debugging, testing and using HTTP/2