Gjør ditt nettsted raskere med færre HTTP-forespørsler

Forrige uke studerte vi hvordan mange HTTP-forespørsler etter hverandre kan gjøre ditt nettsted tregere enn det behøver å være, og hva du kan gjøre for å unngå dette.

Minst like viktig er det å holde det totale antallet bilder, skript og stilsett på nettstedet nede.
HTTP
Du skjønner, både nettleseren din og tjenermaskinen i den andre enden har en begrensning på hvor mange forskjellige filer de vil overføre samtidig. Vanligvis ligger denne grensen på 16 filer.
Dette betyr at om du har en side med 17 like store bilder, kan denne siden på en dårlig dag ta dobbelt så lang tid å laste som dersom den bare hadde 16. For om alle de 16 sporene på stasjonen allerede er i bruk, må det syttende toget finne seg i å vente til ett av de andre togene har reist videre.
Ikke bare stiller forespørslene seg i kø når det blir for mange av dem. Både nettleseren og tjeneren er glade i å fortelle alt mulig om seg selv hver gang de utveksler en fil. Uansett hvor liten filen er går de samme høflighetsfrasene igjen. Nettleseren pleier å begynne med noe sånt som dette:
GET /eksperimenter/data-uri/img/application_add.png HTTP/1.1
User-Agent: Opera/9.80 (X11; Linux i686; U; nb) Presto/2.5.24 Version/10.52
Host: e-tjenesten.org
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png,
image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Language: nb-NO,nb;q=0.9,en;q=0.8
Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1
Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0
Referer: http://e-tjenesten.org/eksperimenter/data-uri/img/
Cache-Control: no-cache
Connection: Keep-Alive, TE
TE: deflate, gzip, chunked, identity, trailers
Tjeneren kan ikke være noe dårligere, så den svarer omtrent som følger:
HTTP/1.1 200 OK
Date: Mon, 10 May 2010 22:36:07 GMT
Server: Apache/2.2.9 (Debian) mod_auth_kerb/5.3 DAV/2 PHP/5.2.6-1+lenny8 with Suhosin-Patch mod_python/3.3.1 Python/2.5.2 mod_ssl/2.2.9 OpenSSL/0.9.8g mod_perl/2.0.4 Perl/v5.10.0
Last-Modified: Wed, 31 Mar 2010 17:10:48 GMT
ETag: "688cca9-26b-4831bd3d2b600"
Accept-Ranges: bytes
Content-Length: 619
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: image/png
Deretter kommer selve filen. Underveis har altså det lille ikonet på bare 619 byte i praksis blitt tre ganger så stort, alle høflighetsfrasene inkludert. Det samme skjer for hver eneste lille fil.
Løsninger for å redusere antall forespørsler
Det er som du kanskje ser flere gode grunner til å holde antallet stilsett, skript og bilder så lavt som mulig. For skript og stilsett er ikke dette så komplisert. Å slå mange små skript eller stilsett sammen til ett stort er en enkel oppgave.
Men hva så med bilder?
Vi er stolte over at webapplikasjonene vi utvikler og drifter har et meget enkelt design, uten unødvendig dill som tar stor plass. Men vi har mange små ikoner på menyelementer, knapper og lister, og flere forskjellige bakgrunnsbilder. Det kan være flere titalls slike små elementer på en enkelt side, og selv om hver enkelt fil er veldig liten går sidelastingen tregere enn vi liker.

Er det mulig å redusere antallet forespørsler på noe vis, uten å måtte fjerne ikonene?
Mange jobber seg rundt problemet ved å legge bildene sine på ett subdomene, stilsettene på et andre og skriptene på et tredje. Da vil nettleseren åpne 16 tilkoblinger til hver av disse subdomenene, og laste ned langt flere filer samtidig. Dette er vel og bra når det gjør nettstedet raskere, men er ofte ikke nok.
Google setter sammen alle de grafiske elementene på sin søkeside til ett eneste bilde:
![]()
Riktig del av bildet vises så på riktig element ved hjelp av nøyaktig posisjonering av bakgrunnsbildet, slik at kun den relevante delen av bildet blir synlig for brukeren:
<span class="csb ch" style="background-position:-76px 0; margin-right:34px;width:66px">
Dette reduserer antallet HTTP-forespørsler betraktelig, men et slikt puslespill er kronglete å vedlikeholde og oppdatere. Kildekoden blir heller ikke spesielt leselig.
Finnes det flere måter å løse dette problemet på?
data URI
Ja, heldigvis. Si hei til data URI.
En data URI er en adresse som inneholder en fil. Det høres unektelig litt snodig ut, men er egentlig ganske enkelt. Se for eksempel på denne her:
Slike adresser med filer i kan du bruke alle steder på en nettside der du kan henvise til eksterne ressurser – i lenker, stilsett eller bilder. Det betyr at du for eksempel kan bytte ut denne koden:
.ikon { background-image: url(ikon.png); }
… med denne:
.ikon { background-image: url(data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/ INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwA
AAFiSURBVBgZpcEhbpRRGIXh99x7IU0asGBJWEIdCLaAqcFiCArFCkjA0KRJF0EF
26kkFbVVdEj6/985zJ0wBjfp8ygJD6G3n358fP3m5NvtJscJYBObchEHx6QKJ6SK
snn6eLm7urr5/PP76cU4eXVy/ujouD074hDHd5s6By7GZknb3P7mU+WNLZGKn
x595JDvf96zTQSM92vRYA4lMEEO5RNraHWUDH3FV48f0K5mAYJk5pQQpqIgixa
E1JDKtRDd2OsYfJaTKNcTA2IBIIesMAOPdDUGYJSqGYml5lGHHYkSGhAJBBIkAo
WREAT3Z3JLqZhF3uS2EloQCQ8xLBxoAEWO7aZxros7EgISIIkwlZCY6s1OlAJT
WFal5VppMzUgbAlQcIkiT0DXSI2U2ymYZs9AWJL4n+df3pncsI0bn5dX344W
05dhctUFbapZcE2ToiLVHBMbGymS7aUhIdoPNBf7Jjw/gQ77u4AAAAASUVORK
5CYII=); }
Det ser kanskje ikke så pent ut, men for hver eksterne referanse du bytter ut på denne måten sparer du en HTTP-forespørsel. Om stilsettet refererer mange bilder kan dette utgjøre en stor forskjell.
For å omgjøre en fil til en data URI kan du bruke data URI kitchen eller en base64_encode()-funksjon i skriptspråket du benytter.
Blir det noe raskere, da?
For å teste om denne fremgangsmåten faktisk er noe raskere enn alternativet, har jeg laget en håndfull forskjellige tester. Disse finner du her.

