Apache mod_rewrite Kurallarını Debug Etme Rehberi

mod_rewrite kuralları yazarken bir noktada hepimiz o karanlık tünele giriyoruz: kural yazıyorsun, test ediyorsun, çalışmıyor. Biraz değiştiriyorsun, hâlâ çalışmıyor. Sonunda saatlerce uğraşıp “acaba sunucu yeniden başlatmayı unuttum mu?” diye soruyorsun kendine. Bu yazıda mod_rewrite debug sürecini baştan sona ele alacağız, gerçek hayatta karşılaşılan senaryolar üzerinden gideceğiz ve bir daha o karanlık tünelde kaybolmayacaksın.

mod_rewrite Neden Bu Kadar Zor Debug Edilir?

mod_rewrite’ın debug edilmesi zor olmasının birkaç nedeni var. Birincisi, kurallar sırayla çalışır ve her kural bir sonrakini etkileyebilir. İkincisi, .htaccess dosyasındaki kurallar ile VirtualHost bloğundaki kurallar farklı davranır. Üçüncüsü ve belki de en önemlisi, hata mesajları genellikle sana tam olarak neyin yanlış gittiğini söylemez.

Ama doğru araçları ve teknikleri kullanırsan bu işi çok daha az acı verici hale getirebilirsin.

RewriteLog: Temel Silahın

Apache 2.2 ve öncesinde RewriteLog ve RewriteLogLevel direktifleri ayrıydı. Apache 2.4 ile birlikte bu ikisi LogLevel direktifi altında birleştirildi. Önce her iki sürüm için nasıl kullanacağını görelim.

Apache 2.4 için LogLevel Ayarı

# httpd.conf veya VirtualHost bloğuna ekle
LogLevel alert rewrite:trace3

Bu kadar basit. trace3 yerine trace1 ile trace8 arasında değerler kullanabilirsin. trace8 en verbose olanı, gerçekten her şeyi yazar. Ama dikkatli ol, production ortamında trace8 kullanmak log dosyalarını dakikalar içinde gigabaytlara çıkarabilir.

Seviye açıklamaları:

  • trace1: Temel yeniden yazma işlemleri
  • trace2: Kural eşleşmeleri ve sonuçları
  • trace3: RewriteCond değerlendirmeleri dahil
  • trace4: Tüm rewrite adımları
  • trace5: Detaylı kural izleme
  • trace6-8: Çok detaylı, genellikle geliştirici seviyesi
# Sadece belirli bir VirtualHost için açmak istersen
<VirtualHost *:443>
    ServerName example.com
    LogLevel warn rewrite:trace3
    ErrorLog /var/log/apache2/example_error.log
    ...
</VirtualHost>

Apache 2.2 için (Hâlâ Legacy Sistemler Var!)

# httpd.conf içine
RewriteLog /var/log/apache2/rewrite.log
RewriteLogLevel 3

Eski sistemleri yönetiyorsan bu direktifler hâlâ geçerli. Ama Apache 2.4 kullanıyorsan bu direktifler artık çalışmaz, sessizce görmezden gelinir, bu da kafanı karıştırabilir.

Log Çıktısını Okumak

Log açıkken bir istek yaptığında error.log dosyasında şöyle bir şey görürsün:

tail -f /var/log/apache2/error.log | grep rewrite

Çıktı böyle görünür:

[rewrite] init rewrite engine with requested uri /urunler/laptop/dell-xps
[rewrite] applying pattern '^urunler/([^/]+)/([^/]+)$' to uri 'urunler/laptop/dell-xps'
[rewrite] RewriteCond: input='/urunler/laptop/dell-xps' pattern='!^/admin' => matched
[rewrite] rewrite 'urunler/laptop/dell-xps' -> 'index.php?kategori=laptop&urun=dell-xps'
[rewrite] go-ahead with proxy request

Her satırı okumayı öğrenmek çok önemli. applying pattern satırı hangi kurala bakalıdığını söyler. => matched veya => not-matched sonucu söyler. rewrite ... -> ise dönüşümün ne olduğunu gösterir.

Gerçek Dünya Senaryosu 1: URL Yeniden Yazma Çalışmıyor

Diyelim ki bir e-ticaret sitesi yönetiyorsun ve SEO dostu URL’ler için şu kural var:

# .htaccess dosyası
RewriteEngine On
RewriteRule ^urunler/([^/]+)/([^/]+)$ index.php?kategori=$1&urun=$2 [L]

Test ediyorsun: https://example.com/urunler/laptop/dell-xps adresine gidiyorsun, 404 alıyorsun. Log’a bakıyorsun:

grep "rewrite" /var/log/apache2/error.log | tail -20

Log’da hiçbir şey görmüyorsun. Neden? Çünkü RewriteEngine On direktifi yok, veya .htaccess dosyası hiç okunmuyor olabilir.

İlk kontrol etmen gereken şey AllowOverride ayarı:

# Bu olması gerekiyor:
<Directory /var/www/html>
    AllowOverride All
</Directory>

# Bu ise .htaccess'i devre dışı bırakır:
<Directory /var/www/html>
    AllowOverride None
</Directory>

AllowOverride None ise Apache .htaccess dosyasını tamamen görmezden gelir. Bunu değiştirip Apache’yi yeniden başlatman gerekir:

sudo systemctl reload apache2
# veya
sudo apachectl graceful

Gerçek Dünya Senaryosu 2: Sonsuz Yönlendirme Döngüsü

Bu klasik bir problem. Tarayıcı “Too many redirects” hatası veriyor. Kural muhtemelen şöyle bir şey:

RewriteEngine On
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]

Bu kural www olmayan istekleri www’ya yönlendirmeye çalışıyor, ama kendi kendini de yakalıyor. Log’a bakınca şunu görürsün:

[rewrite] rewrite 'index.html' -> 'https://www.example.com/index.html'
[rewrite] redirect to https://www.example.com/index.html
[rewrite] rewrite 'index.html' -> 'https://www.example.com/index.html'
[rewrite] redirect to https://www.example.com/index.html
... (sonsuz döngü)

Düzeltmesi şu:

RewriteEngine On
RewriteCond %{HTTP_HOST} !^www. [NC]
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]

Ya da daha temiz bir yaklaşım:

RewriteEngine On
RewriteCond %{HTTP_HOST} ^example.com$ [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]

RewriteCond ile önce host kontrolü yapıyoruz. Zaten www.example.com üzerindeyse kural tetiklenmiyor, döngü oluşmuyor.

RewriteRule Flags ve Debug

Bayraklar (flags) debug sürecinde sıkça atlanan bir nokta. Yaygın bayraklar ve ne işe yaradıkları:

  • [L]: Last, bu kuraldan sonra durdur
  • [R=301]: Redirect, tarayıcıyı yönlendir
  • [P]: Proxy, isteği proxy’e ilet
  • [NC]: No Case, büyük/küçük harf duyarsız
  • [QSA]: Query String Append, mevcut query string’i koru
  • [F]: Forbidden, 403 döndür
  • [G]: Gone, 410 döndür
  • [E=VAR:VAL]: Ortam değişkeni set et
  • [T=mime/type]: MIME type belirle
  • [NE]: No Escape, özel karakterleri escape etme
  • [PT]: Pass Through, URL’yi sonraki handler’a geçir
# QSA olmadan:
# /ara?q=laptop -> /search.php (orijinal query string kaybolur)
RewriteRule ^ara$ /search.php [L]

# QSA ile:
# /ara?q=laptop -> /search.php?q=laptop
RewriteRule ^ara$ /search.php [L,QSA]

mod_rewrite Test Aracı: RewriteRule Tester

Kural yazmadan önce test etmek istiyorsan birkaç yöntem var.

htaccess tester kullanmak (geliştirme ortamında)

Geliştirme ortamında curl ile test yapabilirsin:

# -I sadece header bilgisini gösterir, -L redirect'leri takip eder
curl -I -L http://localhost/eski-url

# Verbose mod, tüm header'ları göster
curl -v http://localhost/urunler/laptop/dell-xps

# Redirect chain'i görmek için
curl -I -L --max-redirs 10 http://localhost/test-url

Apache yapılandırmasını test etmek

Kural değişikliği yaptıktan sonra Apache’yi restart etmeden önce:

# Yapılandırma dosyasında syntax hatası var mı?
sudo apachectl configtest
# veya
sudo apache2ctl -t

# Tüm virtual host'ları listele
sudo apache2ctl -S

# Hangi modüller yüklü?
sudo apache2ctl -M | grep rewrite

Eğer rewrite_module listede yoksa mod_rewrite etkin değildir:

