Nginx ile Gzip Sıkıştırma Sorunları ve Çözümleri

Web sunucunuzda sayfalar yavaş yükleniyorsa ve network trafiğiniz beklenenden fazlaysa, büyük ihtimalle gzip sıkıştırma ya hiç çalışmıyor ya da yanlış yapılandırılmış demektir. Nginx’te gzip konfigürasyonu ilk bakışta basit gibi görünse de pratikte onlarca farklı sorunla karşılaşabilirsiniz. Yıllarca üretim ortamlarında nginx yönetirken “gzip neden çalışmıyor?” sorusuyla defalarca boğuştum. Bu yazıda en sık karşılaşılan gzip sorunlarını, debug yöntemlerini ve çözüm yollarını gerçek senaryolarla ele alacağım.

Gzip Neden Önemli ve Nerede Yanlış Gider?

Gzip sıkıştırma, HTTP yanıtlarını istemciye göndermeden önce sıkıştırarak bant genişliği kullanımını dramatik biçimde azaltır. HTML, CSS, JavaScript gibi text tabanlı dosyalar %60-80 oranında küçülebilir. Ancak nginx’te bu özelliği etkinleştirmek “tek satır ekleyip geç” meselesi değil.

Sorunlar genellikle şu kategorilerde yaşanır:

  • Konfigürasyon hataları: Yanlış direktifler, eksik MIME type tanımlamaları
  • Proxy ve CDN çatışmaları: Upstream sunucularla gzip header çakışmaları
  • Statik dosya cache sorunları: Önceden sıkıştırılmış dosyaların tekrar sıkıştırılması
  • Buffer ve boyut sınırları: Küçük dosyaların sıkıştırılmaması
  • SSL/TLS ile etkileşim: BREACH gibi güvenlik açıkları nedeniyle gzip devre dışı bırakma

Temel Konfigürasyon ve Doğrulama

Önce standart bir gzip konfigürasyonuna bakalım:

# /etc/nginx/nginx.conf veya site konfigürasyonu
http {
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_min_length 256;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/x-javascript
        application/xml
        application/xml+rss
        application/json
        application/ld+json
        application/manifest+json
        font/ttf
        font/woff
        font/woff2
        image/svg+xml
        image/x-icon;
}

Konfigürasyonu yaptıktan sonra ilk doğrulama adımı nginx syntax kontrolü:

# Syntax kontrolü
nginx -t

# Konfigürasyonu reload et
systemctl reload nginx

# Gzip çalışıyor mu? curl ile test
curl -H "Accept-Encoding: gzip" -I https://example.com/style.css

# Daha detaylı test
curl -v -H "Accept-Encoding: gzip,deflate" https://example.com/ 2>&1 | grep -i "content-encoding"

Yanıtta Content-Encoding: gzip görüyorsanız temel konfigürasyon çalışıyor demektir. Görmüyorsanız debug sürecine girelim.

Sorun 1: Content-Encoding Header Gelmiyor

Bu en yaygın şikayet. Konfigürasyonu eklediniz, nginx’i reload ettiniz ama hala gzip header yok.

Adım 1: nginx.conf lokasyonunu kontrol edin

Bazen gzip direktiflerini yanlış bloğa yazıyoruz. Gzip sadece http bloğunda değil, server ve location bloklarında da tanımlanabilir. Ancak dikkat edilmesi gereken nokta, location bloğundaki tanımlama o lokasyon için geçerli olurken üst bloğu ezebilir.

# Aktif konfigürasyonu kontrol et
nginx -T | grep -A 20 "gzip"

# Include edilen dosyaları da görmek için
nginx -T 2>/dev/null | grep gzip

Adım 2: MIME type listesini kontrol edin

Sıkıştırma çalışmıyorsa dosya türü gzip_types listesinde olmayabilir. application/json API yanıtlarında sık yaşanan bir sorun:

# Dosyanın content-type'ını öğren
curl -I https://example.com/api/data | grep -i content-type

# O content-type için gzip çalışıyor mu?
curl -H "Accept-Encoding: gzip" -I https://example.com/api/data | grep -i content-encoding

Adım 3: gzip_min_length kontrolü

Çok küçük dosyalar sıkıştırılmaz. Varsayılan gzip_min_length 20 bayt iken bunu 256 veya 1024 olarak ayarladıysanız küçük dosyalar sıkıştırılmaz:

