Caddy ile Compression Middleware ve Brotli Desteği

Modern web uygulamalarında sayfa yüklenme hızı artık bir lüks değil, zorunluluk. Google’ın Core Web Vitals metriklerine baktığında, sıkıştırma oranlarının doğrudan LCP ve FID skorlarını etkilediğini görüyorsun. Caddy, bu konuda hem kullanım kolaylığı hem de performans açısından oldukça güçlü seçenekler sunuyor. Bu yazıda Caddy’nin yerleşik sıkıştırma middleware’ini ve Brotli desteğini gerçek dünya senaryolarıyla ele alacağız.

Sıkıştırma Neden Bu Kadar Önemli?

Bir web sunucusu yapılandırması üzerinde çalışırken sıkıştırmayı atlamak, arabanın camını açık bırakarak klima açmak gibidir. Sunucudan istemciye giden her kilobyte, hem bant genişliği maliyetine hem de gecikmeye çevrilir.

Tipik bir senaryo düşün: 500KB’lık bir JavaScript dosyen var. Gzip ile bu yaklaşık 180KB’a iner. Brotli ile ise 150KB veya daha altına düşebilir. Mobil kullanıcılar için bu fark, 3 saniyelik yükleme ile 1.5 saniyelik yükleme arasındaki farktır.

Caddy bu konuda özel bir yere sahip, çünkü:

  • Gzip ve Zstandard desteği yerleşik olarak geliyor
  • Brotli desteği için ayrı bir modül gerekiyor ama entegrasyonu temiz
  • Yapılandırma sözdizimi diğer sunuculara kıyasla çok daha sade
  • HTTPS ile birlikte çalışırken ek yapılandırma gerektirmiyor

Caddy’nin Varsayılan Sıkıştırma Davranışı

Caddy kurulu geldiğinde encode direktifi ile sıkıştırma middleware’ini aktifleştirebilirsin. Varsayılan olarak hiçbir sıkıştırma aktif değildir, yani açıkça belirtmen gerekir.

Temel bir Caddyfile ile başlayalım:

# /etc/caddy/Caddyfile
example.com {
    encode gzip zstd
    root * /var/www/html
    file_server
}

Bu yapılandırmada encode direktifi hem gzip hem de Zstandard’ı aktifleştirir. Caddy, istemcinin Accept-Encoding başlığına bakarak hangisini desteklediğine karar verir ve buna göre sıkıştırma uygular.

Yapılandırmayı test etmek için:

# Caddy yapılandırmasını doğrula
caddy validate --config /etc/caddy/Caddyfile

# Servisi yeniden başlat
sudo systemctl reload caddy

# curl ile sıkıştırma başlıklarını kontrol et
curl -I -H "Accept-Encoding: gzip, deflate, br" https://example.com

Dönen başlıklarda Content-Encoding: gzip veya Content-Encoding: zstd görmelisin.

encode Direktifinin Detaylı Yapılandırması

encode direktifi sandığından daha fazla seçenek sunuyor. Minimum sıkıştırma boyutu, sıkıştırma seviyesi ve hangi content type’ların sıkıştırılacağı gibi parametreleri ayarlayabilirsin.

example.com {
    encode {
        gzip 6
        zstd
        minimum_length 1024
        match {
            header Content-Type text/plain*
            header Content-Type text/html*
            header Content-Type text/css*
            header Content-Type application/javascript*
            header Content-Type application/json*
            header Content-Type application/xml*
            header Content-Type image/svg+xml*
        }
    }
    root * /var/www/html
    file_server
}

Bu yapılandırmada dikkat edilmesi gereken noktalar:

  • gzip 6: Gzip sıkıştırma seviyesi 1-9 arasında ayarlanabilir. 6 iyi bir denge noktası. 9 maksimum sıkıştırma ama CPU yükü yüksek, 1 hız odaklı ama daha az sıkıştırma
  • minimum_length 1024: 1KB’dan küçük dosyaları sıkıştırma. Küçük dosyalarda sıkıştırma bazen dosyayı büyütebilir
  • match bloğu: Sadece belirtilen content type’lara sıkıştırma uygula. Zaten sıkıştırılmış PNG, JPEG gibi dosyalara sıkıştırma uygulamak boşuna CPU harcar