sudo a2enmod rewrite
sudo systemctl restart apache2

Gerçek Dünya Senaryosu 3: HTTPS Yönlendirmesi ile Çakışan Kurallar

Birden fazla kural yazınca işler karmaşıklaşır. Hem HTTP’yi HTTPS’e, hem de www olmayanı www’ya yönlendiriyorsun diyelim:

RewriteEngine On

# HTTP -> HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

# www olmayan -> www
RewriteCond %{HTTP_HOST} !^www. [NC]
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Bu iki kural birlikte yazılınca sorun çıkabilir. [L] bayrağı .htaccess içinde “bu kural setinde dur” anlamına gelir, Apache’nin tüm yeniden yazma motorunu durdurmaz. Yani ilk kural çalışır, URL güncellenir, ama ikinci kural yeni URL üzerinde yeniden çalışabilir.

Daha temiz yaklaşım her şeyi tek bir kuralda birleştirmek:

RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www. [NC]
RewriteRule ^(.*)$ https://www.example.com%{REQUEST_URI} [R=301,L]

Bu kural hem HTTPS değilse hem de www yoksa yönlendirme yapıyor, her iki durumu da tek seferde hallediyor.

Log Analizi için Faydalı Komutlar

Gerçek production ortamında log analizi yaparken şu komutlar çok işe yarar:

# Sadece rewrite log satırlarını filtrele ve izle
tail -f /var/log/apache2/error.log | grep -i rewrite

# Belirli bir URL için rewrite adımlarını bul
grep "urunler/laptop" /var/log/apache2/error.log | grep rewrite

# En son 50 rewrite işlemini göster
grep "rewrite" /var/log/apache2/error.log | tail -50

# Rewrite hatalarını say (hangi pattern en çok başarısız oluyor?)
grep "not-matched" /var/log/apache2/error.log | awk '{print $NF}' | sort | uniq -c | sort -rn | head -10
# Apache log formatına timestamp ekleyerek daha okunabilir yap
grep "rewrite" /var/log/apache2/error.log | awk '{print $1, $2, $NF}' | tail -20

Ortam Değişkenleri ile Debug

Bazen kurallarda neler olduğunu anlamak için ortam değişkenleri set edip sonra header’da görmek işe yarar:

# .htaccess
RewriteEngine On

# Bu kural çalışırsa debug değişkeni set et
RewriteRule ^test-url$ /index.php [E=DEBUG_REWRITE:1,L]

# Header olarak gönder (sadece geliştirme ortamında!)
Header set X-Rewrite-Debug "%{DEBUG_REWRITE}e" env=DEBUG_REWRITE

Sonra curl ile test et:

curl -I http://localhost/test-url
# X-Rewrite-Debug: 1 header'ını görmelisin

Bu yöntem özellikle kompleks kural setlerinde hangi kurulun tetiklendiğini anlamak için çok faydalı.

RewriteMap ile Gelişmiş Debugging

Büyük ölçekli URL yönetimlerinde RewriteMap kullanılır. Bu da debug sürecinde ayrı bir karmaşıklık katmanı ekler:

# httpd.conf içinde (VirtualHost dışında veya server config seviyesinde)
RewriteMap yonlendirmeler txt:/var/www/yonlendirmeler.txt

<VirtualHost *:443>
    RewriteEngine On
    RewriteMap yonlendirmeler txt:/var/www/yonlendirmeler.txt
    RewriteCond %{REQUEST_URI} ^/eski/(.+)$
    RewriteRule ^/eski/(.+)$ ${yonlendirmeler:$1|/404} [R=301,L]
</VirtualHost>

Map dosyası (yonlendirmeler.txt):

urun-a    /yeni/urunler/urun-a
kategori-x    /yeni/kategoriler/kategori-x
eski-blog-post    /blog/yeni-baslik

Debug ederken map dosyasının doğru okunup okunmadığını kontrol etmek için:

# Map dosyasının Apache tarafından erişilebilir olduğunu kontrol et
ls -la /var/www/yonlendirmeler.txt
sudo -u www-data cat /var/www/yonlendirmeler.txt

# Cache'lenen map'i temizlemek için Apache'yi graceful restart
sudo apachectl graceful

Sık Yapılan Hatalar ve Çözümleri

Hata 1: Pattern’da slash problemi

# YANLIŞ - Pattern başında slash olmamalı (genellikle)
RewriteRule ^/urunler/(.+)$ /index.php?id=$1 [L]