# Dosya boyutunu kontrol et
curl -s https://example.com/style.css | wc -c

# Eğer boyut gzip_min_length'ten küçükse sıkıştırılmaz
# Test için min_length'i 0 yap ve dene

Sorun 2: Proxy Arkasındaki Nginx’te Gzip Sorunları

Gerçek hayatta nginx çoğunlukla bir proxy olarak çalışır: upstream’de uygulama sunucusu (Node.js, PHP-FPM, Gunicorn vb.) vardır. Bu yapıda gzip sorunları karmaşıklaşır.

Şöyle bir senaryo düşünün: upstream uygulamanız zaten gzip sıkıştırılmış yanıt dönüyor, nginx de tekrar sıkıştırmaya çalışıyor. Sonuç bozuk yanıtlar veya double encoding.

# Upstream'in zaten gzip döndürüp döndürmediğini kontrol et
# (doğrudan upstream'e bağlanarak)
curl -H "Accept-Encoding: gzip" -I http://127.0.0.1:8080/api/test

# nginx access log'unda upstream başlıklarını görüntülemek için
# log formatını genişlet

Bu durumda nginx konfigürasyonunda dikkatli olunmalı:

# /etc/nginx/sites-available/myapp.conf

upstream myapp {
    server 127.0.0.1:8080;
}

server {
    listen 80;
    server_name example.com;

    # Upstream zaten sıkıştırıyorsa gunzip ile aç, nginx tekrar sıkıştırsın
    gunzip on;

    # Ya da proxy_pass ile upstream compression'ı yönet
    location / {
        proxy_pass http://myapp;
        
        # Upstream'e gzip isteği gönderme
        proxy_set_header Accept-Encoding "";
        
        # nginx tarafında sıkıştır
        gzip on;
        gzip_types text/plain application/json text/css application/javascript;
    }
}

gzip_proxied direktifi de kritik:

  • gzip_proxied off: Proxy yanıtlarını sıkıştırma (varsayılan)
  • gzip_proxied any: Tüm proxy yanıtlarını sıkıştır
  • gzip_proxied expired: Sadece Cache-Control: no-cache olmayan yanıtları sıkıştır
  • gzip_proxied no-cache: Cache-Control: no-cache başlıklı yanıtları sıkıştır
  • gzip_proxied no-store: Cache-Control: no-store başlıklı yanıtları sıkıştır
  • gzip_proxied auth: Authorization başlığı olan yanıtları sıkıştır

Sorun 3: CDN ve Load Balancer Katmanında Sorunlar

Cloudflare, AWS CloudFront veya benzeri bir CDN kullanıyorsanız, CDN kendi gzip/brotli sıkıştırmasını yapabilir. Bu durumda nginx’ten CDN’e gelen yanıtın sıkıştırılmış olup olmadığı ve CDN’in bunu nasıl ilettiği önemli.

Bir üretim ortamında karşılaştığım senaryo: CDN’den gelen Accept-Encoding: identity başlığı nedeniyle nginx gzip uygulamıyordu.

# CDN'nin nginx'e ne gönderdiğini görmek için geçici debug log ekle
# nginx.conf veya server bloğuna:

log_format debug_gzip '$remote_addr - $request '
                      'upstream_http_content_encoding: $upstream_http_content_encoding '
                      'sent_http_content_encoding: $sent_http_content_encoding '
                      'http_accept_encoding: $http_accept_encoding';

access_log /var/log/nginx/gzip_debug.log debug_gzip;
# Log'u izle
tail -f /var/log/nginx/gzip_debug.log

# Test isteği gönder
curl -H "Accept-Encoding: gzip" https://example.com/test

# Log'da http_accept_encoding alanı boşsa sorun burada

Sorun 4: Statik Dosyalar İçin gzip_static

Eğer build sürecinizde önceden .gz dosyaları oluşturuyorsanız (webpack, gulp vb.), nginx’in bu pre-compressed dosyaları kullanmasını sağlayabilirsiniz. Ancak bu yapıda da sorunlar çıkabilir.