Brotli Desteği: Kurulum ve Entegrasyon

Brotli, Google tarafından geliştirilen ve özellikle metin tabanlı içerikler için gzip’e göre %15-25 daha iyi sıkıştırma oranı sunan bir algoritmadır. Caddy’nin standart dağıtımında Brotli yoktur, ancak xcaddy aracıyla Brotli modülünü derleyerek ekleyebilirsin.

xcaddy ile Brotli Modülü Derleme

Önce xcaddy aracını yükle:

# Go kurulu olduğunu varsayıyoruz
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

# xcaddy'nin PATH'te olduğunu kontrol et
xcaddy version

Brotli modülüyle birlikte Caddy’yi derle:

# Brotli desteğiyle Caddy derle
xcaddy build 
    --with github.com/ueffel/caddy-brotli

# Derlenen binary'yi yerleştir
sudo mv caddy /usr/bin/caddy

# İzinleri ayarla
sudo chmod +x /usr/bin/caddy
sudo setcap cap_net_bind_service=+ep /usr/bin/caddy

# Versiyonu ve modülleri doğrula
caddy version
caddy list-modules | grep brotli

Eğer caddy list-modules çıktısında http.encoders.br görüyorsan, Brotli modülü başarıyla yüklenmiştir.

Brotli ile Caddyfile Yapılandırması

Brotli modülü kurulduktan sonra yapılandırma oldukça basit:

example.com {
    encode {
        br
        gzip 6
        zstd
        minimum_length 1024
    }
    root * /var/www/html
    file_server
}

Caddy burada içerik müzakeresi (content negotiation) yaparak istemcinin Accept-Encoding başlığındaki tercihe göre en uygun algoritmayı seçer. Modern tarayıcılar Brotli’yi desteklediğinden, masaüstü ve mobil kullanıcıların büyük çoğunluğu otomatik olarak Brotli sıkıştırmasından yararlanır.

Brotli kurulumunu test etmek için:

# Brotli desteğini curl ile test et
curl -I -H "Accept-Encoding: br" https://example.com

# Detaylı başlık bilgisi al
curl -v -H "Accept-Encoding: br, gzip, deflate" 
    https://example.com 2>&1 | grep -i "content-encoding"

# Sıkıştırma öncesi ve sonrası boyut karşılaştırması
curl -s -H "Accept-Encoding: br" https://example.com | wc -c
curl -s https://example.com | wc -c

Gerçek Dünya Senaryosu 1: PHP Uygulaması için Caddy

Bir Laravel uygulaması çalıştırdığını varsay. JSON API yanıtları, HTML sayfaları ve statik dosyalar içeriyor. Bu senaryo için kapsamlı bir yapılandırma:

