Make love, not var_dump())

Captain's Log

İnternetin hızlanması ve yaygınlaşmasına paralel olarak günümüz web siteleri çok sayıda görsel ile geliyor. Sosyal medyada paylaştığımız çoğu içerikten tutun da e-ticaret sitelerinde gördüğümüz çoğu şey bir görsel veya video. Günümüzün 2 kat çözünürlük destekleyen Retina ekranlar sayesinde(yüzünden!!) de masaüstü ve mobilde yüksek çözünürlüklü resimler sunmak bir gereklilik haline geldi.

Web'de en çok kullanılan PNG ve JPEG resim formatları. Neredeyse tüm tarayıcılar ve cihazlar tarafından desteklenmeleri, bir çok resim işleme aracı tarafından düzenlenebilmeleri bu iki formatı bu günlere getirdi. Ancak bu resim formatlarının sıkıştırma algoritmaları eski olduğu için genellikle boyutları çok yüksek olabiliyor. Bu resim formatlarını optimize etmek için bir çok araç var. WebP'den önce bu tip araçlar kullanarak bu resimleri optimize edip sunuyorduk.


WebP, Google'ın 2010 yılında, açık bir standart olarak duyurduğu çok benzer kalitelerde %45'lere kadar daha az boyutta sıkıştırma sağlayan bir format. Optimize edilmiş JPG ve PNG formatlarından bile %10-%30 arasında daha düşük boyutta resimler üretebiliyorsunuz. Şu anda Chrome, Opera ve Android tarayıcılarda kullanılabiliyor.


WebP ile, kaliteden biraz ödün vererek, %90'lara varan sıkıştırmalar yapmanız mümkün. Bu da bizim için %90 daha hızlı yüklenen siteler demek oluyor!


WebP kullanımı, normal resim formatları kullanımından çok farklı değil. Gimp veya Photoshop gibi bir araçla, resmi WebP formatında kaydetmeniz yeterli. sonrasında

<img src="dosya-adi.webp">
şeklinde kullanmaya başlayabilirsiniz.


Tabii yukarıda bahsettiğim gibi, bunu sadece desteklenen tarayıcılar için yapmanız gerekiyor. WebP destekleyen tarayıcılar, isteklerde "Accept" header'ı olarak "image/webp" içeren bir header gönderiyor.


accept:image/webp,image/*,*/*;q=0.8

gibi. Kodunuzda "eğer bu header varsa webp, yoksa jpg yükle" gibi çok basit bir koşul ile, pazar payı yaklaşık %50 olan yukarıdaki tarayıcıları kullanan kullanıcılara çok daha hızlı bir deneyim sağlayabilirsiniz.

WebP hakkında, detaylı bir incelemeyi David Walsh'un şuradaki yazısında (ingilizce) bulabilirsiniz.


"Elimizdeki tüm resimleri webp'ye çevirip, kodumuzu 'eğer accept header'ı varsa webp, yoksa png sun' şeklinde güncellemeden bunu yapmanın bir yolu yok mu?" diye sorduğunuzu duyar gibiyim;


Burada da imdadımıza, kadim dostumuz NGINX ve onun yancısı Small Light yetişiyor. Small Light, sitesinde de belirtiği gibi, NGINX için, dinamik olarak resim manipülasyonu yapabildiğiniz bir modül. Resmi resize etmekten, crop etmeye, bulanıklaştırmaktan, kalitesini düşürmeye gibi bir çok işlemi yapabilmenizi sağlıyor.


Örneğin, bir resminiz var, ve bunun 300x300'lük thumbnail'larını oluşturmak istiyorsanız yapmanız gereken tek şey, small light'lı nginx sunucunuza

http://sunucu/resim.jpg?cw=300&ch=300
gibi bir parametre geçmek. Small Light bunun gibi bir çok parametre destekliyor.


Bunlardan birisi de "of" komutu. Bu da format dönüşümleri yapmanızı sağlıyor.

http://sunucu/resim.jpg?of=png
dediğinizde, jpg olan resminiz artık PNG olarak sunulmaya başlıyor örneğin.


