Laravel: Reusing a HTTP/2 connection
Most of you who use the HTTP Client included in Laravel tend to be wary of doing multiple requests to a server. The most common problem is paying the connection overhead over and over again, so normally you will want to make request only when necessary and cache the response when needed.
By today standards, HTTP/2 has become a very common protocol among modern web servers — HTTP/3 just became RFC — and one of the key features is connection reuse. Clients are encouraged to keep the connection open for additional requests, which is very useful for Internet browsers when there are plethora of resources that must be fetched.
What if I told you that you can use HTTP/2 in Laravel with the included HTTP Client, and even reuse the same connection?
The secret recipe
To make use of HTTP/2 on the Laravel HTTP Client we need two things: the cURL extension enabled, and to explicitly use HTTP/2. To reuse the connection we need to simply reuse same Guzzle client instance.
Sounds a bit tedious but the setup is relatively easy.
1) Let’s use cURL for this
The first part is triple check on
php.ini. Check that the line with
extension=curl is uncommented and the extension is loaded through
php -m. You may be amazed how some people think they have it enabled when it’s not.
Without cURL, Guzzle will fallback to native sockets and HTTP/1.1, which is slowest than cURL in most scenarios.
2) Let’s use HTTP/2
The next step is to tell Guzzle that it should connect through HTTP/2. By using the
withOptions() method, we can put a nice
2.0 on the
3) Let’s reuse the connection
Finally, the magic step is reusing the same Guzzle Client. Doing two requests to the same server will not reuse the same connection, because, the client will be created anew for each request. Instead, we first build a client, save it somewhere, and inject it into the requests to be sent.
Once done, we can simply reuse the same client by setting it into the pending request through
If we fire these two requests, the responses will come just fine. If you’re curious, we can add the
debug key with
true to check what Guzzle is doing in each request.
If we see a line saying “Re-using existing connection!” for the same host, Congratulations! You’re using HTTP/2 the way was meant to!
Some servers don’t play nice
There are three things to consider for HTTP/2 requests to work.
The first, and most obvious, is that the server must support HTTP/2. Some servers will tell or document it somewhere, others won’t guarantee that. For example, Stripe API does support HTTP/2 but doesn’t say it so officially. Always double check with a quick test if it does before using it, or ask on their forums/repositories.
For live Internet-facing servers, HTTP/2 support is usually not a problem because HTTPS is standard, but some local development servers may come with some problems because they’re not encrypted, and serving local TLS is a pain in the ass.
Let’s take an example for SurrealDB and my Subscriptions for Laravel library.
After a brief conversation, SurrealDB confirmed it supports HTTP/2 over HTTP, which is thanks to the underlying warp/hyper library. The problem is that warp doesn’t allows protocol upgrade if it’s not encrypted. Every time I executed a query to retrieve all the subscribers for a particular plan, a new connection spawned using HTTP/1.1.
In the cases when we are 100% sure the server supports HTTP/2, we can forcefully connect that way. To do that, we can signal cURL to not ask for upgrade, but use HTTP/2 immediately, by setting
If we debug the connection as we did before, we will see that Guzzle immediately uses HTTP/2 instead of asking for an upgrade, and further connections will be reused. 🥳
With that, your requests should be blazing fast, and you shouldn’t be afraid to make multiple requests to one server as long you can reuse the same connection.