Excitements, confusions and frustrations

Knowledge without action is wastefulness and action without knowledge is foolishness. - Al-Ghazali

Building Apache HTTP server from source with HTTP/2(SPDY) support

Posted at — Nov 13, 2016

I recently setup new server, for experimenting with new technical stacks. I have been planning to use it for comparisons and benchmarks. So, as a beginner, I decided to have my http server support HTTP/2.
My server has “Centos 7” installed, which has httpd version 2.4 on yum repo. Initially I thought I would just switch on a single module and it will be all over. But it turns out that, latest http server version provided by yum is 2.4.6, which didn’t have support for http2 module yet.So I decided to build it manually.

Why http2 module is not supported?

First of all, http2 spec does not require https. But all major browsers require https in order to use HTTP/2. And to support HTTP over TLS, Apache HTTP server requires mod_ssl, which needs openssl development libraries when built from source. HTTP/2 needs certain TLS extension called ALPN, which is provided for OpenSSL 1.0.2+ versions.

Steps

First switch to root user.

sudo su -
bash

Install dependencies

yum -y groupinstall "Development Tools"
yum install libnghttp2-devel pcre-devel zlib-devel wget -y

Install OpenSSL 1.0.2

mkdir -p /usr/local/src && cd /usr/local/src
wget https://www.openssl.org/source/openssl-1.0.2-latest.tar.gz
tar -zxf openssl-1.0.2-latest.tar.gz
cd openssl-1.0.2j
./config shared --prefix=/usr/local --openssldir=/usr/local/openssl

make
make test
make install

When installation is complete, confirm new version with command below.

/usr/local/bin/openssl version

Build http server

APR and APR-Util is required for compiling http server from source.

cd /usr/local/src
wget http://www-eu.apache.org/dist/httpd/httpd-2.4.23.tar.gz
wget http://www-eu.apache.org/dist//apr/apr-1.5.2.tar.gz
wget http://www-eu.apache.org/dist//apr/apr-util-1.5.4.tar.gz
tar -xzf httpd-2.4.23.tar.gz

Copy APR and APR-Util source code into library source directory of httpd source.

cd /usr/local/src/httpd-2.4.23/srclib
cp /usr/local/src/apr-1.5.2.tar.gz .      && tar -xzf apr-1.5.2.tar.gz      && mv apr-1.5.2 apr
cp /usr/local/src/apr-util-1.5.4.tar.gz . && tar -xzf apr-util-1.5.4.tar.gz && mv apr-util-1.5.4 apr-util

Build httpd

APACHE_DIR=/usr/local/apache
cd /usr/local/src/httpd-2.4.23
./configure      \
  --prefix=${APACHE_DIR}        \
  --with-included-apr           \
  --enable-mpms-shared=all      \
  --enable-mods-shared=all      \
  --enable-static-ab            \
  --enable-static-checkgid      \
  --enable-static-htdbm         \
  --enable-static-htdigest      \
  --enable-static-logresolve    \
  --enable-static-rotatelogs    \
  --enable-http2                \
  --with-ssl=/usr/local/

make
make install

# add extra library path (mainly openssl), to apache environment variables
echo "export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:/usr/local/lib64" \
>> ${APACHE_DIR}/bin/envvars

If some modules are missing from modules directory, or if you want to build extra manually, you compile them using apxs command. For example,

$APACHE_DIR/bin/apxs -i -c ./modules/generators/mod_cgi.c
$APACHE_DIR/bin/apxs -i -c ./modules/filters/mod_deflate.c

Configure httpd

For this part you can choose how to configure your Apache server. But for quick guidance, you use commands below.

mv    ${APACHE_DIR}/conf/{extra,conf.d}
mkdir ${APACHE_DIR}/conf/conf.d/OLD
mv    ${APACHE_DIR}/conf/conf.d/{*.conf,OLD/}
mv    ${APACHE_DIR}/conf/httpd.conf{,.OLD}

cp    ${APACHE_DIR}/conf/conf.d{/OLD/httpd-default.conf,}

Create a configuration file at ${APACHE_DIR}/conf/modules.conf for managing loaded modules.

vi ${APACHE_DIR}/conf/modules.conf

And insert following

LoadModule alias_module modules/mod_alias.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule autoindex_module modules/mod_autoindex.so
LoadModule deflate_module modules/mod_deflate.so
LoadModule dir_module modules/mod_dir.so
LoadModule expires_module modules/mod_expires.so
LoadModule headers_module modules/mod_headers.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule mime_module modules/mod_mime.so
LoadModule negotiation_module modules/mod_negotiation.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule status_module modules/mod_status.so

