How to Enable HTTP/2 with Nginx on RHEL 7

HTTP/2 is the major revision of the HTTP network protocol that brings multiplexed streams, header compression, and server push to web traffic. Compared to HTTP/1.1, HTTP/2 dramatically reduces page load times on sites with many assets by eliminating head-of-line blocking and reducing the overhead of repeated TLS handshakes across multiple connections. Nginx has supported HTTP/2 since version 1.9.5, and RHEL 7’s EPEL repository provides a recent-enough version to enable it. Because browsers only implement HTTP/2 over TLS, you must have a working HTTPS configuration in place before this tutorial begins. This guide covers version requirements, adding http2 to your listen directive, verifying ALPN support, and tuning nginx.conf for optimal HTTP/2 performance.

Prerequisites

  • RHEL 7 with Nginx installed from EPEL (version 1.10 or later recommended)
  • A valid TLS certificate configured in Nginx (Let’s Encrypt or commercial)
  • OpenSSL 1.0.2 or later (required for ALPN support)
  • Root or sudo access
  • Port 443 open in your firewall

Step 1: Verify Nginx Version and HTTP/2 Support

HTTP/2 support in Nginx requires version 1.9.5 or later. Check your installed version:

nginx -v
# nginx version: nginx/1.20.1

If Nginx is not installed or is below 1.9.5, install or update from EPEL:

sudo yum install -y epel-release
sudo yum install -y nginx

Next, verify that the Nginx binary was compiled with the http_v2_module:

nginx -V 2>&1 | grep -o 'http_v2_module'
# Expected output: http_v2_module

If the module is not listed, the package in your repository was not compiled with HTTP/2 support. In practice, the EPEL Nginx package on RHEL 7 includes it by default.

Step 2: Verify OpenSSL Version for ALPN Support

Browsers negotiate HTTP/2 using ALPN (Application-Layer Protocol Negotiation), an extension to the TLS handshake. ALPN requires OpenSSL 1.0.2 or later. RHEL 7 ships OpenSSL 1.0.2 as part of its default package set, so this is typically already satisfied.

openssl version
# OpenSSL 1.0.2k-fips  26 Jan 2017

Verify that Nginx was linked against this OpenSSL version:

nginx -V 2>&1 | grep 'built with OpenSSL'

If OpenSSL is below 1.0.2, ALPN will not work and browsers will silently fall back to HTTP/1.1 even with the http2 directive present.

Step 3: Add http2 to the Listen Directive

HTTP/2 in Nginx is enabled by appending http2 to the SSL listen directive in your server block. Open your site configuration file:

sudo nano /etc/nginx/conf.d/yourdomain.conf

Find the existing HTTPS listen line and add http2:

# Before
listen 443 ssl;

# After
listen 443 ssl http2;

A complete minimal HTTPS server block with HTTP/2 enabled looks like this:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name yourdomain.com www.yourdomain.com;

    ssl_certificate     /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         EECDH+AESGCM:EDH+AESGCM;
    ssl_prefer_server_ciphers on;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;

    root  /var/www/yourdomain.com;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$host$request_uri;
}

Note that http2 is a listen directive parameter — it is not a standalone directive elsewhere in the block. Each TLS-enabled server block that you want to serve over HTTP/2 needs its own listen 443 ssl http2 line.

Step 4: Apply Nginx Configuration Optimizations for HTTP/2

HTTP/2’s multiplexing changes how responses should be tuned. Add the following directives to the http block in /etc/nginx/nginx.conf for optimal performance:

http {
    # ...existing settings...

    # HTTP/2 push preload (optional, for assets you know the browser will need)
    # http2_push_preload on;

    # Increase maximum concurrent streams per connection
    http2_max_concurrent_streams 128;

    # Idle timeout for HTTP/2 connections
    http2_idle_timeout 3m;

    # Buffer size for reading the client request body
    client_body_buffer_size 128k;

    # Allow keep-alive connections to reduce connection overhead
    keepalive_timeout 65;
    keepalive_requests 1000;

    # Sendfile and tcp settings
    sendfile    on;
    tcp_nopush  on;
    tcp_nodelay on;

    # Gzip compression (HTTP/2 clients support it)
    gzip              on;
    gzip_vary         on;
    gzip_proxied      any;
    gzip_comp_level   6;
    gzip_types        text/plain text/css application/json
                      application/javascript text/xml application/xml
                      application/xml+rss text/javascript;
}

HTTP/2 multiplexes many requests over a single connection, so keepalive_requests 1000 ensures the connection remains open long enough for the browser to fully utilize multiplexing rather than cycling connections frequently.

Step 5: Test and Reload Nginx

sudo nginx -t

If the test reports syntax is ok and test is successful, reload:

sudo systemctl reload nginx

Step 6: Verify HTTP/2 with curl

Use curl with the --http2 flag to request the page over HTTP/2:

curl -I --http2 https://yourdomain.com/

Look for the protocol version in the output:

HTTP/2 200
server: nginx/1.20.1
content-type: text/html; charset=UTF-8

If you see HTTP/2 200 in the status line, HTTP/2 is working correctly. If you see HTTP/1.1 200, Nginx fell back to HTTP/1.1, which usually indicates a missing http2 in the listen directive or an OpenSSL version that does not support ALPN.

For a more detailed negotiation trace showing the ALPN protocol selection:

curl -v --http2 https://yourdomain.com/ 2>&1 | grep -E 'ALPN|Using HTTP'

Expected output includes lines like:

* ALPN, offering h2
* ALPN, offering http/1.1
* ALPN, server accepted h2
* Using HTTP2, server supports multi-use

Step 7: Verify with Browser Developer Tools

In Google Chrome or Firefox, open Developer Tools (F12), go to the Network tab, and reload the page. Right-click on any column header in the request list and enable the Protocol column. Requests served over HTTP/2 display h2 in that column. Requests still using HTTP/1.1 show http/1.1.

In Chrome, you can also verify ALPN negotiation by visiting chrome://net-internals/#http2 and looking for your domain in the active sessions list.

Step 8: Test TLS and ALPN with openssl s_client

Confirm ALPN negotiation at the TLS level:

openssl s_client -connect yourdomain.com:443 
    -alpn h2 
    -servername yourdomain.com 
    < /dev/null 2>&1 | grep -E 'ALPN|Protocol'

A successful ALPN negotiation shows:

ALPN protocol: h2

If the output shows No ALPN negotiated or shows http/1.1, the server is not advertising HTTP/2 via ALPN, which will prevent browsers from using it regardless of what the listen directive says.

Step 9: Common Troubleshooting

  • http2 not recognized in listen directive: Your Nginx binary was compiled without http_v2_module. Reinstall from EPEL or compile from source with --with-http_v2_module.
  • Browsers using HTTP/1.1 despite http2 in config: Check OpenSSL version (openssl version) and confirm it is 1.0.2 or later. Also ensure you are connecting to port 443 with a valid, trusted certificate.
  • SSL_ERROR_RX_RECORD_TOO_LONG in browser: You have http2 on a non-SSL listen directive. HTTP/2 without TLS (h2c) is not supported by any major browser.
  • Worker process crashing after enabling http2: Check /var/log/nginx/error.log for details. A common cause is a too-small worker_rlimit_nofile setting when many concurrent streams are opened.

Enabling HTTP/2 in Nginx on RHEL 7 is a single-line change — adding http2 to your SSL listen directive — but the full benefit requires a properly configured TLS stack with ALPN support, tuned keep-alive and buffer settings, and verification that both the server and clients are successfully negotiating the h2 protocol. With Nginx 1.10+ from EPEL and the system’s OpenSSL 1.0.2 package, RHEL 7 has everything needed to serve a modern, performant HTTP/2 site without rebuilding any packages from source.