Přednačítání přes HTTP 2 server push

HTTP 2 umožňuje, aby server poslal prohlížeči předem nějaké objekty, když vidí, že žádá o obsahovou stránku. Typicky skripty, styly a nějaké obrázky použité v té stránce. Říká se tomu Server Push, tedy tlačení serverem. Může to významně zrychlit načítání a hlavně vykreslování stránek, protože prohlížeč nemusí čekat, než mu soubory dorazí.

Ačkoli se o HTTP 2 Server Push dost mluví, málo se používá. I když na serveru HTTP 2 běží, server sám od sebe nic pushovat (tlačit) nebude. Musí se mu to říct http hlavičkami. Buďto aplikačně, nebo přes .htaccess.

Nutné předpoklady - Podoba http hlavičky Link - Pushování přes .htaccess - Pushování pomocí serverové aplikace - V redakčních systémech - Jak zjistit, jestli pushování funguje - Jaké objekty pushovat - Jak je to s kešováním

Nutné předpoklady pro server push

Stránky běží na https.

Na serveru běží HTTP/2 a Apache nebo Nginx. Zatím (na jaře 2017) umí HTTP/2 jenom pár hostingů, časem doufám víc. Hostingy, které HTTP 2 umí, se snažím uvádět v přehledu hostingů s PHP.

Z webových serverů umí Server Push Apache a Nginx. Apache je server pravděpodobně nejrozšířenější, takže je velká šance, že na vašem serveru bude. Další rozšířený server Nginx umí Push od února 2018. Já s Nginxem zatím neumím, takže zdejší příklady se vztahují k Apache, ale nebude to v Nginxu velký rozdíl.

Je potřeba umět nastavovat hlavičky serverovým skriptem nebo přes .htaccess - v případě .htaccess je potřeba mít na serveru ještě modul mod_headers.c, což ale většinou je.

Přednačítané objekty musejí pocházet ze stejného serveru a stejného hostname (takže nesmí být ani z jiné subdomény).

Podpora v prohlížečích už je výborná (jaro 2017).

Podoba http hlavičky Link

Http hlavička Link se používá na vícero věcí. Že půjde o server push, určuje rel=preload. Řetězec as=style (případně další hodnoty scrip, image apod.) se doporučuje uvádět, aby prohlížeč uměl správně zdroj použít a nepožádal o něj znovu.

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

Takových linků můžu přidat víc pod sebe. Případně můžu objekty zapsat do jednoho řádku hlavičky:

Link: </styl.css>; rel=preload; as=style, </skript.js>; rel=preload; as=script, </obrazky/logo.png>; rel=preload; as=image

Ve chvíli, kdy server takovou hlavičku ve své odpovědi uvidí, začne rovnou posílat klientovi i uvedené soubory v dalších streamech stejného TCP spojení. Klient (prohlížeč) je začne přijímat a použije je ve chvíli, kdy mu jeho parser řekne, že by se tytéž soubory hodily pro vykreslení stránky.

Podívejte se také na formální zápis tagu link, kde možnost preload a další podobné zmiňuju.

Pushování přes .htaccess

Dejme tomu, že budu chtít pushovat soubory logo.png, styl.css a skript.js. Budu je chtít pushovat jenom ke stránkám, které mají příponu html. (Předpokládám, že na serveru běží Apache a je povolen .htaccess.) Do souboru httpd.conf nebo .htaccess přidám následující instrukce:

<FilesMatch "\.html$">
Header add Link "</styl.css>; rel=preload; as=style"
Header add Link "</skript.js>; rel=preload; as=script"
Header add Link "</obrazky/logo.png>; rel=preload; as=image"
</FilesMatch>

Server potom vytvoří 3 hlavičky link a po odeslání souboru začne rovnou odesílat (preload) zmíněné další objekty. Všimněte si, že adresy k souborům začínají lomítkem - jde o kořenovou adresu. Adresy je potřeba samozřejmě upravit, aby odpovídaly adresám na skutečném serveru.

Proč tam mám to <FilesMatch ".\html$">: Kdybych skripty a styly pushoval ke všem objektům, například k obrázkům, bylo by to zbytečné. Jsou potřeba jenom u html stránek. Já ve svých stránkách používám příponu .html, ale stejně tak tam můžete zapsat <FilesMatch ".\php$"> nebo jinou příponu souboru, kterou používáte. Jestliže máte adresy složitější nebo nekončí příponou, tak si nějak poraďte. (Asi by to šlo opodmínkovat negativně.) Ostatně adresy nekončící příponou obvykle vznikají nějakou aplikací, takže se tam hlavičky Link dají nasázet aplikačně.