LoadModule mpm_prefork_module modules/mod_mpm_prefork.so

# (optional) for centos systemctl (or change httpd.service file manually)
# LoadModule systemd_module modules/mod_systemd.so

# for caching at SSL
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so

# SSL 
LoadModule ssl_module modules/mod_ssl.so

# HTTP 2.0 Support
LoadModule http2_module modules/mod_http2.so

<IfModule mpm_worker_module>
   LoadModule cgid_module modules/mod_cgid.so
</IfModule>
<IfModule mpm_event_module>
   LoadModule cgid_module modules/mod_cgid.so
</IfModule>
<IfModule mpm_prefork_module>
   LoadModule cgi_module modules/mod_cgi.so
</IfModule>

Next, for optimizing apache with used MPM, you need to define configuration for each type of MPM.

vi ${APACHE_DIR}/conf/conf.d/optimize.conf

Insert following

<IfModule mpm_prefork_module>
    StartServers           5
    MinSpareServers        5
    MaxSpareServers        10
    MaxClients             200
    MaxConnectionsPerChild 4500
</IfModule>

<IfModule mpm_worker_module>
    ServerLimit            16
    StartServers           3
    MaxRequestWorkers      200
    MinSpareThreads        25
    MaxSpareThreads        75
    ThreadsPerChild        25
    MaxConnectionsPerChild 4500
</IfModule>

<IfModule mpm_event_module>
    StartServers           3
    MinSpareThreads        25
    MaxSpareThreads        75
    ThreadLimit            64
    ThreadsPerChild        25
    MaxRequestWorkers      30
    MaxConnectionsPerChild 4500
</IfModule>

And for ssl and http2 modules,

vi ${APACHE_DIR}/conf/conf.d/ssl.conf

Insert

Listen 443

SSLRandomSeed startup builtin
SSLRandomSeed connect builtin

SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4
SSLProxyCipherSuite HIGH:MEDIUM:!MD5:!RC4

SSLHonorCipherOrder on

SSLProtocol all -SSLv3
SSLProxyProtocol all -SSLv3

SSLPassPhraseDialog  builtin
SSLSessionCache        "shmcb:/usr/local/apache/logs/ssl_scache(512000)"
SSLSessionCacheTimeout  300
vi ${APACHE_DIR}/conf/conf.d/http2.conf
Protocols h2 h2c http/1.1

Now open main configuration file and insert following

ServerRoot "/usr/local/apache"

Listen 80

Include conf/modules.conf

User  apache2
Group apache

ServerName localhost.localdomain
ServerAdmin master@localhost

<Directory />
    AllowOverride none
    Require all denied
</Directory>

DocumentRoot "/usr/local/apache/htdocs"
<Directory "/usr/local/apache/htdocs">
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

DirectoryIndex index.html

<Files ".ht*">
    Require all denied
</Files>

ErrorLog "logs/error_log"
LogLevel warn

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
<IfModule logio_module>
  LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
</IfModule>
CustomLog "logs/access_log" common

### DON'T ENABLE /CGI-BIN/ EXECUTION UNLESS YOU NEED IT, AND IT IS SECURE
#ScriptAlias /cgi-bin/ "/usr/local/apache/cgi-bin/"
#
#<Directory "/usr/local/apache/cgi-bin">
#    AllowOverride None
#    Options None
#    Require all granted
#</Directory>

TypesConfig conf/mime.types
AddType application/x-compress .Z
AddType application/x-gzip .gz .tgz


Include conf/conf.d/*.conf

Header unset Server

ServerSignature Off
ServerTokens Min

<Location /server-status>
    SetHandler server-status
#    Require host .example.com
    Require ip 127
</Location>

<Location /server-info>
    SetHandler server-info
#    Require host .example.com
    Require ip 127
</Location>

Now your configuration is all done and http server is rady to run. But before running as server we need to add a unix user and group for apache.

groupadd apache
useradd -d ${APACHE_DIR}/htdocs -M -g apache -s /bin/false apache2

And if firewall service is running, we need to allow http and https services (registered as port 80,443 at /etc/services) to go through for incoming connections.

firewall-cmd --zone=public --permanent --add-service=http
firewall-cmd --zone=public --permanent --add-service=https
firewall-cmd --reload

Finally, we are ready. We can start the server with

${APACHE_DIR}/bin/apachectl start

And confirm that server is running

ps axu | grep httpd
comments powered by Disqus