# DOĞRU - .htaccess'te REQUEST_URI başındaki slash zaten yok
RewriteRule ^urunler/(.+)$ /index.php?id=$1 [L]

# DOĞRU - VirtualHost veya server config'de slash olabilir
RewriteRule ^/urunler/(.+)$ /index.php?id=$1 [L]

Hata 2: RewriteBase eksik

# Subdirectory'de çalışıyorsan RewriteBase şart
# Site: example.com/mağaza/ dizininde çalışıyor
RewriteEngine On
RewriteBase /magaza/
RewriteRule ^urun/([0-9]+)$ index.php?id=$1 [L]

Hata 3: Regex özel karakterleri

# Nokta her karakteri yakalar, escape etmek gerekir
# YANLIŞ: example.com yazmak, .com kısmı her karakteri yakalar
RewriteCond %{HTTP_HOST} ^example.com$

# DOĞRU: Noktayı escape et
RewriteCond %{HTTP_HOST} ^example.com$

Performance: Production’da Debug Modunu Kapatmak

Debug işi bitince mutlaka log seviyesini düşür. Bu çok önemli:

# Debug bitti, normal seviyeye dön
LogLevel warn

# Veya sadece rewrite için
LogLevel warn rewrite:warn

# Değişikliği uygula (restart yerine graceful tercih et)
sudo apachectl graceful

Log dosyasının boyutunu kontrol et:

# Log dosyası ne kadar büyümüş?
du -sh /var/log/apache2/error.log

# Son ne zaman döndürüldü?
ls -lh /var/log/apache2/

# Logrotate durumu
sudo logrotate -d /etc/logrotate.d/apache2

Gerçek Dünya Senaryosu 4: WordPress veya PHP Uygulaması ile Birlikte Çalışmak

WordPress gibi frameworkler kendi .htaccess kurallarını yazar. Özel kurallar ekleyince çakışmalar olabilir:

# WordPress varsayılan .htaccess
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress

# ÖZEL KURALLAR BURAYA GELMELİ - WordPress bloğunun ÖNÜNE!
# Yanlış yere eklersen WordPress kuralları senin kurallarını geçersiz kılar
RewriteRule ^api/(.*)$ https://api.example.com/$1 [P,L]

Bu durumda debug ederken sıra çok önemli. Log’da kuralların hangi sırayla değerlendirildiğini takip et.

Özet: Debug Akışı

Bir kural çalışmıyorsa şu sırayla ilerle:

  • mod_rewrite’ın yüklü olduğunu kontrol et: apache2ctl -M | grep rewrite
  • AllowOverride ayarını kontrol et: .htaccess kullanıyorsan AllowOverride All olmalı
  • LogLevel’ı trace3 yap: LogLevel warn rewrite:trace3
  • Apache’yi graceful restart yap: apachectl graceful
  • Log’u izle: tail -f /var/log/apache2/error.log | grep rewrite
  • curl ile test yap: -v ve -I flagleri kullan
  • Kuralları tek tek test et: Karmaşık kural setlerini parça parça test et
  • Regex’i ayrı test et: Bir regex tester kullan (regex101.com gibi)
  • Debug bitti mi? LogLevel’ı geri al: Production’da trace seviyesi bırakma

Sonuç

mod_rewrite debug etmek başta korkutucu görünüyor ama aslında sistematik bir süreç. En önemli nokta doğru log seviyesini açmak ve log çıktısını okumayı öğrenmek. trace3 seviyesinde Apache sana neredeyse her adımı anlatıyor, sadece dinlemeyi bilmek gerekiyor.

Production ortamında asla tahmin yürütme. Log aç, bak, anla, düzelt, kapat. Özellikle büyük trafik alan sitelerde trace seviyesi log sadece birkaç dakika içinde diski doldurabilir, bu yüzden log monitoring’i ihmal etme.

Son olarak şunu söyleyeyim: En iyi mod_rewrite debug aracı, kuralları yazmadan önce ne yapmak istediğini net olarak bilmek. Regex’i anlamadan, HTTP protokolünü bilmeden mod_rewrite ile güreşmek boşa kürek çekmek gibi. Temel regex ve HTTP bilgisi, saatlik debug seanslarını dakikalar içinde çözebilir hale getirir.

Benzer Konular

Bir yanıt yazın

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