I testene laster jeg henholdsvis 10, 20 og 100 forskjellige ikoner på to forskjellige måter. I det ene tilfellet på gamlemåten, der alle ikonene er eksterne filer. I det andre tilfellet er samtlige inkludert i stilsettet ved hjelp av data URI.
Hver av testene er kjørt ti ganger, og gjennomsnittlig lastetid fremgår i tabellen under. Testene er kjørt både på min noenlunde raske nettlinje hjemme, og via en tregere 3G-tilkobling.
| 17Mbit kabel | 3G | |||
|---|---|---|---|---|
| Antall | Flere filer | Én fil | Flere filer | Én fil |
| 10 | 153ms | 105ms | 1278ms | 555ms |
| 20 | 217ms | 107ms | 2342ms | 1105ms |
| 100 | 575ms | 296ms | 9514ms | 2206ms |
På den raskere linjen gir dette trikset en hastighetsøkning på mellom 46% og 102%. På den tregere linjen er forskjellen enda større: mellom 130% og 330%!
Jeg må innrømme at dette var langt over hva jeg hadde forventet.
Vil dette fungere på mitt nettsted?
Testene over er syntetiske og tester kun et veldig smalt bruksområde, så det finnes ingen garantier for at du kan få en like stor hastighetsøkning på ditt nettsted. Kanskje er det helt andre flaskehalser som gjør seg gjeldende der. Jeg vil anbefale deg å kjøre dine egne tester og se hvor stor forskjell dette utgjør hos deg.
I vårt tilfelle har vi et digert stilsett som refererer over hundre forskjellige ikoner, men kun en brøkdel av disse er i bruk på hver enkelt side. For å teste dette scenariet laget jeg en test som refererer til 150 forskjellige ikoner i stilsettet, men kun bruker henholdsvis 10, 20 og 30 av dem.
Nettlesere er såpass intelligente at de ikke laster ned eksterne ressurser de ikke behøver, så her har eksterne bilder et fortrinn. Når du legger alt sammen til én pakke med data URI må du laste ned ned alt eller ingenting.
| 17Mbit kabel | 3G | |||
|---|---|---|---|---|
| Antall | Flere filer | Én fil | Flere filer | Én fil |
| 10 | 99ms | 167ms | 1221ms | 2510ms |
| 20 | 155ms | 186ms | 1910ms | 2322ms |
| 30 | 210ms | 191ms | 2881ms | 2142ms |
Som du ser av tabellen over lønner ikke data URI-trikset seg om du bruker en for liten andel av bildene du refererer i stilsettet. Bruker du kun 20 av 150 refererte ikoner går den optimaliserte versjonen tregere ved første sidelasting, mens ved 30 av 150 ikoner er resultatet positivt. Ved andre sidelasting, når stilsett og ikoner ligger mellomlagret i nettleseren, vil dog den optimaliserte versjonen alltid ha et fortrinn. Uansett bør vi nok rydde godt i stilsettet vårt.
Hvilke nettlesere fungerer dette i?
Data URI fungerer fint i alle moderne nettlesere, men ikke i Internet Explorer 6 og 7. Så lenge merkbar andel av våre potensielle kunder bruker disse eldre nettleserne kan vi selvsagt ikke ignorere dette.
Heldigvis kan vi gi et ekstra stilsett eldre versjoner av Internet Explorer ved hjelp av såkalte «conditional comments»:
<link rel='stylesheet' href='style.css'>
<!--[if lte IE 7]>
<link rel=’stylesheet’ href=’iesucks.css’>
<![endif]–>
I style.css har vi vårt ordinære stilsett med data URI. Dette brukes av alle nettlesere. De eldre nettleserne vil ikke forstå noe av data URI-referansene og vil ignorere disse. I iesucks.css, som kun vil lastes av Internet Explorer 6 og 7, overstyrer vi de opprinnelige deklarasjonene. Slik:
.ikon { background-image: url(ikon.png); }
Sidelasting vil gå noe tregere for brukere av disse antikke nettleserne, men det kan vi leve med. Det viktigste er at alt fungerer som normalt også for dem.
Fotografiet øverst er tatt av vitelone. Ikonene er fra famfamfam og Gnome-prosjektet.





