HTTP/2 Server Push na prática

Quando discutimos a performance do novo site do Alura, comentei do nosso uso do HTTP 2.0 e, principalmente, do recurso chamado Server Push.

Já comentei um pouco da teoria do Server Push no post sobre as novidades do HTTP 2.0. Mas como usar na prática? Vejamos.

O gargalo e o inline de recursos

Para renderizar uma página, o navegador precisa baixar muitas coisas além do HTML. Precisa de HTML, CSS, JavaScript, imagens, fontes etc. Isso em várias requisições diferentes que o navegador precisa fazer para obter os recursos necessários.

Só que um do maiores gargalos da Web é que ele precisa ler o HTML para, por exemplo, descobrir qual requisição de CSS ele precisa fazer. E aí ler o CSS para descobrir, por exemplo, qual imagem de background baixar. Isso pra tudo.

É bem ruim. A renderização fica bloqueada porque o CSS não carregou ainda, mas pra isso ele precisa baixar e ler o HTML primeiro. Cria-se uma árvore de dependências que muitas vezes acarreta em carregamento sequencial dos elementos da página.

Sabendo disso, muitas pessoas começaram a fazer o inline de recursos. Para evitar ter que esperar o HTML ser lido pra só aí requisitar o CSS, você pode embutir o código CSS direto no HTML nas tags <style>. Ou seja, evitamos uma segunda requisição para obter o CSS, que já vai junto com o HTML logo na primeira.

Isso é particularmente interessante em recursos blocantes que atrasam a renderização inicial da página. Em especial, o CSS necessário para renderizar o topo da página. Esse tipo de prática ficou conhecida como otimizar o Critical Rendering Path. Embute-se o CSS de cima da dobra e carregamos o restante de forma assíncrona.

O que é o Server Push

O inline de recursos é útil e uma otimização necessária. Mas não deixa de ser uma gambiarra. É trabalhoso de fazer e, principalmente, ruim para o cache. Com o CSS embutido no HTML, ele não pode ser cacheado de forma independente e ser compartilhado por várias páginas diferentes.

Eis então que o HTTP/2 entra com seu Server Push.

O Server Push resolve o problema do inline direto no protocolo de forma simples e elegante. A ideia é que, quando o usuário requisitar o HTML por exemplo, podemos enviar a resposta do CSS junto mesmo antes de ele requisitar.

Ou seja, com uma única requisição, no HTTP/2, podem ser enviadas múltiplas respostas. Sem precisar fazer inline, sem matar o cache e de forma bastante simples.

Server Push nos servidores

O Server Push é um recursos interno do protocolo HTTP/2 que é binário e difícil de manipular na mão. Então os servidores começaram a pensar numa forma dos desenvolvedores expressarem quais recursos devem ser pushados. Não existe uma regra aqui mas parece haver um consenso se formando em torno de usar a RFC de Web Linking. É assim que o Apache faz e é assim que o Google App Engine faz (e como usamos no site do Alura).

É bem simples: na resposta, você inclui um cabeçalho Link que lista as URLs dos recursos adicionais que serão pushados para o cliente. O servidor lê esse header e faz as mágicas para transformar num server push.

A sintaxe da RFC de Web Linking manda a gente colocar a URL do recurso, o tipo dele e a relação que temos com ele. No caso, para o server push funcionar, queremos uma relação de preload.

Na prática, é mais simples que parece. Se quiser enviar o arquivo estilos.css, adicionamos o seguinte cabeçalho:

Link: <estilos.css>; rel=preload; as=style

Podemos pushar vários recursos de uma vez:

Link: <estilos.css>; rel=preload; as=style, <home.css>; rel=preload; as=style

O servidor interpreta esse cabeçalho e oferecer para o navegador o push dos recursos listados como preload.

Para enviar o cabeçalho em si, use sua linguagem de backend favorita. Em PHP, basta um:

<?php header('Link: <estilos.css>; rel=preload; as=style'); ?>

Ou em Java:

response.addHeader("Link: <estilos.css>; rel=preload; as=style");

Como ver o push

E como saber se tudo está funcionando e ver na prática a diferença? Ao acessar a timeline dos requests no DevTools ou alguma outra ferramenta, você vai observar que a resposta do CSS vem imediatamente após o HTML. Ela foi pushada antes mesmo do HTML ser parseado pelo navegador.

Eu gosto de usar o WebPageTest. E ao observar o waterfall conseguimos ver duas coisas:

Screenshot 2016-03-22 17.09.44

Primeiro repare que o CSS é baixado logo após o HTML. Dá pra ver analisando a barrinha azul do segundo request logo após o fim da barrinha azul do primeiro (HTML). E ao clicar em algum recurso que foi pushado, o WebPageTest mostra uma janela onde é possível ver Loaded By HTTP/2 Server Push.

No site do Alura, usamos push para CSS e algumas web fonts.

Porque Server Push é excelente

O server push é muito simples e elegante de usar. Coloca um header simples e pronto, a mágica tá lá. Bem melhor que fazer inlines estranhos e atrapalhados na mão.

E ele brilha no fato de ser totalmente integrado ao protocolo e ao cache do navegador. Se o recurso já estiver no cache, o navegador pode instantaneamente recusar o push e evitar download duplicado. Os recursos pushados continuam sendo URLs e recursos independentes, logo possuem seus próprios caches individuais que podem ser bem otimizados.

Com o push e com o multiplexing do HTTP/2, diminui também a necessidade de se concatenar arquivos CSS e JS, e de se fazer sprites de imagens. Você pode pushar vários arquivos CSS de uma vez o que seria quase como concatenar e inlinear tudo.