app.example.com {
    encode {
        br
        gzip 6
        zstd
        minimum_length 512
        match {
            header Content-Type application/json*
            header Content-Type text/html*
            header Content-Type text/plain*
            header Content-Type text/css*
            header Content-Type application/javascript*
            header Content-Type application/xml*
            header Content-Type image/svg+xml*
            header Content-Type font/woff*
            header Content-Type font/ttf*
            header Content-Type application/font-woff*
        }
    }

    root * /var/www/laravel/public
    php_fastcgi unix//run/php/php8.2-fpm.sock
    file_server

    # Statik dosyalar için cache header
    @static {
        file
        path *.css *.js *.png *.jpg *.jpeg *.gif *.ico *.svg *.woff *.woff2
    }
    header @static Cache-Control "public, max-age=31536000, immutable"

    # API route'ları için sıkıştırma önceliği Brotli
    @api {
        path /api/*
    }
    header @api Content-Type "application/json; charset=utf-8"
}

Bu yapılandırmada dikkat et: font dosyalarını da sıkıştırma listesine ekledim. WOFF2 zaten sıkıştırılmış bir format olduğundan bu satırı çıkarabilirsin, ama TTF ve OTF için sıkıştırma anlamlı bir boyut tasarrufu sağlar.

Gerçek Dünya Senaryosu 2: Reverse Proxy Arkasındaki Node.js Uygulaması

Caddy’yi bir Node.js uygulamasının önüne reverse proxy olarak koyduğunda, sıkıştırmayı Caddy tarafında yapmak uygulamanın CPU yükünü azaltır:

api.example.com {
    encode {
        br
        gzip 5
        zstd
        minimum_length 256
    }

    # Node.js uygulama sıkıştırma yapmasın diye başlık ekle
    header_up Accept-Encoding "identity"

    reverse_proxy localhost:3000 {
        header_up Host {upstream_hostport}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}

        health_uri /health
        health_interval 30s
        health_timeout 5s
    }

    # Güvenlik başlıkları
    header {
        X-Content-Type-Options "nosniff"
        X-Frame-Options "SAMEORIGIN"
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        -Server
    }
}

header_up Accept-Encoding "identity" satırı kritik bir nokta. Eğer bu satırı eklemezsen, Node.js uygulaması kendi sıkıştırmasını yapabilir ve Caddy ikinci kez sıkıştırmaya çalışabilir. Bu hem gereksiz CPU kullanımına yol açar hem de bazen hatalı yanıtlara neden olabilir.

Sıkıştırma Performansını İzleme

Sıkıştırmanın gerçekten çalışıp çalışmadığını ve ne kadar etki yarattığını ölçmek için birkaç yöntem var.

Caddy’nin varsayılan loglama formatı JSON olduğundan, log analizini kolaylaştırabilirsin:

# Caddy log yapılandırması - /etc/caddy/Caddyfile
{
    log {
        output file /var/log/caddy/access.log {
            roll_size 100mb
            roll_keep 10
        }
        format json
        level INFO
    }
}

example.com {
    log {
        output file /var/log/caddy/example.com.log
        format json
    }

    encode {
        br
        gzip 6
        zstd
        minimum_length 1024
    }

    root * /var/www/html
    file_server
}

Log dosyasından sıkıştırma etkinliğini analiz etmek için:

# Content-Encoding başlıklarını loglardan çek
cat /var/log/caddy/example.com.log | 
    python3 -c "
import sys, json
stats = {}
for line in sys.stdin:
    try:
        log = json.loads(line)
        enc = log.get('resp_headers', {}).get('Content-Encoding', ['none'])[0]
        stats[enc] = stats.get(enc, 0) + 1
    except:
        pass
for k, v in sorted(stats.items(), key=lambda x: -x[1]):
    print(f'{k}: {v}')
"

# Gerçek zamanlı istek izleme
tail -f /var/log/caddy/example.com.log | 
    grep -o '"Content-Encoding":"[^"]*"'

JSON Config ile İleri Seviye Yapılandırma

Caddyfile yerine JSON konfigürasyonunu tercih ediyorsan, encode middleware’i şöyle tanımlayabilirsin:

# Caddy JSON config'i API üzerinden uygula
curl -X POST "http://localhost:2019/load" 
    -H "Content-Type: application/json" 
    -d '{
  "apps": {
    "http": {
      "servers": {
        "myserver": {
          "listen": [":443"],
          "routes": [
            {
              "match": [{"host": ["example.com"]}],
              "handle": [
                {
                  "handler": "encode",
                  "encodings": {
                    "br": {},
                    "gzip": {"level": 6},
                    "zstd": {}
                  },
                  "minimum_length": 1024,
                  "prefer": ["br", "zstd", "gzip"]
                },
                {
                  "handler": "file_server",
                  "root": "/var/www/html"
                }
              ]
            }
          ]
        }
      }
    }
  }
}'

prefer alanı önemli: Brotli destekleyen istemcilere önce Brotli teklif et, desteklemeyenlere Zstandard, en son olarak Gzip. Bu şekilde algoritma öncelik sıralamasını açıkça belirleyebilirsin.

Önceden Sıkıştırılmış Dosyalar ile Precompression

Caddy’nin çok işe yarayan bir özelliği de önceden sıkıştırılmış dosyaları sunma yeteneği. Webpack veya build pipeline’ın .gz ve .br uzantılı dosyalar üretiyorsa, Caddy bunları gerçek zamanlı sıkıştırma yerine doğrudan sunabilir. Bu hem CPU’yu rahatlatır hem de daha tutarlı yanıt süreleri sağlar.

example.com {
    root * /var/www/dist

    file_server {
        precompressed br gzip
    }

    # Gerçek zamanlı sıkıştırma da aktif tut
    # (önceden sıkıştırılmış dosya yoksa devreye girer)
    encode {
        br
        gzip 6
        zstd
        minimum_length 1024
    }
}

Build sürecinde dosyaları önceden sıkıştırmak için:

# Tüm statik dosyaları gzip ve brotli ile sıkıştır
find /var/www/dist -type f 
    ( -name "*.html" -o -name "*.css" -o -name "*.js" 
       -o -name "*.json" -o -name "*.svg" -o -name "*.xml" ) | 
while read file; do
    # Gzip ile sıkıştır
    gzip -k -9 "$file"
    # Brotli ile sıkıştır (brotli paketi kurulu olmalı)
    brotli -k -q 11 "$file"
    echo "Sıkıştırıldı: $file"
done

# Sonuçları kontrol et
ls -lh /var/www/dist/*.js* | head -20

Bu yöntemde brotli komut satırı aracını kullanabilmek için sisteme kurman gerekir:

# Ubuntu/Debian
sudo apt install brotli

# RHEL/CentOS
sudo dnf install brotli

Yaygın Hatalar ve Çözümleri

Sıkıştırma yapılandırmasında karşılaşılan sorunlar genellikle birkaç kaynaktan gelir.

Çifte sıkıştırma problemi: Reverse proxy arkasında uygulama sunucusu da sıkıştırma yapıyorsa, istemci çözümleyemeyeceği bir içerik alabilir. Çözümü yukarıda gösterdim: header_up Accept-Encoding "identity" ile upstream’e sıkıştırma yapmamasını söyle.

HTTPS olmadan Brotli çalışmaz: Brotli, HTTP/2 ile birlikte tasarlanmıştır ve tarayıcılar Brotli desteğini sadece HTTPS bağlantılarda aktifleştirir. Caddy zaten otomatik HTTPS sağladığından bu genellikle sorun olmaz, ama test ortamında düz HTTP kullanıyorsan Brotli yanıtı göremeyebilirsin.

Küçük dosyalarda sıkıştırma olumsuz etki yaratıyor: minimum_length parametresini atlama. 512 byte’tan küçük dosyaları sıkıştırmak, sıkıştırma header overhead’ı nedeniyle dosyayı büyütebilir.

# Mevcut sıkıştırma yapılandırmasını kontrol et
caddy validate --config /etc/caddy/Caddyfile

# Canlı yapılandırmayı API üzerinden görüntüle
curl http://localhost:2019/config/ | python3 -m json.tool | grep -A 20 "encode"

# Belirli bir URL'de sıkıştırma çalışıyor mu?
curl -sv -H "Accept-Encoding: br, gzip" https://example.com/app.js 2>&1 | 
    grep -E "(content-encoding|content-length|< HTTP)"

Sonuç

Caddy ile sıkıştırma yapılandırması, Nginx veya Apache’ye kıyasla çok daha az karmaşık. Temel encode direktifi ile dakikalar içinde gzip ve Zstandard sıkıştırmasını aktifleştirebilir, biraz daha emek harcayarak xcaddy ile Brotli desteğini de ekleyebilirsin.

Gerçek dünya uygulamalarında önerdiğim yapılandırma sıralaması şu şekilde:

  • Önce encode direktifini ekle, gzip ile başla
  • minimum_length’i ayarla, küçük dosyaları sıkıştırmadan muaf tut
  • Content-type filtrelemesi yap, zaten sıkıştırılmış binary dosyaları hariç tut
  • Üretim ortamına geçmeden önce xcaddy ile Brotli modülünü derle ve ekle
  • Build pipeline’ın varsa precompressed dosyalarla gerçek zamanlı sıkıştırma maliyetini ortadan kaldır
  • Reverse proxy kullanıyorsan upstream sıkıştırmasını kapat

Performans ölçümlerini unutma. Sıkıştırma aktifleştirdikten sonra sayfa ağırlığını ve TTFB değerlerini kaydet. Gerçek etkiyi görmeden “yeterli” olduğuna karar verme. Çoğu durumda metin tabanlı içeriklerde %60-70 boyut azalması bekleyebilirsin; bu küçük bir konfigürasyon değişikliği için oldukça ciddi bir kazanım.

Yorum yapın