Getting HTTP/2 Working on Apache

apache-2.4Apache2http2openssl

I'm more of an nginx head these days (and I don't have any problems getting HTTP/2 working with that) but I'm completely flummoxed by Apache and cannot seem to get it to serve a site over HTTP/2.

The Server is Debian 8. I've installed Apache 2.4.27 and openssl1.1.0f by enabling the Debian 'testing' repo.

Here's some useful information and server configs:

Apache Version

root@server:~# apachectl -V
Server version: Apache/2.4.27 (Debian)
Server built:   2017-07-16T21:01:10
Server's Module Magic Number: 20120211:68
Server loaded:  APR 1.5.1, APR-UTIL 1.5.4
Compiled using: APR 1.5.2, APR-UTIL 1.5.4
Architecture:   64-bit
Server MPM:     prefork
  threaded:     no
    forked:     yes (variable process count)
Server compiled with....
 -D APR_HAS_SENDFILE
 -D APR_HAS_MMAP
 -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
 -D APR_USE_SYSVSEM_SERIALIZE
 -D APR_USE_PTHREAD_SERIALIZE
 -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
 -D APR_HAS_OTHER_CHILD
 -D AP_HAVE_RELIABLE_PIPED_LOGS
 -D DYNAMIC_MODULE_LIMIT=256
 -D HTTPD_ROOT="/etc/apache2"
 -D SUEXEC_BIN="/usr/lib/apache2/suexec"
 -D DEFAULT_PIDLOG="/var/run/apache2.pid"
 -D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
 -D DEFAULT_ERRORLOG="logs/error_log"
 -D AP_TYPES_CONFIG_FILE="mime.types"
 -D SERVER_CONFIG_FILE="apache2.conf"

Apache Modules

root@server:~# apachectl -M
Loaded Modules:
 core_module (static)
 so_module (static)
 watchdog_module (static)
 http_module (static)
 log_config_module (static)
 logio_module (static)
 version_module (static)
 unixd_module (static)
 access_compat_module (shared)
 alias_module (shared)
 auth_basic_module (shared)
 authn_core_module (shared)
 authn_file_module (shared)
 authz_core_module (shared)
 authz_host_module (shared)
 authz_user_module (shared)
 autoindex_module (shared)
 deflate_module (shared)
 dir_module (shared)
 env_module (shared)
 expires_module (shared)
 filter_module (shared)
 headers_module (shared)
 http2_module (shared)
 mime_module (shared)
 mpm_prefork_module (shared)
 negotiation_module (shared)
 php5_module (shared)
 reqtimeout_module (shared)
 rewrite_module (shared)
 setenvif_module (shared)
 socache_shmcb_module (shared)
 ssl_module (shared)
 status_module (shared)

OpenSSL Version

root@server:/root# openssl version
OpenSSL 1.1.0f  25 May 2017

Apache Base Config

root@server:~# cat /etc/apache2/apache2.conf
DefaultRuntimeDir ${APACHE_RUN_DIR}
PidFile ${APACHE_PID_FILE}
Timeout 300
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5

User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}

HostnameLookups Off

ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn

IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf

Include ports.conf

<Directory />
    Options FollowSymLinks
    AllowOverride None
    Require all denied
</Directory>

<Directory /usr/share>
    AllowOverride None
    Require all granted
</Directory>

<Directory /var/www/>
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

AccessFileName .htaccess

<FilesMatch "^\.ht">
    Require all denied
</FilesMatch>

LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