O Server Push é uma das melhores novidades do HTTP/2. O suporte nos servidores ainda não é universal mas tem melhorado. Alguns navegadores HTTP/2 às vezes não suportam o push e o ignoram (como o Safari 9.0). Mas o suporte está crescendo rápido!

Alguns servidores têm experimentado com formas mais automáticas e espertas de fazer o push. O Jetty por exemplo possui um filtro que analisa o tráfego do seu site e descobre automaticamente quais recursos vale a pena pushar. E faz isso de forma transparente, basta habilitar o recurso.

E você já usa HTTP/2 no seu site? Tem casos de uso para o server push?

Tags: ,

21 Comentários

  1. Deivid Marques 26/04/2016 at 12:36 #

    Como sempre um ótimo e útil artigo Sérgio!!!

  2. Leandro Oriente 26/04/2016 at 12:57 #

    Fui sedento procurar pelo suporte do NGINX e não atendem ainda.

    Já tenho HTTP/2 ativo. Agora é esperar pelas próximas atualizações do NGINX.

    https://www.nginx.com/blog/http2-r7/

  3. Gilberto Albino 29/04/2016 at 11:20 #

    Push = enviar
    Pull = trazer

    Pushado? Fala sério! Onde esse Brasil vai parar com esse Portunglês!

  4. Marcelo Caetano 29/04/2016 at 11:42 #

    Olá Sérgio, muito bom esse post e o sobre a otimização do site do Alura também, você sabe dizer mais sobre o uso do server push nos dispositivos móveis? tem algum teste feito nesse sentido? acho que o acesso mobile seria o maior beneficiado desse recurso…

  5. Luan Reis 04/05/2016 at 00:41 #

    Muito bom o artigo, parabéns!! 🙂

    Também concordo com o comentário do Gilberto Albino, como no português temos a palavra “puxado” a maneira que foi utilizado o termo pushado deixou bastante confuso.

  6. Daniel 04/05/2016 at 12:51 #

    Parabéns pelo artigo, muito bem explicado!

  7. Aurelio 09/05/2016 at 11:10 #

    Bom dia, poderiam me informar se o NGINX teria suporte a essa funcionalidade ?

  8. Sérgio Lopes 09/05/2016 at 14:42 #

    @Marcelo Testamos em mobile aqui tbm sim, e foi de boa. Realmente o potencial nesse cenario é bem grande. O HTTP2 todo foi pensado pra melhorar a Web toda mas em especial redes de alta latência (aka. Mobile)

    @Aurelio Nginx ainda não tem suporte. Mas é questao de tempo pra eles adicionarem.

  9. Mauri Carvalho 13/05/2016 at 13:54 #

    “É bem simples: na resposta, você inclui um cabeçalho Link que lista as URLs dos recursos adicionais que serão pushados para o cliente. O servidor lê esse header e faz as mágicas para transformar num server push.”

    na response ou na request?

  10. Sérgio Lopes 13/05/2016 at 14:13 #

    @Mauri: O Link vai na response! O server que manda

  11. Wallace Espindola 07/06/2016 at 08:09 #

    Excelente artigo, alto padrão como sempre Sérgio! Esperando sedento pela liberação da versão com http/2 no tomcat 9…

  12. Regis 07/06/2016 at 08:19 #

    Com a evolução do HTML, CSS e JS e agora com HTTP conseguimos deixar os códigos bem mais limpo e com uma boa performance.

    Sei que não é o tema da discussão aqui, porem falando em mundo web ainda o ECMAScript 6 vem com muitas novidades interessantes também.

  13. Erivaldo 07/06/2016 at 15:41 #

    Parabéns pelo post

  14. Eduardo J Bernardino 07/06/2016 at 21:24 #

    Muito bom! Estão de parabéns.

    sobre os “pushes”, acho que não atrapalhou em nada o entendimento.
    pelo título já se tem uma ideia do que ocorre.

    Abraços.

  15. André 17/06/2016 at 08:58 #

    Bom artigo! Mas usar os termos “blocados”, “pushados” (sugere o falso cognato) e “cacheado” (é permanente ou chapinha?) são um pouco demais, não? Que tal “bloqueados”, “enviados” e “mantidos em cache”?

  16. Renato S Moreno 03/08/2016 at 13:07 #

    Existe esse Path feito pelo Cloudflare que adiciona o suporte a server push no Nginx
    https://blog.cloudflare.com/open-sourcing-our-nginx-http-2-spdy-code/

  17. Leonardo 29/08/2016 at 16:26 #

    funciona localhost?

  18. Sérgio Lopes 30/08/2016 at 15:28 #

    @leonardo

    Funciona sim, só precisa configurar certinho SSL etc. Esse cara é um jeito bem simples de fazer localhost: https://github.com/GoogleChrome/simplehttp2server

  19. Guilherme Soares 07/09/2016 at 11:19 #

    Show de bola o post… Quanto aos termos utilizados, penso que o pessoal que se incomoda com isso deve pesquisar sobre tecnologia em blogs de professores de português e não ficar enchendo o saco com bobagem.

  20. Jonnathan Venancio 30/10/2016 at 18:35 #

    Sergio gostaria agradecer pelo conteúdo. também assisti suas vídeo aulas na Alura, muito legal. e finalmente consegui fazer meu primeiro site atingir nota 100 no pagespeed. agora seria bom você nos ajudar a configurar https em localhost. Fica meus agradecimentos pela ótimas vídeo aula e aguardo mais boas praticas de performance.

Deixe uma resposta