Get nginx with HTTP/2 protocol support up and running today. You’re not afraid of compiling a bit of source-code on your own, are you?
TL;DR: Get the build script for compiling nginx with LibreSSL on github.com.
Why HTTP/2 can be useful?
The focus of the protocol is on performance; specifically, end-user perceived latency, network and server resource usage. One major goal is to allow the use of a single connection from browsers to a Web site.
At a high level, HTTP/2:
- is binary, instead of textual
- is fully multiplexed, instead of ordered and blocking
- can therefore use one connection for parallelism
- uses header compression to reduce overhead
- allows servers to “push” responses proactively into client caches
You can read more about the benefits on the HTTP/2 frequently asked questions wiki page.
Nick Shadrin, Technical Solutions Architect at nginx inc., gave a nice presentation about the current state of HTTP/2 support in nginx.
In short: it is worth the trouble.
Why choose LibreSSL?
In order to make HTTP/2 work with browsers, you need encryption. Although encryption is no requirement in the specification per se, all browser vendors decided it was necessary to enforce. So before you venture forth, make sure you have a working HTTPS setup in place. Thanks to the efforts of Lets Encrypt you can get certificates for free now.
Secondly, most current Linux distributions do not ship an OpenSSL version supporting the Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension which is neccesary for protocol negotiation. Unless a recent version is provided by your distribution of choice, compiling by hand is necessary anyway.
Lastly, OpenSSL updates have become quiet frequent, as more and more security vulnerabilites are discovered that are so severe, they are given names. The DROWN-Attack is the most current one, but I am certain more will follow.
In contrast, LibreSSL seems to have a slightly better track-record regarding security vulneribilities. It wasn’t even affected by some of the reported issues.
If you want to support HTTP/2 with nginx right now, you’ll most likely have to build it on your own. Why not choose a library that looks a bit more stable and secure.
Show me the code
The following shell-script is tailored to RHEL/CentOS 7. If you’re using another Linux distribution, you’ll have to spot the few moments where yum
is used and replace those commands with what is appropiate.
#!/usr/bin/env bash
# Names of latest versions of each package
export VERSION_PCRE=pcre-8.39
export VERSION_ZLIB=zlib-1.2.8
export VERSION_LIBRESSL=libressl-2.4.4
export VERSION_NGINX=nginx-1.11.8
# URLs to the source directories
export SOURCE_LIBRESSL=http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/
export SOURCE_PCRE=http://ftp.csx.cam.ac.uk/pub/software/programming/pcre/
export SOURCE_NGINX=http://nginx.org/download/
export SOURCE_ZLIB=http://zlib.net/
# Path to local build
export BUILD_DIR=/tmp/nginx-static-libressl/build
# Path for libressl
export STATICLIBSSL="${BUILD_DIR}/${VERSION_LIBRESSL}"
function setup() {
# create and clean build directory
mkdir -p ${BUILD_DIR}
rm -Rf ${BUILD_DIR}/*
# install build environment tools
yum -y groupinstall "Development Tools"
}
function download_sources() {
# todo: verify checksum / integrity of downloads!
echo "Download sources"
pushd ${BUILD_DIR}
curl -sSLO "${SOURCE_ZLIB}${VERSION_ZLIB}.tar.gz"
curl -sSLO "${SOURCE_PCRE}${VERSION_PCRE}.tar.gz"
curl -sSLO "${SOURCE_LIBRESSL}${VERSION_LIBRESSL}.tar.gz"
curl -sSLO "${SOURCE_NGINX}${VERSION_NGINX}.tar.gz"
popd
}
function extract_sources() {
echo "Extracting sources"
pushd ${BUILD_DIR}
tar -xf "${VERSION_PCRE}.tar.gz"
tar -xf "${VERSION_LIBRESSL}.tar.gz"
tar -xf "${VERSION_NGINX}.tar.gz"
tar -xf "${VERSION_ZLIB}.tar.gz"
popd
}
function compile_nginx() {
echo "Configure & Build nginx"
pushd "${BUILD_DIR}/${VERSION_NGINX}"
make clean
./configure \
--prefix=/usr/share/nginx \
--sbin-path=/usr/sbin/nginx \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--http-client-body-temp-path=/var/lib/nginx/tmp/client_body \
--http-proxy-temp-path=/var/lib/nginx/tmp/proxy \
--http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi \
--http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi \
--http-scgi-temp-path=/var/lib/nginx/tmp/scgi \
--pid-path=/run/nginx.pid \
--lock-path=/run/lock/subsys/nginx \
--user=nginx \
--group=nginx \
--with-threads \
--with-file-aio \
--with-ipv6 \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_slice_module \
--with-http_stub_status_module \
--without-select_module \
--without-poll_module \
--without-mail_pop3_module \
--without-mail_imap_module \
--without-mail_smtp_module \
--with-stream \
--with-stream_ssl_module \
--with-pcre="${BUILD_DIR}/${VERSION_PCRE}" \
--with-pcre-jit \
--with-openssl="${STATICLIBSSL}" \
--with-zlib="${BUILD_DIR}/${VERSION_ZLIB}" \
--with-cc-opt="-fPIC -pie -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic" \
--with-ld-opt="-Wl,-z,now -lrt"
make -j1
popd
}
echo "Building ${VERSION_NGINX} with static ${VERSION_LIBRESSL}, ${VERSION_PCRE}, and ${VERSION_ZLIB} ..."
setup && download_sources && extract_sources && compile_nginx
retval=$?
echo ""
if [ $retval -eq 0 ]; then
echo "Your nginx binary is located at ${BUILD_DIR}/${VERSION_NGINX}/objs/nginx."
echo "Listing dynamically linked libraries ..."
ldd ${BUILD_DIR}/${VERSION_NGINX}/objs/nginx
echo ""
${BUILD_DIR}/${VERSION_NGINX}/objs/nginx -V
else
echo "Ooops, build failed. Check output!"
fi
Configuration paths, compiler, and linker settings are loosely based on the regular nginx RPM-package. Nginx modules that require additional libraries are excluded. Email-related modules are disabled on purpose, as well. Adapt the script to your liking, it is just a blueprint.
Update 2016-03-24: Since the LibreSSL 2.3.3 release, the developers have added the install_sw
target to the build script. Patching nginx sources is no longer needed.
It is absolutely necessary to patch the nginx source-files in the patchNginxOpenSSLMakefile()
function.
Yes, now [since v1.9.12] nginx uses
install_sw
target to install OpenSSL when--with-openssl=path
is used, and this breaks things when you try to use LibreSSL instead of OpenSSL. […], it’s only expected to be able to compile OpenSSL.
Alternatively, you might want to patch LibreSSL to provide a install_sw
target.
In general, I would recommend running the build in a chroot, Docker container, or VM; all that matters is the nginx binary created at the end of the process.
Also, keep in mind that the build will contain all debug symbols (meaning the file is almost 20MB big), so you may want to skip the --debug
flag in configure, and call strip
on the executable afterwards for good measure.
Modern browsers benefit most
Virtually all modern, a.k.a. “ever-green”, browsers will benefit from having to open only one connection to a given website. At the very least, nginx can now handle even more concurrent requests than ever before.
On the other hand, “old-ish” clients must fallback to standard HTTP/1.1 with all its associated drawbacks. Running the ngx-spdy
module in parallel doesn’t look like a good idea, either.
Reconsider, before you take leave of the domain-sharding pattern pattern, if most of the website visitors come from mobile or less than current clients.
Addendum: My sincere apologies to everyone who visited this page on March 7, 2016. I accidently had published the post in a very rough draft state, which was barely readable and missed many important points.