Enable HTTP/2 on Nginx

Experimental support for HTTP/2 became available in Nginx version 1.9.5 (mainline). It is really easy to enable, and I’ll show you how.

First of all: If you’re not running the latest version yet, I recommend that you upgrade Nginx to the latest version.

If you’re already running SPDY, please note that the SPDY module have been replaced with the HTTP/2 module in Nginx. Fortunately, to run HTTP/2 you just need to upgrade to Nginx 1.9.5 or later and replace spdy with http2 on your listen directive line.

Now, make sure your version of Nginx is compiled with HTTP/2 support:

$ nginx -V

Make sure you can find --with-http_v2_module somewhere in that output. If you don’t, upgrade to a build that supports HTTP/2 – like the link I posted above.

Now it is as simple as adding a single word to your Nginx config. Open the server block config for your HTTPS site (all implementations of HTTP/2 require HTTPS), and change this line:

listen 443 ssl;


listen 443 ssl http2;

and reload your config:

$ service nginx reload

(If you haven’t enabled HTTPS yet, check this post: Securing Nginx with HTTPS)

Now all HTTP/2 enabled visitors should get your site delivered over HTTP/2, while older browsers get regular SSL (really: TLS). Major browsers supports HTTP/2 now.

The only thing left, is that you probably want to route all visitors that comes through standard HTTP on port 80 to your HTTPS port 443.