# webpack veya build aracıyla önceden sıkıştırılmış dosya oluşturma
gzip -k -9 /var/www/html/assets/bundle.js
ls -la /var/www/html/assets/
# bundle.js ve bundle.js.gz ikisi de olmalı
# nginx.conf gzip_static konfigürasyonu
server {
    listen 80;
    server_name example.com;
    root /var/www/html;

    # Önceden sıkıştırılmış dosyaları kullan
    gzip_static on;

    location /assets/ {
        gzip_static on;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

gzip_static modülünün kurulu olup olmadığını kontrol edin:

nginx -V 2>&1 | grep -o "with-http_gzip_static_module"
# Çıktı "with-http_gzip_static_module" ise modül mevcut

# Eğer yoksa Ubuntu/Debian'da
apt-get install nginx-extras

# CentOS/RHEL'de
yum install nginx-mod-http-gzip-static

Sorun 5: Bozuk veya Tutarsız Gzip Yanıtları

Bazen gzip etkin ama tarayıcı sayfayı düzgün render etmiyor, ya da “Content Encoding Error” alıyorsunuz. Bu genellikle şu nedenlerden olur:

Double compression (çift sıkıştırma): Uygulama zaten gzip sıkıştırıyorsa ve nginx de sıkıştırıyorsa yanıt bozulur.

# Gerçek content-encoding durumunu gör
curl -v -H "Accept-Encoding: gzip" https://example.com/ 2>&1 | grep -E "(Content-Encoding|content-encoding)"

# Yanıtı decode et ve içeriği kontrol et
curl -H "Accept-Encoding: gzip" https://example.com/ | gunzip - | head -20

# Eğer gunzip hata veriyorsa double compression olabilir
# Ya da:
curl -H "Accept-Encoding: gzip" https://example.com/ > /tmp/response.gz
file /tmp/response.gz
# "gzip compressed data" çıkmalı, başka bir şey çıkıyorsa sorun var

Buffer sorunları: Büyük dosyalarda buffer boyutu yetersiz kalabilir:

# nginx.conf
http {
    gzip on;
    gzip_buffers 32 4k;  # ya da 16 8k - duruma göre ayarla
    
    # Proxy buffer ayarları da önemli
    proxy_buffer_size 128k;
    proxy_buffers 4 256k;
    proxy_busy_buffers_size 256k;
}

Sorun 6: Vary Header Eksikliği ve Cache Sorunları

Vary: Accept-Encoding başlığı olmadan CDN veya tarayıcı cache’i yanlış çalışabilir. Örneğin gzip desteklemeyen bir istemci, cache’deki sıkıştırılmış yanıtı alırsa sayfa bozuk görünür.

# Vary header kontrolü
curl -I -H "Accept-Encoding: gzip" https://example.com/ | grep -i vary

# Beklenen çıktı:
# Vary: Accept-Encoding
# Konfigürasyonda gzip_vary on olduğundan emin ol
# nginx.conf:
gzip_vary on;

# Bu direktif otomatik olarak Vary: Accept-Encoding ekler
# Eğer proxy kullanıyorsanız proxy_set_header Vary ile de yönetebilirsiniz

# Bazı CDN'ler Vary başlığını kaldırıyor, kontrol için:
curl -sI https://example.com | grep -i vary
curl -sI https://origin.example.com | grep -i vary
# İkisi aynı değilse CDN müdahale ediyor demektir

Log Analizi ile Gzip Sorunlarını Takip Etme

Üretim ortamında gzip sorunlarını izlemek için özel log formatı kullanmak hayat kurtarır:

# /etc/nginx/nginx.conf - genişletilmiş log format
http {
    log_format compression '$remote_addr - $remote_user [$time_local] '
                           '"$request" $status $bytes_sent '
                           '"$http_referer" "$http_user_agent" '
                           'rt=$request_time '
                           'ce="$sent_http_content_encoding" '
                           'ae="$http_accept_encoding" '
                           'ct="$sent_http_content_type" '
                           'bs=$bytes_sent '
                           'bl=$body_bytes_sent';

    access_log /var/log/nginx/access.log compression;
}

Bu log formatıyla hangi dosyaların sıkıştırıldığını, hangilerinin sıkıştırılmadığını ve bant genişliği tasarrufunu analiz edebilirsiniz:

# Gzip ile gönderilen istekleri filtrele
grep 'ce="gzip"' /var/log/nginx/access.log | wc -l

# Gzip olmadan gönderilen büyük yanıtları bul (potansiyel sorunlar)
awk '$NF ~ /ce=""/ && $8 > 10000' /var/log/nginx/access.log

# Toplam bytes_sent vs body_bytes_sent farkı (sıkıştırma oranı tahmini)
awk '{sum_bytes += $12; sum_body += $13} END {print "Total bytes:", sum_bytes, "Body bytes:", sum_body, "Ratio:", sum_body/sum_bytes}' /var/log/nginx/access.log

Güvenlik Notları: BREACH Saldırısı ve Gzip

BREACH (Browser Reconnaissance and Exfiltration via Adaptive Compression of Hypertext) saldırısı, SSL/TLS üzerinden gzip sıkıştırma kullanan sitelere karşı çalışır. Özellikle sayfada kullanıcı girdisi yansıtılıyorsa ve CSRF token gibi gizli değerler varsa risk artar.

Bu nedenle bazı güvenlik odaklı konfigürasyonlarda gzip’i seçici kullanmak gerekir:

# Sadece statik asset'lerde gzip, HTML sayfalarında kapalı
server {
    gzip off;  # Global kapalı

    location ~* .(css|js|svg|woff|woff2)$ {
        gzip on;
        gzip_types text/css application/javascript image/svg+xml font/woff font/woff2;
        expires 1y;
    }

    location / {
        # HTML içerik için gzip kapalı (BREACH önlemi)
        gzip off;
        proxy_pass http://app_backend;
    }
}

Performans Testi ve Doğrulama

Gzip düzgün çalışıyor mu, gerçekten tasarruf sağlıyor mu? Bunu ölçmek için:

# Sıkıştırılmış ve sıkıştırılmamış boyutları karşılaştır
echo "=== Gzip olmadan ==="
curl -s -o /dev/null -w "%{size_download} bytes" https://example.com/

echo ""
echo "=== Gzip ile ==="
curl -s -H "Accept-Encoding: gzip" -o /dev/null -w "%{size_download} bytes" https://example.com/

# Daha kapsamlı test - birden fazla kaynak için
for url in "/" "/style.css" "/app.js" "/api/data"; do
    size_plain=$(curl -s -o /dev/null -w "%{size_download}" https://example.com${url})
    size_gzip=$(curl -s -H "Accept-Encoding: gzip" -o /dev/null -w "%{size_download}" https://example.com${url})
    echo "URL: ${url} | Plain: ${size_plain} | Gzip: ${size_gzip} | Tasarruf: $(( (size_plain - size_gzip) * 100 / size_plain ))%"
done
# Apache Bench ile gzip performans testi
ab -n 1000 -c 50 -H "Accept-Encoding: gzip" https://example.com/

# siege ile test
siege -c 25 -t 60S -H "Accept-Encoding: gzip" https://example.com/

Hızlı Kontrol Listesi

Gzip sorun yaşadığınızda sırayla şunları kontrol edin:

  • nginx -t çıktısı temiz mi?
  • Gzip direktifleri doğru blokta mı (http, server veya location)?
  • gzip_types listesinde soruna neden olan MIME type var mı?
  • Dosya boyutu gzip_min_length değerinden büyük mü?
  • gzip_proxied değeri proxy setup’ınıza uygun mu?
  • Upstream uygulama zaten gzip gönderiyor mu (double compression)?
  • gzip_vary on ayarlı mı?
  • CDN veya load balancer Accept-Encoding başlığını değiştiriyor mu?
  • gzip_static için .gz dosyaları mevcut ve güncel mi?
  • nginx’in doğru modüllerle derlenmiş mi (nginx -V)?

Sonuç

Nginx’te gzip sıkıştırma sorunları genellikle basit konfigürasyon hatalarından ya da katmanlı mimari (proxy, CDN, load balancer) etkileşimlerinden kaynaklanır. Sorun yaşadığınızda panik yapmadan önce curl -v ile header analizi yapın, log formatını genişleterek veriyi toplayın ve katman katman debug edin.

Üretim ortamında gzip’i etkinleştirirken BREACH güvenlik açığını göz önünde bulundurun, özellikle dinamik HTML içerik sunan uygulamalarda seçici davranın. Statik asset’ler için gzip_static ile pre-compressed dosyalar kullanmak hem CPU yükünü azaltır hem de yanıt sürelerini iyileştirir.

En önemlisi, değişiklik yaptıktan sonra her zaman ölçün. Gzip ne kadar tasarruf sağladığını, hangi içerik türlerinde etkili olduğunu ve gerçek kullanıcı deneyimine katkısını log analizi ve performans testleriyle doğrulayın. Sayısal veri olmadan yapılan optimizasyon, tahmin oyunundan öteye geçmez.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir