Recently while upgrading to Nginx 1.9.6 on Ubuntu 14.04.2 LTS, I encountered a puzzling situation where HTTP/2 wasn't being served despite having all the correct configurations in place. Let's dive into the investigation and solution.
First, let's verify the essential components for HTTP/2 support:
# Verify Nginx version and modules
nginx -V | grep http_v2
# Sample working http2 configuration
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
...
}
Several factors could prevent HTTP/2 from working:
- SSL certificate issues (self-signed certs might cause fallback)
- Outdated OpenSSL version
- Browser caching or forced protocols
- Missing ALPN support
Use these commands to verify HTTP/2 support:
# Check protocol with curl
curl -I --http2 https://yourdomain.com
# Alternatively with openssl
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com -nextprotoneg ''
Based on the configuration shown, here are potential fixes:
# 1. Ensure modern cipher suites
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384...';
# 2. Enable ALPN explicitly
ssl_prefer_server_ciphers on;
# 3. Verify system OpenSSL version
openssl version
Here's a complete configuration that worked for me:
server {
listen 443 ssl http2;
server_name 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;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384...';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Other server directives...
}
When facing HTTP/2 issues with Nginx, first verify if the module is properly loaded:
nginx -V 2>&1 | grep -o with-http_v2_module
Your output shows the module is included in the build, which confirms basic compatibility.
HTTP/2 has strict TLS requirements that differ from HTTP/1.1:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256...';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
Modern browsers require specific conditions for HTTP/2:
# Check Chrome's protocol through Developer Tools:
1. Open Chrome DevTools (F12)
2. Navigate to Network tab
3. Right-click header columns and enable "Protocol"
4. Reload page and verify protocol
Here's a verified HTTP/2 configuration that works with Chrome:
server {
listen 443 ssl http2;
server_name example.com;
# Modern TLS configuration
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256...;
ssl_prefer_server_ciphers on;
# HTTP/2 optimization
http2_push_preload on;
http2_max_concurrent_streams 100;
# Root configuration
root /var/www/html;
index index.html;
}
1. Self-signed certificates often prevent HTTP/2 negotiation - use Let's Encrypt instead
2. Outdated OpenSSL versions may lack ALPN support - upgrade to 1.0.2+
3. Mixed content (HTTP resources on HTTPS page) can cause fallback
4. Old Nginx versions may need explicit http2
in listen directive
Use these commands to test HTTP/2 support:
curl -I --http2 https://yoursite.com
openssl s_client -connect yoursite.com:443 -nextprotoneg ''