If you haven’t already done so, you are now ready to optimize HTTPS on Nginx.


  1. Might be a good idea to mention that http2 is replacing spdy

    nginx: “spdy”: ngx_http_spdy_module was superseded by ngx_http_v2_module

    1. Thanks for the feedback. Yes, since the HTTP/2 module is incompatible with the SPDY module, the SPDY module has been removed. If you’re already running SPDY, it is just a matter of replacing spdy with http2 on your listen directive line.
      I’ve updated the post with a notice on upgrading from SPDY to HTTP/2.

      1. Hello Bjørn,

        Thank you for the useful material. Could you recommend smthn else to read regarding updating Nginx confs to move from spdy to http2 params?

  2. If you have a lot of virtual hosts, maybe spread over multiple files, you can use this command to change all instances of spdy to http2 in all the files in the current directory, including any subdirectories:

    find . -type f -exec sed -i "s/spdy/http2/g" {} \;

  3. You’ll also need to remove any spdy module directives, such as spdy_keepalive_timeout, spdy_headers_comp, etc., and you won’t need this anymore either:

    add_header Alternate-Protocol 443:npn-spdy/3;

  4. Do you know if there will be any problems when using OpenSSL 1.0.1 instead of the recommended 1.0.2d? It seems to work fine with the old version but I only tested with Firefox and Chrome and somewhere on the nginx website it said that 1.0.2d is required…

    1. This is not the first time I hear that OpenSSL 1.0.2* is a requirement for HTTP/2, but my Nginx reports “built with OpenSSL 1.0.1f 6 Jan 2014”, and it clearly works. IDK ¯\_(ツ)_/¯

          1. Updated:::
            I does not work for me
            My nginx : nginx/1.12.2 OPENSSL: OpenSSL 1.1.0h 27 Mar 2018
            NO ALPN Negotitation.

    2. Same here. Running on 1.0.1 and working (but ALPN is disabled, don’t know what the consequences of that are)

    3. Although enable http2 in directive but my browser doesn’t recognize http2
      Maybe I need to compile with Nginx source?

  5. I ran into a strange effect. Maybe you have an idea: I’ve used http2 for a week and just wanted to expertiment temporarily without it. So I changed the listen line from

    listen 443 ssl http2;
    listen 443 ssl;

    and restarted nginx.

    When checking with my browser the site is still served on http2 even though it is disabled. Not sure what I’m doing wrong here 8-/

    1. I’ve seen the same thing, so at first I thought maybe HTTP/2 was enabled by default. But when I configured a new server, HTTP/2 wasn’t enabled without specifying the http2 directive.

  6. Just found something useful: Openssl1.0.2d is now part of the ondrej php5 Ubuntu repo. If you don’t want the latest php5 but just the openssl1.0.2d as recommended by nginx then just go here:



    libssl1.0.2_1.0.2d-2+deb.sury.org~trusty+1_amd64.deb (replace trusty with correct version)
    openssl_1.0.2d-2+deb.sury.org~trusty+1_amd64.deb (replace trusty with correct version)

    and install them with dpkg -i

  7. The Openssl problem relates to the ALPN Request sent from the browser to the webserver. The Chrome team is planning to remove the predecessor request NPN to only allow ALPN.

    ALPN is only supported by Openssl 1.0.2. Do you know if there is a PPA to install Openssl 1.0.2 and also nginx compiled with this Openssl version?

    Ubuntu Server version 15.04 (I think Openssl 1.0.2 is available only for 15.10)

  8. Bjorn,

    Thanks for your blog posts I got http2 and installed Nginx. Really nicely laid out info.

    In my current setup Nginx (only SSL 443)–>Varnish (80 http) –> Apache. Do you think creating Nginx vhost for all my sites (http 80) so they can use Nginx https2 will have any benefit.?????!

    If was thinking about having Varnish cache dynamics and Nginx the css, js and images.

    What are you thoughts or experience?

    1. Hi Rafael

      You shouldn’t serve anything over http (port 80), only do redirects to https (port 443) – which should have HTTP/2 enabled.

      Nginx serves static resources extremely fast, so whenever I use Nginx in front of Varnish, I serve all static resources (images, css, js etc) from Nginx and only pass the expensive requests to Varnish. This sounds like the exact same setup that you are mentioning :-)

  9. Works like a charm; thank you for posting this solution! I’m using ISPConfig 3.1 Beta to set up everything, and fortunately it already supports HTTP2 (falling back to SPDY if HTTP2 support is not found on nginx).

    Very cool :-)

  10. I found that current Chrome will refuse to connect with http2 enabled unless you are strict with your ciphers. Digital Ocean suggests the following, which I confirm does work:


    The only browser that cannot connect to this is IE6 according to SSL Labs.

    Originally I was using ‘HIGH:!aNULL:!MD5;’ but chrome wants nothing to do with that when using http2.

    1. Hi Vick,

      you saved my day, Firefox 47 too can’t negotiate HTTP2 if the ssl_ciphers list is too broad.
      Worse, it’s not eventrying HTTPS/1.1 ;(

      thanks to your tip I found a solution.

  11. Hi,
    I tried doing the http2 with ssl in the nginx(1.10.3-stable and 1.11.9-mainline) version. But I am running into
    “SSL_read() failed (SSL: error:14094418:SSL routines:ssl3_read_bytes:tlsv1 alert unknown ca:SSL alert number 48) while processing HTTP/2 connection”

    I am not sure why this would. Below is the config:
    user www-data;
    worker_processes auto;
    events {
    use epoll;
    worker_connections 128;
    multi_accept on;

    http {
    map $http_upgrade $connection_upgrade {
    default upgrade;
    ” close;
    proxy_read_timeout 86400;

    server {
    listen 443 ssl http2 default_server;

    ssl_password_file /etc/nginx/nginx_sscert.pass;
    ssl_certificate /etc/nginx/ssl/nginx_sscert.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx_sscert.key;

    ssl_protocols TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 180m;

    ssl_session_tickets off;
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;

    root /var/www/dist;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    gzip on;
    gzip_min_length 1000;
    gzip_types text/plain application/x-javascript text/xml text/css;

    client_body_temp_path /tmp/nginx;
    proxy_temp_path /tmp/nginx;
    error_log /var/log/nginx/debug.log debug;

    What are your thoughts on this? Any pointers on this is much appreciated.


    1. These instructions are OS independent. They are just on how to configure Nginx.

      But: since Google dropped support for NPN in Chrome and requires ALPN, you must have OpenSSL 1.0.2 or newer installed on the server. For Ubuntu, that will in practice mean that you need Ubuntu 15.10 (maybe 15.04?) or newer. I don’t believe CentOS 7 have OpenSSL 1.0.2 in the official repo, but I’ve heard that you can install it via a 3rd party repo. If you’re using CentOS, you want to check with someone else than me :-)

Comments are closed.