Pushování pomocí serverové aplikace

Všechny serverové jazyky umějí nastavit http hlavičku. Pro příklad PHP:

<?php
header("Link: </styl.css>; rel=preload; as=style");
?>
<html>
... zbytek stránky

Tenhle příklad funguje. Header() je funkce jazyka PHP a jako argument má vypsanou hlavičku Link, jakou jsem uvedl výše.

V praxi asi bude vypadat použití trochu jinak, například cesta a jméno souboru stylu asi bude v proměnné, ale to si každý péhápkář jistě sám vymyslí. Nejčastější chybou začátečníků je, že se hlavičky nedají nastavovat poté, co se posílá nějaký výstup, proto mám v příkladu uvedeno i to <html>, aby bylo jasné, že se hlavička musí nastavit předtím, než se začne posílat jakýkoli html kód.

V redakčních systémech

Nemám osobní zkušenost, ale očekávám, že na server push by měly fungovat doplňky redakčních systémů. Například pro Wordpress existuje doplněk HTTP/2 Server Push, který o sobě tvrdí, že přidává Link hlavičky pro každý skript a styl, který stránka následně bude odesílat. (Kromě těch vytvořených dalšími doplňky.)

Budu rád, dáte-li mi vědět mailem, že něco takového používáte.

Jak zjistit, jestli pushování funguje

V prohlížeči ve Web inspektoru. Klávesa F12, karta Network nebo Síť.

Když se ve sloupečku Initiator se objeví Push / Other, znamená to, že soubor byl úspěšně pushován (tlačen). Projevuje se to i na časovém grafu úplně vpravo, kde pushované soubory nemají žádný zaznamenaný čas čekání (zelená ploška).

Ve sloupečku Protocol si všimněte hodnoty h2, což znamená http 2.0.

Jaké objekty pushovat

Server Push osobně používám hlavně pro skripty a styly, protože na ně stránka při vykreslování čeká. Tedy typicky soubory vložené přes <link rel="stylesheet" src="..."> a <script src="...">.

Obrázky doporučuji nepushovat, ať nezabírají linku důležitějším objektům, což jsou skripty a styly. Pokud obrázky pushovat, tak jen takové, které jsou na stránce hodně nahoře a případně tvoří významnou část grafiky stránky. Protože většinou píšu instrukce pro Link do souboru .htaccess, jsem zároveň omezen jenom na takové obrázky, které tvoří část šablony. Přes .htaccess totiž nejde moc dobře pushovat obsahové obrázky, které se vyskytují jenom v jednom článku na celém webu. Musel bych podmínkovat a pro každý název souboru preloadovat jiné obrázky, což je z organizačního hlediska blbost. Naopak dává docela dobrý smysl posílat obrázky ze šablony, protože ty se vyskytují na každé stránce. Skripty a styly se také většinou používají na každé stránce, takže o to větší důvod pushovat je přes .htaccess.

Multiplexing

Navíc server s dobře nastaveným HTTP 2.0 by měl pro obrázky používat multiplexing. Takže obrázky většinou není potřeba pushovat, moc by se to nezrychlilo.

Jak funguje multiplexing na http 2: Jakmile parser prohlížeče zjistí, že potřebuje nějaký obrázek, neotevírá kvůli němu nové TCP spojení, ale použije již to otevřené. Takovou věc umí už HTTP 1.1, říká se tomu pipelining. Multiplexing ale není limitován počtem najednou požadovaných objektů (http 1.1 to bere po šesti) a hlavně odpovědi může prohlížeč dostávat v libovolném pořadí, což je hlavní nedostatek pipeliningu (stačí, když se jeden objekt někde zasekne a ostatní na něj musejí čekat).

Jinak řečeno: pro stránky s mnoha obrázky je výhodou běžet přes HTTP 2.0, i když se nic pushovat nebude.

Udržitelnost pushování

Netlačte ze serveru zbytečnosti. Styly a skripty jsou podle mě optimum, cokoli víc se bude špatně udržovat.

Když změníte adresy skriptů a stylů, je potřeba na to pamatovat a měnit je i v http hlavičkách. Což se asi dá, pokud máte dobře napsané serverové skripty, ale těžko na to bude někdo myslet, když dává na server novou verzi stylu a přejmenovává přitom soubor.

Preventivně doporučuji pravidelně kontrolovat konzoli v Dev tools. Konzole hlásí, pokud se něco pushuje zbytečně.

Jak je to s kešováním

