HTTP/2 for Apache httpd
Copyright (C) 2015 greenbytes GmbH
There are several new things coming for HTTP/2 in the upcoming release 2.4.18 of Apache httpd. And one of them is PUSH. This post talks about the implementation of this protocol feature and all the other things that will be included.
HTTP/2 Server Push means that a server can send responses to a client which it never asked for. Such pushes need to be tied to a request that originated from the client. That has the advantage that the client has context to process these responses. Imagine a browser opening two tabs, requesting two pages. Tying pushes to requests makes it clear to the browser which push belongs to which. So, if the user closes one tab, the browser knows which responses to cancel.
As a rule of thumb, the page load time is affected by the RTT, the round trip time, between your server and a client. If a client needs to send a request for a resource, that sending will take RTT/2 time to travel to the server. And the response needs RTT/2 to travel back to the client. So, when a server pushes responses to the client, it saves half the RTT for that resource.
Will that save RTT/2 in page load time? That depends. There are several resources in a web pages, where saving this time will not affect page load time, as a lot of other stuff is happening and this particular resource is not blocking anything else. In such a case, it is better to omit the push. Not the least because the client may already have it in its local cache from a previous visit!
So, you need to select the candidates for pushes carefully. But for motivation, there are page scenarios where push saving can really add up. That happens when vital resources are daisy chained, when the browser needs to see the content of resource A to find out that it needs to load B and then it finds out it needs to load C, etc. Pushing A+B+C at the same time can then save 1.5 times the RTT.
The new directive
H2Push on|offlet's you control where server pushes are enabled. You can use it in server or vhost configurations. It is on by default. And it of course only works for clients that allow it. Which, fortunately, is Chrome, Firefox and others do. However Safari 9 does not.
How does it work? The implementation of HTTP/2, mod_h[ttp]2, looks at the headers of responses for ones
which are named Link
with the rel
parameter value preload
. An
example of such headers would be:
Link: </css/my.css>;rel=preload Link: </js/jquery.js>;rel=preloadand those will result in these resources to be pushed. You can combine several links into a single header, such as
Link: </css/my.css>;rel=preload, </js/jquery.js>;rel=preload
Where do these headers come from? Either your cgi/php/whatever application sets them or you can use
mod_header
to define those for certain locations. An example would be:
<Location /index.html> Header add Link "</css/my.css>;rel=preload" Header add Link "</js/jquery.js>;rel=preload" </Location>
Another new directive related to server pushes is
H2PushPriority mime-type after|before|interleaved weightwhich set the ordering and priority with which pushed responses are sent out.
You need a nghttp2 library version 1.5.0 or newer for this to have an effect!
Explaining HTTP/2 priorities is a topic of its own. Happily, other people have already talked about it. There is Moto Ishizawas very nice explanation of HTTP/2 priorities, and there is Tatsuhiro Tsujikawa's explanation of his implementation in nghttp2.
tl;dr
The higher the weight, the more gets send out the sooner. This happens either after
the requested resource has been sent, or before
or interleaved
with it.
The following is an example of how you can use the directive:
H2PushPriority * after H2PushPriority text/css before H2PushPriority image/jpeg after 32 H2PushPriority image/png after 32 H2PushPriority application/javascript interleavedThere is a special rule for '*' which is a catchall that applies to all resources that do not find a specific rule. The order in which you use the directive is irrelevant. If you leave out the weight, defaults are chosen depending on the ordering you specified. The default when no rules are specified is
after 16
.
It will be interesting to hear from people how they use it for their sites and what they observed. A little more detail about the effects, defaults and consequences you can find at the mod_h[ttp]2 documentation once 2.4.18 is released.
Several improvements habe been made in the area of TLS:
A thing that had bitten several people in the 2.4.17 release was the lack of support for connection reuse. In HTTP/2,
when you have the same certificate for a.exmple.org
and b.example.org
, browsers will reuse
any open connection they have for requests to the 'other' domain. But Apache did not allow that, answering with
a 421
response code defined in the HTTP/2 spec for such a case. Some browsers did not handle
that very gracefully.
In 2.4.18, any such connection reuse is allowed, as long as both domains have exactly the same SSL protocol settings.
Another improvement done to the TLS handling in mod_h[ttp]2 is that requests which trigger client certificate
authentication or any other renegotiation of TLS parameters, are aborted with the specific HTTP1_1_REQUIRED
error code. This informs the browser that the request should be done using the HTTP/1.1 protocol.
People in standardization are working on defining TLS client authentication for HTTP/2, but things are not done yet. So sites which need client certificate need this feature.
With the new directive
ModernTLSOnly on|offyou can control if mod_h[ttp]2 only accepts TLS connections which are secured as specified in RFC 7540 or should be totally relaxed about it and use HTTP/2 however you configured your SSL parameters. By default, the rigid checks are on and if your server expects to use HTTP/2 with common browsers, this is what you want.
With the new directives
H2TLSWarmUpSize bytes H2TLSCoolDownSecs secsyou can fine tune how TLS record sizes should be chosen on a HTTP/2 connection. TLS record sizes can vary from 1 byte up to 16K, but there is always some overhead involved with each record. So, best throughput is achieved with 16K sizes. Unless you are on the real internet or even a cellular network and a packet might get lost and your TCP protocol is not yet certain what window size is best, etc.
Ilya Grigorik has written a whole book about such things and is full of good advice about web performance tuning. His recommendations for these parameters are 1MB of data for warmup (e.g. until 16K record sizes is preferred) and 1 second for cool-down, after which an idle connection is reset to the starting size of 1300 bytes records.
If you use your Apache in more controlled environments, like your LAN, you can disable this tuning by setting both parameters to 0, making mod_h[ttp]2 use 16K record sizes whenever possible.
I wrote something about the throughput improvements a while ago and this code gets also shipped with 2.4.18. Enjoy!
One thing I'd like to add to performance measurements: they vary with the mpm
module you
use and the OS you are on. From the three available mpm
s prefork
and worker
show the best performance with mod_h[ttp]2, while event
lags behind. The reason is that mod_h[ttp]2 does not play
nice with event
right now and needs some internal changes to make best use of it.
As a last point, a somewhat, so far ivory towerish feature has been added and that is support
for trailers
. But what are trailers, you may ask?
In HTTP, trailers are the same as headers, meta data about the resource, only send after the request/response body, not before like headers. It's like post scriptums added to a letter (if anyone still knows what that is).
Trailers were introduced in HTTP/1.1 and, to stay backward compatible to HTTP/1.0, only worked when transfer
encoding chunked
was used. Which no one really wants to use only for that and so it
was never used much and so implementations are limited.
In HTTP/2 however, trailers work for every request and response. This is nice if you want to implement a HTTP/1.1 to HTTP/2 gateway and, eventually, need to transfer trailers back and forth. Or if you use Apache as a HTTP/2 proxy to a HTTP/1.1 host.
You can get Apache releases from here. Expect the 2.4.18 release to show up there sometime beginning of December.
Münster, 26.11.2015,
Stefan Eissing, greenbytes GmbH
Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. See LICENSE for details.