IncludeOptional conf-enabled/*.conf

IncludeOptional sites-enabled/*.conf

Apache SSL Config

root@server:~# cat /etc/apache2/mods-available/ssl.conf
<IfModule mod_ssl.c>
    SSLRandomSeed startup builtin
    SSLRandomSeed startup file:/dev/urandom 512
    SSLRandomSeed connect builtin
    SSLRandomSeed connect file:/dev/urandom 512

    AddType application/x-x509-ca-cert .crt
    AddType application/x-pkcs7-crl .crl

    SSLPassPhraseDialog  exec:/usr/share/apache2/ask-for-passphrase

    SSLSessionCache shmcb:${APACHE_RUN_DIR}/ssl_scache(512000)
    SSLSessionCacheTimeout 300

    SSLCipherSuite 'EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS'

    SSLHonorCipherOrder on

    SSLProtocol all -SSLv3

    SSLCompression off
    SSLSessionTickets off

    SSLUseStapling on
    SSLStaplingResponderTimeout 5
    SSLStaplingReturnResponderErrors off
    SSLStaplingCache shmcb:/var/run/ocsp(128000)

    SSLOpenSSLConfCmd DHParameters "/etc/apache2/ssl/dhparam.pem"

    <FilesMatch "\.php$">
        SSLOptions +StdEnvVars
    </FilesMatch>

    BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
</IfModule>

Apache HTTP2 Config

root@server:~# cat /etc/apache2/mods-enabled/http2.conf
H2Push on
H2PushPriority * after
H2PushPriority text/css before
H2PushPriority image/jpeg after 32
H2PushPriority image/png after 32
H2PushPriority application/javascript interleaved

Apache Security Config

root@server:~# cat /etc/apache2/conf-available/security.conf
ServerTokens Prod
ServerSignature Off
TraceEnable Off

<IfModule mod_headers.c>
    Header set X-UA-Compatible "IE=Edge"
    Header always set Strict-Transport-Security "max-age=15768000"

    <FilesMatch "\.(appcache|atom|bbaw|bmp|crx|css|cur|eot|f4[abpv]|flv|geojson|gif|htc|ico|jpe?g|js|json(ld)?|m4[av]|manifest|map|mp4|oex|og[agv]|opus|otf|pdf|png|rdf|rss|safariextz|svgz?|swf|topojson|tt[cf]|txt|vcard|vcf|vtt|webapp|web[mp]|webmanifest|woff2?|xloc|xml|xpi)$">
        Header unset X-UA-Compatible
    </FilesMatch>

    Header always append X-Frame-Options SAMEORIGIN
    Header set X-Content-Type-Options nosniff
    Header set X-XSS-Protection "1; mode=block"
</IfModule>

<IfModule mod_setenvif.c>
    SetEnvIf Request_URI "^/(browserconfig\.xml|manifest\.json|robots\.txt|sitemap\.xml|.well-known/.*)$" dontlog
</IfModule>

<IfModule mod_rewrite.c>
    <FilesMatch "^/(browserconfig\.xml|manifest\.json|robots\.txt|sitemap\.xml)$">
        RewriteEngine on
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteRule . /index.php [L]
    </FilesMatch>
</IfModule>

<FilesMatch "(?:^|/)\.">
    Require all denied
</FilesMatch>

<FilesMatch "(?:\.(?:bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)|~)$">
    Require all denied
</FilesMatch>

Apache VirtualHost Config

root@server:~# cat /etc/apache2/sites-available/www_xxxxxx_com.conf
ServerName server

<VirtualHost *:80>
    ServerName xxxxxx.com
    Redirect 301 / https://www.xxxxxx.com/
</VirtualHost>

<VirtualHost *:80>
    ServerName www.xxxxxx.com
    Redirect 301 / https://www.xxxxxx.com/
</VirtualHost>

<VirtualHost *:443>
    ServerName xxxxxx.com
    Redirect 301 / https://www.xxxxxx.com/

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/xxxxxx.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/xxxxxx.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/xxxxxx.com/fullchain.pem
    SSLCACertificateFile /etc/letsencrypt/live/xxxxxx.com/fullchain.pem

    Protocols h2 http/1.1
</VirtualHost>

<VirtualHost *:443>
    ServerName www.xxxxxx.com
    ServerAdmin webmaster@localhost
    DocumentRoot /data/www_xxxxxx_com/public_html

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined env=!dontlog

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/xxxxxx.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/xxxxxx.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/xxxxxx.com/fullchain.pem
    SSLCACertificateFile /etc/letsencrypt/live/xxxxxx.com/fullchain.pem

    Protocols h2 http/1.1

    <Directory /data/www_xxxxxx_com/public_html/>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

I tested all this using https://tools.keycdn.com/http2-test which came out negative, plus using curl (version 7.55.1 – with HTTP2 support):

➜  ~ curl -v --http2 https://www.xxxxxx.com
* Rebuilt URL to: https://www.xxxxxx.com/
*   Trying XXX.XXX.XXX.XXX...
* TCP_NODELAY set
* Connected to www.xxxxxx.com (XXX.XXX.XXX.XXX) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /usr/local/etc/openssl/cert.pem
  CApath: /usr/local/etc/openssl/certs
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=xxxxxx.com
*  start date: Aug 24 11:07:00 2017 GMT
*  expire date: Nov 22 11:07:00 2017 GMT
*  subjectAltName: host "www.xxxxxx.com" matched cert's "www.xxxxxx.com"
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL certificate verify ok.

HTTP/1.1 200 OK
Date: Thu, 24 Aug 2017 15:30:35 GMT
Server: Apache
Strict-Transport-Security: max-age=15768000
X-Frame-Options: SAMEORIGIN
Upgrade: h2
Connection: Upgrade
Set-Cookie: PHPSESSID=knqh31ge9fqd60g4r40r7ngum4; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
X-UA-Compatible: IE=Edge
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Content-Type: text/html; charset=UTF-8

Does anyone have any idea what I'm missing here, because it's driving me bonkers?

Cheers!

Best Answer

Oh for goodness sake - why do I almost always figure stuff like this out AFTER I post the question. Oh well - hope this helps someone else.

The answer lies in the error log:

[Thu Aug 24 17:55:09.795729 2017] [http2:info] [pid 16949] AH03090: mod_http2 (v1.10.7, feats=CHPRIO+SHA256+INVHD+DWINS, nghttp2 1.24.0), initializing...
[Thu Aug 24 17:55:09.795846 2017] [http2:warn] [pid 16949] AH10034: The mpm module (prefork.c) is not supported by mod_http2. The mpm determines how things are processed in your server. HTTP/2 has more demands in this regard and the currently selected mpm will just not do. This is an advisory warning. Your server will continue to work, but the HTTP/2 protocol will be inactive.
[Thu Aug 24 17:55:09.827124 2017] [mpm_prefork:notice] [pid 16949] AH00163: Apache/2.4.27 (Debian) OpenSSL/1.0.2l configured -- resuming normal operations

So there we go - just use 'event' or 'worker' MPM instead of prefork.

Related Topic