Pokud se s každou obsahovou stránkou ze serveru rovnou pushují její skripty a styly, může se zdát, že to povede k většímu -- a často zbytečnému -- datovému toku ze serveru do prohlížeče. Bez pushování by se totiž skripty a styly stáhly jednou a uložily do keše. Nestane se, že se skripty a styly budou posílat ze serveru zbytečně?

Šťoural jsem se v tom asi hodinu a zjistil jsem, že většinou ne. Prohlížeč, pokud má objekty správně kešované, si je vezme ze své keše. Nepodařilo se mi prozkoumat pozadí, zas takový hacker nejsem. Pochopil jsem to tak, že server sice ty kešované objekty začne tlačit přes push, ale klient ho většinou stihne zastavit přes RST_STREAM. Pokud jsou styly a skripty datově hodně malé (což moje jsou), někdy se to asi nestihne zastavit. To je pouze hypotéza. Našel jsem i informaci, že v tom nemají jasno ani autoři specifikace HTTP 2 a že na tom budou dál pracovat. Pokud někdo víte něco víc, rád si to přečtu v mailu nebo odkážu na vysvětlení.

Fakt je, že pokud objekty jsou v paměti, znovu se většinou nestahují a berou se z keše. Pokud jsou jenom na disku, prohlížeč je většinou vezme z toho, co server poslal přes push, asi protože je to rychlejší.

Našel jsem pár anglických návodů, které doporučují rozhodnutí o pushování dělat na základě cookies se stejnou platností, jako je max-age pushovaného souboru. Pokud uživatel už má cookie, je pravděpodobné, že má i soubory v keši. V takovém případě se nepushuje. Pokud prohlížeč cookie nemá, pushuje se. (Problém těch článků je, že to dělají složitými konstrukcemi v PHP, přičemž já PHP na stránky nepoužívám. Domnívám se, že by tatáž logika šla napsat čistě do .htaccess nebo httpd.conf pomocí <if>, ale zkoušel jsem to hodinu a vzdal jsem to. Blbě se to testuje a nehodlám trávit život zkoumáním, co dělá která blbá uvozovka, proč nejde napsat záporná podmínka a jak se mažou cookie tak, aby se fakt smazaly a ne jenom jako. Jestli chcete někdo v mé práci pokračovat, ozvěte se, poskytnu kód, který asi fungoval, ale za boha to nedokážu ověřit.)

Poznámky

Prohlížeč při negociaci může poslat SETTINGS_ENABLE_PUSH nastavenou na 0, což by mělo server přimět k tomu, aby push nedělal. To se asi použije při vypnutých obrázcích (jenom dohad).

Pokud se na konec hlavičky Link napíše ještě "; nopush", nebude server pushovat. Úplně nechápu, k čemu to je, ale může se hodit v aplikacích.

Server Push jsem zatím použil na svých webech gruzie.com, portugalsko.net a turecko.org. Můžete se jim podívat na hlavičky, jestli to chcete vidět v reálu.

Když se pushuje objekt, který se na stránce nepoužije, tak to tolik nevadí. Samozřejmě se zbytečně přenášela data. Informaci o tomto zbytečném přenosu lze najít v konzoli prohlížeče. Píše to: "The resource https://example.com/... was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it wasn't preloaded for nothing."

Podobnou funkčnost jako http hlavička Link lze vyvolat html tagem link s rel="preload", což by mělo fungovat ve Chromu:

<link rel="preload" href="skript.js" as="script">

Při testování mi to nefungovalo v žádném prohlížeči, protože jsem tam neměl to as="script". S tím as to funguje.

Proč je to tagem <link> na nic: protože tento tag vidí až prohlížeč. Server html  nečte, jenom odesílá. Stahování začne až při parsování, tedy podobně pozdě, jako kdyby se objekt vyparsoval normálně ze zdrojáku. (Jediná výhoda (jak to chápu já), je to, že se <link> objevuje na začátku zdrojáku, takže se stahování začíná přeci jen trochu dříve. Maximálně může dostat větší prioritu.) Takže nemůžu <link rel="preload"> doporučit, připadá mi jako zbytečnost. Pokud chcete něco přednačítat tagem link, je může se na to lépe hodit prefetch.

Doporučuji taky nastudovat HTTP status 103 Early Hint. Od léta 2022 by měla fungovat ve Chrome a také způsobovat přednačtení objektu.

 

Reklama

www.webhosting-c4.cz, extra rychlý SSD webhosting s doménou v ceně
o tvorbě, údržbě a zlepšování internetových stránek

Návody HTML CSS JavaScript Články Ostatní

Encyklopedie FrontPage Reklama PHP Server

Jak psát web píše Yuhů, Dušan Janovský. Kontakt.