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:
.htaccesskullanıyorsanAllowOverride Allolmalı - 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:
-vve-Iflagleri 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.
