Bir web sayfasını yavaşlatan şeylerin başında neredeyse her zaman görseller gelir. JavaScript'i küçültürsünüz, CSS'i birleştirirsiniz, önbellek başlıklarını ayarlarsınız; ama ana sayfada 2 MB'lık bir ekran görüntüsü duruyorsa bütün o emek ziyaretçinin gözünde kaybolur. Özellikle mobil veriyle bağlanan biri için sayfanın geç açılması, sitenizi kapatıp başka bir sonuca tıklaması için yeterli bir sebeptir.
Bu sitede görselleri WebP formatına taşıdım ve sonuç beklediğimden de iyi oldu: sitedeki 66 PNG/JPG görselin toplam boyutu 11,1 MB'tan 1,9 MB'a indi. Üstelik hiçbir HTML dosyasına, veritabanındaki hiçbir dosya adına ve yönetim paneline yüklenen hiçbir görselin adına dokunmadan. Bu yazıda bunu nasıl yaptığımı, kullandığım kodlarla birlikte adım adım anlatıyorum.
Bu yazıda neler var?
- WebP nedir, neden bu kadar küçük?
- Bu sitede ölçtüğüm gerçek sonuçlar
- WebP'ye geçmenin üç yolu ve neden sunucu tarafını seçtim
- .htaccess ile tarayıcıya göre WebP sunmak
- PHP GD ile otomatik WebP kopyası üretmek
- Toplu dönüştürme ve panel yüklemeleri
- Çalıştığını nasıl test edersiniz?
- Dikkat edilmesi gerekenler ve sık sorulan sorular
1) WebP nedir, neden bu kadar küçük?
WebP, Google'ın web için geliştirdiği bir görsel formatıdır. Tek bir format içinde hem JPG gibi kayıplı hem de PNG gibi kayıpsız sıkıştırmayı destekler; şeffaflık (alfa kanalı) ve animasyon da içinde vardır. Yani normalde fotoğraflar için JPG, logolar ve ekran görüntüleri için PNG, hareketli görseller için GIF kullandığınız her yerde tek başına WebP kullanabilirsiniz.
Boyut farkının sebebi daha akıllı bir sıkıştırma yöntemidir. Kayıplı WebP, görseli bloklara bölüp her bloğu komşu piksellerden tahmin etmeye çalışır ve yalnızca tahminle gerçek arasındaki farkı saklar. Kayıpsız WebP ise tekrar eden renkleri ve desenleri PNG'den çok daha verimli kodlar. Pratikte aynı görsel kalitesinde JPG'ye göre yaklaşık %25–35, PNG'ye göre çoğu zaman çok daha fazla küçülme elde edilir.
Tarayıcı desteği artık bir sorun değil: Chrome, Edge, Firefox, Opera ve Safari'nin (macOS Big Sur ve iOS 14'ten itibaren) güncel sürümlerinin tamamı WebP gösterebiliyor. Yine de hâlâ eski bir cihazla gelen ziyaretçiler olabileceği için kuracağımız sistemde orijinal dosyayı her zaman yedek olarak tutacağız.
2) Bu sitede ölçtüğüm gerçek sonuçlar
Teoriden önce rakamlar. Aşağıdaki değerler bu sitenin sunucusundaki gerçek dosyalardan alındı; WebP kopyaları kalite 80 ile üretildi:
| Görsel | Orijinal | WebP | Kazanç |
|---|---|---|---|
| Ürün sayfasındaki tam sayfa ekran görüntüsü (PNG) | 2,36 MB | 173 KB | %93 |
| Ana sayfadaki profil fotoğrafı (PNG) | 307 KB | 33 KB | %89 |
| Blog içi şema görseli (PNG) | 136 KB | 43 KB | %69 |
| Blog kapak görseli (JPG) | 130 KB | 59 KB | %55 |
| Sitedeki 66 görselin toplamı | 11,1 MB | 1,9 MB | %83 |
En büyük kazanç ekran görüntülerinde. Bunun sebebi, ekran görüntülerinin genellikle PNG olarak kaydedilmesi ve PNG'nin fotoğraf benzeri içerikte (gölgeler, gradyanlar, yumuşatılmış yazılar) çok verimsiz kalması. Zaten iyi sıkıştırılmış JPG'lerde kazanç daha mütevazı, ama yine de yarı yarıya.
3) WebP'ye geçmenin üç yolu
Bir siteyi WebP'ye taşımanın temelde üç yolu var:
- Tüm dosyaları dönüştürüp adları değiştirmek:
kapak.jpgsilinir, yerinekapak.webpgelir. En basit görünen yol ama en riskli olanı: HTML'deki, veritabanındaki, CSS'teki ve eski blog yazılarının içindeki bütün adresleri tek tek güncellemeniz gerekir. Bir tanesini unutursanız kırık görsel çıkar. Eski tarayıcıya da yedek kalmaz. <picture>etiketi: Her görsel için HTML'e hem WebP hem orijinal kaynağı yazarsınız; tarayıcı desteklediğini seçer. Standart ve temiz bir çözüm, ama sitedeki her<img>etiketini değiştirmek gerekir. Editörle yazılmış eski blog içerikleri, CSS arka planları ve yönetim panelinden eklenen görseller için pratik değildir.- Sunucu tarafında içerik anlaşması (content negotiation): Görselin adresi hiç değişmez. Tarayıcı
kapak.jpgister; sunucu tarayıcının WebP desteklediğini görürse aynı adresten WebP içeriği döndürür, desteklemiyorsa orijinal JPG'yi.
Ben üçüncüsünü seçtim, çünkü sitenin hiçbir yerine dokunmadan çalışıyor. Orijinal dosya yerinde kalıyor, yanına sadece bir kopya ekleniyor: kapak.jpg dosyasının yanında kapak.jpg.webp. Kopyayı silerseniz site eskisi gibi çalışmaya devam ediyor; yani geri dönüşü de risksiz.
4) .htaccess ile tarayıcıya göre WebP sunmak
Apache ya da LiteSpeed kullanan paylaşımlı hostinglerin (cPanel dahil) çoğunda bu iş birkaç satırlık bir .htaccess kuralıyla yapılabiliyor. Bu sitede kullandığım kural şu:
<IfModule mod_rewrite.c>
RewriteEngine On
# Tarayıcı WebP destekliyorsa ve görselin .webp kopyası varsa onu ver
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} \.(png|jpe?g)$ [NC]
RewriteCond %{REQUEST_FILENAME}.webp -f
RewriteRule ^(.+)$ $1.webp [T=image/webp,L]
</IfModule>
# Aynı adres tarayıcıya göre farklı içerik döndürdüğü için önbellekler Accept'e göre ayırsın
<IfModule mod_headers.c>
<FilesMatch "\.(png|jpe?g|webp)$">
Header append Vary Accept
</FilesMatch>
</IfModule>
Satır satır ne yaptığına bakalım:
RewriteCond %{HTTP_ACCEPT} image/webp: Tarayıcılar görsel isterkenAcceptbaşlığında hangi formatları anladıklarını söyler. WebP destekleyen tarayıcı bu başlığaimage/webpyazar. Bu koşul, desteklemeyen tarayıcıların kurala hiç girmemesini sağlar.\.(png|jpe?g)$: Kural yalnızca PNG ve JPG isteklerinde çalışır; SVG, GIF ya da diğer dosyalar etkilenmez.%{REQUEST_FILENAME}.webp -f: En önemli güvenlik ağı. Kural ancak diskte gerçekten bir.webpkopyası varsa devreye girer. Kopyası olmayan görsel olduğu gibi sunulur, kırık görsel riski yoktur.[T=image/webp,L]: Sunucuya, döndürülen dosyanın türünüimage/webpolarak bildirmesini söyler. Bu olmazsa sunucu dosyayı uzantısına bakarakimage/jpegdiye işaretleyebilir.Vary: Accept: Çoğu kişinin atladığı ama kritik olan kısım. Aynı adres artık iki farklı içerik döndürebiliyor; bu başlık, aradaki önbelleklere (CDN, proxy, tarayıcı) “bu yanıtAcceptbaşlığına göre değişir, ikisini karıştırma” der. Yazılmazsa bir önbellek, WebP'yi WebP desteklemeyen bir tarayıcıya da sunabilir.
5) PHP GD ile otomatik WebP kopyası üretmek
Sunucu tarafı hazır; şimdi kopyaları üretmemiz gerekiyor. Bunun için harici bir araç kurmak yerine PHP'nin hemen her hostingde açık gelen GD eklentisini kullandım. imagewebp() fonksiyonu varsa sunucunuz WebP üretebiliyor demektir.
function ea_webp_kopya_olustur(string $path, int $quality = 80): ?int
{
if (!preg_match('/\.(png|jpe?g)$/i', $path) || !is_file($path) || !function_exists('imagewebp')) {
return null;
}
$webpPath = $path . '.webp';
$info = @getimagesize($path);
$img = false;
if ($info && $info[2] === IMAGETYPE_PNG) {
$img = @imagecreatefrompng($path);
if ($img) {
imagepalettetotruecolor($img); // paletli PNG'ler için şart
imagealphablending($img, true);
imagesavealpha($img, true); // şeffaflığı koru
}
} elseif ($info && $info[2] === IMAGETYPE_JPEG) {
$img = @imagecreatefromjpeg($path);
}
if (!$img) {
@unlink($webpPath);
return null;
}
$tmp = $webpPath . '.tmp';
$ok = @imagewebp($img, $tmp, $quality);
imagedestroy($img);
// WebP en az %10 küçük değilse kopyayı tutma; orijinal sunulsun
if (!$ok || !is_file($tmp) || filesize($tmp) > filesize($path) * 0.9) {
@unlink($tmp);
@unlink($webpPath);
return null;
}
rename($tmp, $webpPath);
return filesize($webpPath);
}
Fonksiyonda dikkat etmenizi istediğim üç ayrıntı var:
- Şeffaflık: Logo gibi şeffaf PNG'lerde
imagesavealpha()çağrılmazsa saydam alanlar siyah çıkar. Paletli (8 bit) PNG'ler ise önceimagepalettetotruecolor()ile gerçek renge çevrilmelidir; aksi halde renkler bozulabilir. - %10 kuralı: Her görsel WebP'de küçülmez. Çok küçük ikonlar ya da zaten iyi optimize edilmiş dosyalar bazen büyüyebilir. Kazanç %10'un altındaysa kopya hiç oluşturulmaz;
.htaccesskuralı da kopya olmadığı için orijinali sunar. - Geçici dosya: Kopya önce
.tmpuzantısıyla yazılıp sonra yeniden adlandırılır. Böylece dönüştürme sırasında gelen bir ziyaretçiye yarım yazılmış bir dosya gitmez.
Kalite değeri olarak 80 kullanıyorum. Benim testlerimde 80 ile 90 arasında gözle görülür bir fark olmadı ama boyut belirgin şekilde arttı; 70'in altında ise ekran görüntülerindeki küçük yazılarda bulanıklık başladı.
6) Toplu dönüştürme ve panel yüklemeleri
Sitede zaten bulunan görseller için küçük bir komut satırı betiği yazdım. Betik assets/img ve uploads klasörlerini tarıyor, her PNG/JPG için yukarıdaki fonksiyonu çağırıyor ve kopyası güncel olan dosyaları atlıyor. Bu sayede tekrar tekrar çalıştırmak güvenli:
// Kopya zaten var ve orijinalden yeniyse dokunma
if (is_file($path . '.webp') && filemtime($path . '.webp') >= filemtime($path)) {
continue;
}
Yeni görseller için ise elle bir şey yapmak gerekmiyor. Yönetim panelinde bir blog kapağı, proje görseli ya da editör içine resim yüklendiğinde, dosya kaydedildikten hemen sonra aynı fonksiyon çağrılıyor ve WebP kopyası o anda oluşuyor. Görsel panelden silindiğinde de yanındaki .webp kopyası birlikte siliniyor; sunucuda sahipsiz dosya birikmiyor.
Dosya izinlerine de dikkat edin: kopyalar web sunucusu tarafından okunabilir olmalı (genellikle 644). Kendi bilgisayarınızda üretip FTP ya da zip ile yüklüyorsanız, izinleri yanlış gelen bir dosya canlıda 403 hatası verir ve görsel hiç görünmez.
7) Çalıştığını nasıl test edersiniz?
En hızlı yol terminalden curl ile iki farklı istek göndermek. İlkinde WebP desteği varmış gibi, ikincisinde yokmuş gibi davranıyoruz:
curl -sI -H "Accept: image/webp" https://siteniz.com/assets/img/profile.png | grep -i "content-type\|content-length\|vary"
curl -sI https://siteniz.com/assets/img/profile.png | grep -i "content-type\|content-length\|vary"
İlk istekte Content-Type: image/webp ve çok daha küçük bir Content-Length, ikincisinde image/png ve orijinal boyutu görmelisiniz. İki yanıtta da Vary: Accept başlığı bulunmalı.
Tarayıcıdan kontrol etmek isterseniz geliştirici araçlarını açın (F12), Network sekmesinde “Img” filtresini seçin ve sayfayı yenileyin. Adresi .png ya da .jpg ile biten görsellerin Type sütununda webp yazıyorsa sistem çalışıyor demektir.
8) Dikkat edilmesi gerekenler
- CDN kullanıyorsanız (örneğin Cloudflare), ücretsiz planlarda
Vary: Acceptbaşlığı her zaman dikkate alınmayabilir. Bu durumda CDN'in kendi görsel optimizasyon özelliğini kullanmak ya da<picture>yöntemine geçmek daha güvenlidir. - Görsel boyutları: WebP dosyayı küçültür ama 4000 piksellik bir fotoğrafı 400 piksellik bir kutuda göstermek hâlâ israftır. Önce görseli gösterileceği boyuta yakın ölçekleyin, sonra dönüştürün.
- Genişlik ve yükseklik:
<img>etiketlerinewidthveheightyazmak, görsel yüklenirken sayfanın zıplamasını (CLS) önler.loading="lazy"ise ekranın altında kalan görsellerin sonradan yüklenmesini sağlar. - AVIF: WebP'den de küçük dosyalar üreten daha yeni bir format. Ancak üretmesi daha yavaş ve paylaşımlı hostinglerdeki PHP kurulumlarında desteği daha az yaygın. Aynı
.htaccessmantığı AVIF için de kurulabilir; şimdilik WebP benim için en iyi denge.
Sık sorulan sorular
WebP SEO'yu etkiler mi?
Dolaylı olarak evet, olumlu yönde. Google, sayfa hızını ve özellikle en büyük içerikli öğenin yüklenme süresini (LCP) sıralama sinyalleri arasında kullanıyor. Görseller genellikle bu öğenin kendisi olduğu için boyutlarının küçülmesi doğrudan bu metriği iyileştirir. Görselin adresi değişmediği için arama motorlarındaki mevcut görsel sonuçlarınız da etkilenmez.
Orijinal dosyaları silebilir miyim?
Bu yöntemde silmemelisiniz. Hem WebP desteklemeyen tarayıcılar hem de görseli paylaşan sosyal medya botlarının bir kısmı orijinal dosyayı ister. Disk alanı bugün görsellerden çok daha ucuz.
Nginx kullanıyorum, bu kural çalışır mı?
.htaccess dosyaları yalnızca Apache ve LiteSpeed'de çalışır. Nginx'te aynı mantık bir map bloğu ve try_files $uri.webp $uri satırıyla kurulabilir.
Hostingimde GD yoksa ne yapabilirim?
Kopyaları kendi bilgisayarınızda Google'ın ücretsiz cwebp aracıyla ya da Squoosh gibi bir uygulamayla üretip sunucuya yükleyebilirsiniz. .htaccess kuralı, kopyanın nasıl üretildiğiyle ilgilenmez; sadece orada olup olmadığına bakar.
Sonuç
Görselleri WebP'ye taşımak, bir siteyi hızlandırmak için yapabileceğiniz en yüksek getirili işlerden biri. Sunucu tarafı içerik anlaşması sayesinde bunu mevcut içeriğe dokunmadan, tamamen geri alınabilir şekilde yapabilirsiniz: birkaç satırlık bir .htaccess kuralı, kısa bir PHP fonksiyonu ve bir kerelik toplu dönüştürme. Bu sitede bunun karşılığı, ziyaretçi başına indirilen görsel verisinin altıda birine inmesi oldu.
Sorunuz olursa ya da kendi sitenizde uygularken bir yerde takılırsanız iletişim sayfasından bana yazabilirsiniz.