配置了HTTP/2的Nginx无法传送HTTP/2。

我在Nginx配置方面遇到了问题。我升级到nginx 1.9.6来测试http/2,但它在我的服务器上不起作用。 我使用的是Ubuntu 14.04.2 LTS。 以下是nginx -V输出内容:
nginx version: nginx/1.9.6
built with OpenSSL 1.0.2d 9 Jul 2015
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-log-path=/var/log/nginx/access.log --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --lock-path=/var/lock/nginx.lock --pid-path=/var/run/nginx.pid --with-pcre-jit --with-debug --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_geoip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_realip_module --with-http_stub_status_module --with-http_ssl_module --with-http_sub_module --with-http_xslt_module --with-http_v2_module --with-stream --with-ipv6 --with-mail --with-mail_ssl_module --with-openssl=/build/nginx-GFP362/nginx-1.9.6/debian/openssl-1.0.2d --add-module=/build/nginx-GFP362/nginx-1.9.6/debian/modules/nginx-auth-pam --add-module=/build/nginx-GFP362/nginx-1.9.6/debian/modules/nginx-echo --add-module=/build/nginx-GFP362/nginx-1.9.6/debian/modules/nginx-upstream-fair --add-module=/build/nginx-GFP362/nginx-1.9.6/debian/modules/nginx-dav-ext-module --add-module=/build/nginx-GFP362/nginx-1.9.6/debian/modules/nginx-cache-purge

这是我的虚拟主机配置:

server {
    listen         80;
    server_name    localhost;
    return         301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2; ## listen for ipv4; this line is default and implied

    root /var/www/rendez-vous;
    index index.phtml index.html index.htm;

    # Make site accessible from http://localhost/
    server_name localhost;
    ssl_certificate /etc/nginx/certificates/myeventsportal/server.crt;
    ssl_certificate_key /etc/nginx/certificates/myeventsportal/server.key;

/...
如果我用最新版本的Chrome浏览器访问我的网站,它只能通过http/1.1协议提供。

1你有没有在https://www.nginx.com/blog/nginx-1-9-5/上查看注意事项部分? - Drifter104
你清除了浏览器缓存吗?试试使用隐身窗口。 - JayMcTee
隐身窗口没有改变任何东西。我阅读了注意事项部分,唯一的部分是ssl_prefer_server_ciphers,但我没有握手错误。 - throrin19
这是由于Web服务器发送的标头引起的,您的Web服务器是否配置为发送HTTP/2.0。 - Martin Barker
3个回答

我刚遇到了同样的问题,但我想我知道为什么会发生这种情况。nginx 1.9.6在Ubuntu 14.04上不是一个原始软件包,所以你可能是从nginx PPA获取的。这没问题,但这些软件包是使用14.04的原始库构建的,也就是说使用的是OpenSSL 1.0.1f。不幸的是,这个版本的OpenSSL不包含RFC7301 ALPN支持,而这是正确进行HTTP/2协商所需的;它只支持现已弃用的NPN。看起来Chrome已经移除了对NPN的支持,因此无法在没有ALPN的情况下进行HTTP/2连接的协商。另一方面,Firefox 41仍然支持NPN,所以你应该能够在其中使用HTTP/2。

你可以像这样测试你的服务器-你需要在客户端安装OpenSSL 1.0.2d(运行openssl version来检查):

使用ALPN进行测试:

echo | openssl s_client -alpn h2 -connect yourserver.example.com:443 | grep ALPN
如果ALPN正常工作,您应该看到:
ALPN protocol: h2
否则你会得到:
No ALPN negotiated

测试与NPN:

echo | openssl s_client -nextprotoneg h2 -connect yourserver.example.com:443
如果那样做的话,你会得到:
Next protocol: (1) h2
No ALPN negotiated
那意味着它正在成功地通过NPN协商HTTP/2连接,这是Firefox的做法。 那么如何解决这个问题呢?我唯一能想到的办法就是从PPA(我使用这个来为PHP安装,其中也包含openssl)安装一个更新版本的openssl,并构建与之链接的自己的nginx。你可以通过运行nginx -V来找到现有nginx构建的配置参数,然后使用这些参数来构建你自己的版本。 更新:我发现Chrome不支持使用NPN的HTTP/2的原因并不是因为它不支持NPN(尽管在某个时候会被弃用),而是因为它特别不支持带有NPN的h2,正如chrome://net-internals/#http2页面所示。

Chrome HTTP/2 info


我刚刚注意到你已经在运行openssl 1.0.2d了 - 但是这些测试可能仍然会有用。 - Synchro
我的nginx软件包使用的是最新的openssl版本,但是ubuntu 14.04却安装了一个过时的版本。如果我没记错的话,应该是1.0.1f。 - throrin19
是的,那就是我说的。 - Synchro
1对于第一个命令,我遇到了一个错误:“未知选项 -alpn”,而第二个命令正常运行。 - throrin19
这意味着您没有运行openssl 1.0.2d客户端。安装一个,它就会正常工作。顺便说一句,我刚从PPA重新构建了nginx,现在对我来说完美无缺地运行。 - Synchro
非常感谢您提供了如此具体的信息和测试方法。重新编译nginx至1.0.2h版本后,一切都恢复正常运行了。 - dezinezync
2现在,在2016年的尾巴上,这个问题的进展如何?我仍然看到nginx无法作为HTTP2提供文件。 - vsync
请检查您的版本。14.04 版本的软件包仍然太旧,需要手动构建。较新的 Ubuntu 发行版应该会更好一些。 - Synchro
提供的链接“这个”无法使用。 - robsch
谢谢:我使用这个PPA来安装带有更新的openssl的Nginx,并且对我起作用。链接是:https://launchpad.net/~ondrej/+archive/ubuntu/nginx - Kenmore
我的nginx: nginx/1.12.2 OPENSSL: OpenSSL 1.1.0h 27 Mar 2018 没有ALPN - Ravi Soni

简短版本。

我发现当浏览电脑上的SSL/TLS过滤开启时,ESET防病毒软件会阻止HTTP/2正常工作。请检查确保您的防病毒软件没有对SSL/TLS进行过滤。


简短版

我遇到了与帖子作者相同的问题,但有一个有趣的转折。我将服务器配置升级到了nginx 1.12.1,并使用OpenSSL 1.0.2.g进行编译。初步检查时,它似乎“解决”了HTTP/2不工作的问题。在我的浏览器中,我可以看到服务器证书已由Let's Encrypt验证。内容也是通过HTTP/2提供的。

过了一段时间,我发现相同的页面和资源不再通过HTTP/2提供。巧合的是,该网站不再由Let's Encrypt验证,而是由Eset验证?!!!令我惊讶的是,新的http2问题根本与我的服务器配置无关。事实证明,我在本地计算机上的杀毒软件中开启了SSL/TLS过滤功能,这导致了问题的出现。解决方案是关闭杀毒软件中的SSL/TLS过滤功能。一旦我关闭了它(并重新启动了计算机),HTTP/2就恢复正常了,并且证书再次被Let's Encrypt验证。

要了解如何在ESET中关闭SSL/TLS的操作步骤,请查看http://support.eset.com/kb3126/?locale=en_US

这是我遇到的问题。在一个浏览器中可以正常工作(该浏览器未被防火墙过滤),但在其他任何浏览器中都无法正常工作,这让我免于疯狂。 - Dev
你真是个超级天才。原来是ESET,我花了四天的时间才找到问题所在。在这个Linux的世界里,我尝试了一切可能的方法。简直不敢相信是因为ESET,我一直在折磨我的VPS。 - Airy
1我在https://forum.eset.com/topic/17640-eset-is-filtering-ssltls-traffic-thus-downgrading-latest-http2-protocol/上开了一个支持票据。 - Airy

如 Synchro 在他的回答中所说,问题在于大多数 nginx 包都没有使用 OpenSSL 1.0.2 来构建。对 ALPN 的编译需要相关 OpenSSL 开发源码中的符号。 你可以尝试使用官方的 nginx 发行版,选择 xenial 而不是 trusty。这在我使用 Debian Jessie 和 jessie-backports OpenSSL 1.0.2 上有效,也许对你有用。然而,请注意这是一个不受支持的配置 - 重新构建是“正确”的答案。