Tabii ki, small light'ın webp desteği de mevcut. Kısaca yapmanız gereken. eğer accept header'ında webp desteği varsa resimlerinizin sonuna ?of=webp eklemek.


Bunu bir adım daha ileri götürüp, kodda herhangi bir koşul denetimi yapmadan bunu yapmanın da yolları var elbette. Sonuçta kadim dostumuz NGINX istekleri karşılayan sunucu olduğu için, accept header kontrolünü ona yaptırabilir, eğer webp destekleniyorsa small light kullanarak webp sunmasını sağlayabilirsiniz.


Böylece kodda yapmanız gereken hiçbir değişiklik olmayacak. Siz yine

<img src="http://resim-sunucunuz/foo.jpg">
olarak istek yapacaksınız. Tarayıcınız NGINX destekliyorsa size foo.jpg dosyasını webp olarak sunacak.


Small Light'ın güzelliklerini çok basitçe göstermek için bir Docker imajı oluşturduk. https://github.com/sonsuzdongu/ngx_small_light


git clone https://github.com/sonsuzdongu/ngx_small_light.git
cd ngx_small_light
docker build -t sonsuzdongu/ngx_small_light .
docker run \
    --rm \
    -it \
    --name ngx \
    -p 80:80 \
    -p 8080:8080 \
    -p 8090:8090 \
    -v `pwd`/html:/opt/nginx/html/ \
    sonsuzdongu/ngx_small_light

Bunu yaptığınızda, örnek depo içindeki html klasöründeki resimlerin webp olarak nasıl sunulduğunu görmek için http://127.0.0.1 adresini ziyaret etmeniz yeterli


WebP ile Performanslı Web


Optimize edilmemiş 2mb'lık bayrak.png dosyasının, 44KB olarak sunulduğunu görünce baya sevinmişsinizdir :) Neredeyse aynı kalitede resim ve %90'dan daha az bir boyut.


Bu yazıyı, yazarken Hürriyet Ürün Ekibi'nin paylaştığı Mobil Web Yenileme Süreci - Zorluklar ve Çözümler yazısına denk geldim. Yazıda, Hürriyet mobil web'in yenilenme sürecinden ve bu süreçte yaptıkları performans iyileştirmelerinden bashediyorlardı. "Acaba webp de entegre etmişler mi" diye kısaca bir inceledim, ancak gördüğüm kadarıyla WebP implementasyonun hala yapmamışlar.


Ben de hazırladığım docker imajındaki kod ile, Hürriyet'in sitesinde i.hurimg.com'dan sunulan resimleri, small light ile proxy'ledim.


Yukarıdaki videoda da görüleceği gibi,

  • Hürriyet'in sitesi 7 saniye civarı bir sürede yükleniyor.
  • Sayfadaki resimlerin toplam boyutu 5MB
  • Sayfadaki en yüksek boyutlu resim 305KB boyutunda bir PNG dosyası.

Nginx Small Light ile proxy ettiğimde ise

  • Sayfanın yüklenme hızı 7 saniyeden 4 saniyeye
  • Yüklenen resim miktarı 5MB'dan 1.3MB'a düştü.
  • 305KB'lık resim dosyası da 25KB'a düştü.

Böyle bir performans kazanımını koda hiç dokunmadan kazanabildik. Sadece i.hurimg.com'u kendi kurduğum, hiç optimize edilmemiş nginx small light'a proxy'ledim.


WebP, garip bir şekilde hala çok fazla kullanılmasa da, harcayacağınız ufak bir emek karşısında size kazandıracağı performans görülmeye değer olacaktır.


Not: Small Light'ın tüm parametrelerini açık bırakmak sitenize DDOS atakları yapılmasını sağlayabilir. cw=300&ch=300 nasıl 300 pixellik thumbnail oluşturuyorsa bu parametreler cw=10000&ch=12312321 gibi değerler de olabilir. Kısaca, bu parametrelerden hangilerine izin veriyorsanız sunucu tarafında bunları sınırlamanız önerilir. Ayrıca, small light her istekte bu işlemleri tekrar yapacağı için nginx proxy cache gibi bir modül ile istekleri cache'lemeniz de tavsiye edilir.