HTTP/2 en pratique : Pourquoi le Server Push échoue ?

nov. 29, 2018·
Benjamin Rabiller
Benjamin Rabiller
· 4 min. de lecture
Image credit: thehackernews.com

Introduction à HTTP/2

Le protocole HTTP/2 (h2/h2c) est aujourd’hui un standard pour la performance web. Ses promesses sont majeures :

  • HPACK : Compression des headers pour réduire l’overhead.
  • Server Push : Envoi anticipé des ressources (preload).
  • Server Hints : Aide au navigateur pour le prefetch.
  • Format Binaire : Plus efficace que le format texte.
  • Multiplexage : Une seule connexion TCP pour plusieurs requêtes simultanées.

Mais alors, pourquoi rédiger cet article ?

Un client m’a fait part de son impossibilité à faire fonctionner la fonctionnalité PUSH sur son infrastructure hébergée sur AWS. J’ai souhaité approfondir le sujet pour comprendre où le flux se brise dans une architecture multi-couches.


La feature PUSH sur le banc d’essai

Définition

Le push HTTP/2 permet au serveur d’envoyer des ressources au client (navigateur) sans que ce dernier ne les demande explicitement. On économise ainsi des allers-retours (RTT) précieux.

Schéma du fonctionnement du Server Push HTTP/2

Architectures de test

Architecture d’infrastructure AWS avec ALB, Varnish et Nginx

Nos deux architectures de test sont composées de :

  1. Un ALB public (AWS Application Load Balancer).
  2. Des serveurs Varnish (version 5.2).
  3. Un ALB interne.
  4. Des frontaux Nginx (App Symfony 4).

La différence réside dans la présence ou non d’HTTPS sur l’ALB interne et les backends Nginx. Pour le test, j’ai suivi le tutoriel de Kévin Dunglas pour implémenter le push sous Symfony 4 :

<!DOCTYPE html> 
<html> 
<head> 
    <meta charset="UTF-8"> 
    <title>Welcome!</title> 
    <link rel="stylesheet" href="{{ preload(asset('main.css'), { as: 'style' }) }}"> 
</head> 
<body> 
    <main role="main" class="container"> 
        <h1>Hello World</h1> 
    </main> 
</body> 
</html> 

Phase de diagnostics : Analyse des flux

1. Test via l’ALB public (Architectures A et B)

nghttp -ans https://symfony-sample.xxx.net/

id  responseEnd requestStart  process code size request path
 13    +63.90ms        +76us  63.82ms  200   8K /
 15    +69.81ms     +63.91ms   5.90ms  200   91 /main.css

L’asset main.css n’est pas “pushé”. L’absence de l’astérisque (*) dans nghttp confirme l’échec.

2. Test direct sur Nginx

  • Architecture A (HTTP/80) : Échec. Nginx ne gère pas le HTTP/2 over TCP (h2c).
  • Architecture B (HTTPS/443) :
[root@ip-10-134-168-146 ~]# nghttp -ans https://127.0.0.1:32771/ -v 
[...]
[  0.026] recv PUSH_PROMISE frame <length=52, flags=0x04, stream_id=13> 
          ; END_HEADERS 
          (padlen=0, promised_stream_id=2) 
[...]
id  responseEnd requestStart  process code size request path 
  13    +23.49ms       +727us  22.76ms  200   8K / 
   2    +26.76ms * +23.13ms   3.63ms  200   91 /main.css 

Résultat : OK. En direct via HTTPS, Nginx pousse correctement l’asset.

3. Test via l’ALB interne (Architecture B)

id  responseEnd requestStart  process code size request path 
  13    +28.56ms       +179us  28.38ms  200   8K / 
  15    +32.55ms     +28.58ms   3.97ms  200   91 /main.css 

Résultat : KO. Le multiplexage est présent, mais le PUSH a disparu.

4. Test via Varnish

Varnish 5.0 supporte théoriquement le h2c. Vérifions la RFC 7540 Section 3.2 sur le mécanisme d’Upgrade.

Après diagnostic, l’option n’est pas active par défaut :

varnishadm param.show feature 
# Value is: none (default)
# Activation :
varnishadm param.set feature +http2

Une fois activé, curl confirme le passage en HTTP/2 :

curl -k -svo /dev/null --http2 -H "Host: symfony-sample.xxx.net" http://127.0.0.1/httpprobe 
< HTTP/1.1 101 Switching Protocols 
< Connection: Upgrade 
< Upgrade: h2c 
* Received 101 
* Using HTTP2, server supports multi-use 

Résultat : KO. Toujours pas de PUSH à travers Varnish.


Verdict technique : Pourquoi le Server Push échoue ?

L’impossibilité de faire fonctionner le PUSH s’explique par la dégradation du protocole entre les couches de l’infrastructure.

AWS ALB

Le Load Balancer d’Amazon supporte HTTP/2 uniquement côté client.

“The load balancer converts these to individual HTTP/1.1 requests […] You can’t use the server-push feature of HTTP/2.”AWS Documentation

Nginx

Nginx ne supporte pas HTTP/2 en upstream (proxy_pass).

“At the moment, we only support HTTP/2 on the client side. […] there’s not much benefit in HTTP/2 for low‑latency networks such as upstream connections.” — Source Nginx

Varnish

Bien qu’il accepte le H2, il ne gère pas la transmission des frames PUSH_PROMISE venant du backend pour les redistribuer au client.

Conclusion

Dans une architecture moderne (ALB -> Varnish -> Nginx), le Server Push est inutilisable. Le protocole est dégradé en HTTP/1.1 dès le premier saut interne. Pour bénéficier des avantages de HTTP/2, il faut se concentrer sur la couche la plus proche du client ou utiliser le Resource Preloading classique via les headers Link, qui reste compatible même après dégradation.

Benjamin Rabiller
Auteurs
DevOps/Cloud Architect

Actuellement ingénieur DevOps/Architecte Cloud, j’étais initialement interessé par l’administration système et grâce aux entreprises dans lesquelles j’ai pu travailler Oxalide et maintenant Claranet j’ai eu la chance de découvrir l’univers du Cloud et de l’automatisation.

Je me suis décidé a publier ce blog pour vous faire partager ma passion mais également pour enrichir avec modestie tout ce que l’on peut trouver sur internet. Bonne lecture !