.htaccess Hataları: Yaygın Sorunlar ve Çözümleri

Apache web sunucusu yönetiyorsanız, .htaccess dosyasıyla ilgili bir sorun yaşamadan geçen bir hafta nadiren olur. Syntax hatası, yanlış direktif sıralaması ya da izin sorunları; bunların hepsi sitenizi anında çökertebilir veya beklenmedik davranışlara yol açabilir. Bu yazıda en sık karşılaşılan .htaccess sorunlarını, hata ayıklama yöntemlerini ve pratik çözümleri ele alacağım.

.htaccess Nedir ve Neden Bu Kadar Sorunlu?

.htaccess, Apache’nin dizin bazlı yapılandırma dosyasıdır. Güçlüdür çünkü sunucu yeniden başlatmadan değişiklik yapmanıza izin verir. Ama aynı zamanda tehlikelidir çünkü tek bir syntax hatası tüm sitenizi 500 Internal Server Error’a düşürebilir.

Sorunların büyük kısmı şu üç kategoride toplanır:

  • Syntax hataları: Yanlış yazılmış direktifler veya eksik karakterler
  • İzin sorunları: AllowOverride ayarları veya dosya izinleri
  • Mantık hataları: Doğru yazılmış ama yanlış sıralanmış kurallar

Hadi bunları tek tek inceleyelim.

Temel Hata Ayıklama: Apache Log’larını Okumak

Her şeyden önce, sorun yaşandığında ilk bakılacak yer Apache error log’udur. Log dosyasını gerçek zamanlı takip etmek için:

# Debian/Ubuntu sistemlerde
sudo tail -f /var/log/apache2/error.log

# RHEL/CentOS sistemlerde
sudo tail -f /var/log/httpd/error_log

# Belirli bir virtual host için
sudo tail -f /var/log/apache2/mysite.com-error.log

Bir .htaccess hatası genellikle şu formatta görünür:

[Fri Nov 10 14:23:01.123456 2023] [core:alert] [pid 1234] [client 192.168.1.100:52341]
/var/www/html/.htaccess: Invalid command 'Rewrit', perhaps misspelled or
defined by a module that has not been included in the server configuration

Bu log satırında dikkat etmeniz gereken üç şey var: hata türü (core:alert), hata veren dosya (/var/www/html/.htaccess) ve hata mesajı (Invalid command). Çoğu durumda bu üç bilgi problemi doğrudan işaret eder.

Apache konfigürasyonunu test etmek için sunucu yeniden başlatmadan önce şu komutu çalıştırın:

# Syntax kontrolü
sudo apache2ctl configtest

# Ya da
sudo apachectl -t

# Detaylı çıktı için
sudo apache2ctl -t -D DUMP_VHOSTS

Çıktı Syntax OK diyorsa genel Apache konfigürasyonunda sorun yok demektir. Ancak .htaccess dosyaları bu komutla tam olarak test edilmez; onlar için farklı yöntemler gerekiyor.

Sorun 1: AllowOverride Kapalı

Bu, en sık karşılaşılan ve insanların saatlerce uğraştığı sorundur. .htaccess dosyası mükemmel görünür, ama hiçbir şey çalışmaz.

Belirti: Yönlendirmeler çalışmıyor, authentication isteniyor ama geçilemiyor, ya da yazdığınız kurallar tamamen görmezden geliniyor.

Apache’nin ana konfigürasyon dosyasında (/etc/apache2/apache2.conf veya /etc/httpd/conf/httpd.conf) şu bloğu arayın:

sudo grep -n "AllowOverride" /etc/apache2/apache2.conf
# veya
sudo grep -rn "AllowOverride" /etc/apache2/sites-enabled/

Eğer şunu görüyorsanız problem budur:

<Directory /var/www/>
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

AllowOverride None Apache’ye “.htaccess dosyalarını tamamen yok say” diyor. Bunu düzeltmek için:

<Directory /var/www/html>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Require all granted
</Directory>

Değişiklikten sonra Apache’yi yeniden yükleyin:

sudo systemctl reload apache2
# veya CentOS/RHEL için
sudo systemctl reload httpd

Not: AllowOverride All her şeye izin verir ama güvenlik açısından riskli olabilir. Sadece ihtiyaç duyduğunuz direktiflere izin vermeyi tercih edin:

AllowOverride AuthConfig FileInfo Indexes Limit Options

Sorun 2: mod_rewrite Yüklü Değil

Yönlendirme kuralları yazdınız, 500 hatası alıyorsunuz veya kurallar çalışmıyor. İlk kontrol edin: mod_rewrite aktif mi?

# Aktif modülleri listele
apache2ctl -M | grep rewrite

# Eğer çıktı boşsa modül yüklü değil demektir
# Ubuntu/Debian'da etkinleştirmek için:
sudo a2enmod rewrite
sudo systemctl restart apache2

# RHEL/CentOS'ta httpd.conf içinde şu satırın başındaki # işaretini kaldırın:
# LoadModule rewrite_module modules/mod_rewrite.so

mod_rewrite aktifse ve hâlâ çalışmıyorsa, .htaccess dosyanızın başında RewriteEngine On direktifi olduğundan emin olun:

# Doğru kullanım
RewriteEngine On
RewriteBase /

# Tipik WordPress yönlendirmesi
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

Sorun 3: Sonsuz Yönlendirme Döngüsü

“Too many redirects” hatası alan kullanıcılar için bu bölüm biçilmiş kaftan. Tarayıcı ERR_TOO_MANY_REDIRECTS hatasını gösteriyorsa .htaccess’teki yönlendirme kuralları birbirini tetikliyor demektir.

Gerçek dünya senaryosu: HTTP’den HTTPS’e yönlendirme yazmak istiyorsunuz ama yanlış koşul yazınca döngüye giriyorsunuz.

Yanlış kullanım:

# BU YANLISTIR - sonsuz döngü yaratır
RewriteEngine On
RewriteRule ^(.*)$ https://example.com/$1 [R=301,L]

Doğru kullanım:

RewriteEngine On

# HTTPS kontrolü - zaten HTTPS ise kural çalışmasın
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Proxy ya da load balancer arkasındaysanız (Cloudflare, AWS ELB gibi) bu koşul çalışmayabilir çünkü Apache’ye gelen trafik HTTP olarak görünür. Bu durumda:

RewriteEngine On

# X-Forwarded-Proto başlığını kontrol et
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Döngüyü debug etmek için tarayıcının developer tools’unu açın, Network sekmesine gidin ve sayfayı yenileyin. Ardışık 301/302 yönlendirmelerini göreceksiniz; bu, hangi URL’nin döngüye girdiğini anlamanıza yardımcı olur.

Sorun 4: 403 Forbidden Hatası

.htaccess’ten kaynaklanan 403 hataları genellikle iki farklı sebepten gelir: yanlış Options direktifi veya yanlış Require kuralları.

# Error log'da şunu göreceksiniz:
[client 192.168.1.100:52341] AH01276: Cannot serve directory /var/www/html/:
No matching DirectoryIndex (index.html, index.php) found, and
server-generated directory index forbidden by Options directive

Bu hatayı almak için dizinde index dosyası yoktur ve dizin listeleme kapalıdır. Çözümler:

# Seçenek 1: Dizin listelemeyi aç (dikkatli olun!)
Options +Indexes

# Seçenek 2: Varsayılan index dosyası tanımla
DirectoryIndex index.html index.php default.htm

# Seçenek 3: Özel 403 sayfasına yönlendir
ErrorDocument 403 /403.html

IP tabanlı erişim kısıtlamalarından kaynaklanan 403 için ise modern Apache (2.4+) ve eski Apache (2.2) sözdizimi farklıdır. Bu fark çok kafa karıştırır:

# Apache 2.4+ (modern) - DOĞRU
<Files "wp-config.php">
    Require all denied
</Files>

# Belirli IP'ye izin ver, geri kalanı engelle
<Location /admin>
    Require ip 192.168.1.0/24
    Require ip 10.0.0.1
</Location>

# Apache 2.2 (eski) - ARTIK KULLANILMIYOR
# Order allow,deny
# Allow from 192.168.1.0/24
# Deny from all

Eğer Apache 2.4 kullanıyorsunuz ve eski sözdizimi görüyorsanız, bu 403’e veya beklenmedik davranışlara yol açar. Tüm access control direktiflerini modern sözdizime geçirin.

Sorun 5: .htpasswd ile Authentication Sorunları

Dizin koruma işlemi yaygın bir kullanım senaryosudur ama sıkça hata yapılır. Tipik kurulum:

# Önce .htpasswd dosyası oluşturun
sudo htpasswd -c /etc/apache2/.htpasswd admin

# Var olan dosyaya kullanıcı eklemek için -c kullanmayın! (dosyayı siler)
sudo htpasswd /etc/apache2/.htpasswd newuser

.htaccess içinde authentication tanımı:

AuthType Basic
AuthName "Yonetim Paneli"
AuthUserFile /etc/apache2/.htpasswd
Require valid-user

Yaygın hata: AuthUserFile için göreceli yol kullanmak.

# YANLIS - göreceli yol
AuthUserFile .htpasswd

# DOGRU - mutlak yol
AuthUserFile /var/www/html/private/.htpasswd

Göreceli yol kullanıldığında Apache dosyayı bulamaz ve error log’da şunu görürsünüz:

[auth_basic:error] [pid 1234] AH01620: Could not open password file:
/etc/apache2/.htpasswd

.htpasswd dosyasını web’den erişilemez bir yere koymak iyi bir güvenlik pratiğidir:

# Web root dışına koy
AuthUserFile /etc/apache2/passwords/.htpasswd

# Ya da web root içindeyse .htaccess ile koru
<Files ".htpasswd">
    Require all denied
</Files>

Sorun 6: MIME Tipi ve Karakter Seti Sorunları

Türkçe karakter sorunları, özellikle eski PHP projelerinde sıkça karşılaşılır. .htaccess üzerinden charset ayarlamak mümkündür:

# Varsayılan charset ayarla
AddDefaultCharset UTF-8

# Belirli dosya tipleri için
AddType 'text/html; charset=UTF-8' .html .htm
AddType 'text/css; charset=UTF-8' .css

# PHP dosyaları için
AddType 'application/x-httpd-php' .php .php5

Öte yandan yanlış MIME tipi tanımlamaları güvenlik sorunlarına yol açabilir:

# SVG dosyalarına JavaScript çalıştırma hakkı vermeyin
AddType image/svg+xml .svg

# Bu YANLISTIR:
# AddType application/javascript .svg

Gzip sıkıştırma aktifleştirirken de MIME tipi hatası yaygın görülür:

# mod_deflate ile gzip sıkıştırma
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/plain
    AddOutputFilterByType DEFLATE text/html
    AddOutputFilterByType DEFLATE text/xml
    AddOutputFilterByType DEFLATE text/css
    AddOutputFilterByType DEFLATE application/xml
    AddOutputFilterByType DEFLATE application/xhtml+xml
    AddOutputFilterByType DEFLATE application/rss+xml
    AddOutputFilterByType DEFLATE application/javascript
    AddOutputFilterByType DEFLATE application/x-javascript
</IfModule>

Sorun 7: RewriteRule Regex Hataları

Regex hataları sessiz sedasız çalışmayan kurallar üretir. Hata mesajı görmezsiniz ama kural da çalışmaz.

# Error log'da şunu görürseniz regex problemi vardır:
[core:error] [pid 1234] AH00526: Syntax error on line 15 of
/var/www/html/.htaccess: RewriteRule: bad flag delimiters

Yaygın regex hataları ve çözümleri:

# YANLIS - slash escape edilmemiş
RewriteRule ^/old-page$ /new-page [R=301,L]

# DOGRU - Apache'de leading slash gerekmez
RewriteRule ^old-page$ /new-page [R=301,L]

# YANLIS - RewriteBase ile çakışma
RewriteBase /subdir/
RewriteRule ^(.*)$ /index.php/$1 [L]

# DOGRU
RewriteBase /subdir/
RewriteRule ^(.*)$ index.php/$1 [L]

RewriteRule test etmek için Apache’nin mod_rewrite log’lamasını geçici olarak açabilirsiniz. Bu production’da yapmayın, sadece development ortamında:

# Virtual host konfigürasyonuna ekleyin (sadece test için!)
LogLevel alert rewrite:trace3

# Çok detaylı log için
LogLevel alert rewrite:trace8

Bu ayarla error log’da her rewrite adımını görebilirsiniz:

[rewrite:trace2] [pid 1234] mod_rewrite.c(477): [perdir /var/www/html/]
init rewrite engine with requested uri /old-page
[rewrite:trace3] [pid 1234] mod_rewrite.c(477): [perdir /var/www/html/]
applying pattern '^old-page$' to uri 'old-page'

Sorun 8: Önbellekleme Direktiflerinin Çakışması

Cache başlıkları yanlış yapılandırıldığında hem kullanıcılar eski içerik görür hem de 500 hataları alabilirsiniz.

# mod_expires ile önbellekleme
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresDefault "access plus 1 month"
    
    ExpiresByType text/html "access plus 1 hour"
    ExpiresByType text/css "access plus 1 week"
    ExpiresByType application/javascript "access plus 1 week"
    ExpiresByType image/jpeg "access plus 1 month"
    ExpiresByType image/png "access plus 1 month"
    ExpiresByType image/gif "access plus 1 month"
    ExpiresByType image/webp "access plus 1 month"
</IfModule>

mod_expires yüklü değilse bloğu sayesinde hata almayacaksınız. Ama mod_headers ile yapıldığında dikkatli olun:

# Bu çakışma yaratabilir - mod_headers ve mod_expires birlikte
<IfModule mod_headers.c>
    # Tüm dosyalar için cache header'ı set etmeyin!
    # Header set Cache-Control "max-age=2592000, public"  <- YANLIS
    
    # Bunun yerine belirli dosya tipleri için yapın
    <FilesMatch ".(css|js|png|jpg|gif|ico|webp)$">
        Header set Cache-Control "max-age=604800, public"
    </FilesMatch>
    
    # HTML dosyaları için farklı politika
    <FilesMatch ".html?$">
        Header set Cache-Control "max-age=3600, must-revalidate"
    </FilesMatch>
</IfModule>

Hızlı Teşhis: .htaccess Sorununu Izole Etme

Sorunun gerçekten .htaccess’ten kaynaklandığından emin olmak için şu adımları takip edin:

# 1. Adım: .htaccess'i geçici olarak devre dışı bırak
sudo mv /var/www/html/.htaccess /var/www/html/.htaccess.bak

# Sorun çözüldü mü? Evet ise problem .htaccess'te
# 2. Adım: Kuralları tek tek aktif et
sudo nano /var/www/html/.htaccess

# 3. Adım: Her değişiklikten sonra test et
curl -I https://example.com/test-page

# Redirect header'larını görmek için
curl -L -I https://example.com/old-page

# 4. Adım: Bitince yedeği geri yükle ya da temiz versiyonu koru

HTTP response header’larını incelemek sorunun nerede olduğunu anlamamızı kolaylaştırır:

# Detaylı header analizi
curl -v https://example.com/page 2>&1 | grep -E "^[<>]"

# Sadece response code'u görmek için
curl -o /dev/null -s -w "%{http_code}" https://example.com/page

# Tüm redirect zincirini görmek için
curl -L --max-redirs 10 -v https://example.com/old-page 2>&1 | grep "Location:"

Güvenlik Açısından Kritik .htaccess Kuralları

Yönettiğiniz her Apache sunucusunda şu kuralların olduğundan emin olun:

# .htaccess ve .htpasswd dosyalarını gizle
<FilesMatch "^.ht">
    Require all denied
</FilesMatch>

# Hassas dosyaları koru
<FilesMatch "(wp-config.php|xmlrpc.php|php.ini|php5.ini)$">
    Require all denied
</FilesMatch>

# Dizin taramasını kapat
Options -Indexes

# Server imzasını gizle (httpd.conf'ta daha iyi ama .htaccess'ten de yapılabilir)
ServerSignature Off

# Tehlikeli HTTP metodlarını engelle
<LimitExcept GET POST HEAD>
    Require all denied
</LimitExcept>

# Hotlinking engelle
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^https?://(www.)?example.com [NC]
RewriteRule .(jpg|jpeg|png|gif|webp)$ - [F,NC,L]

Sonuç

.htaccess hata ayıklama, büyük ölçüde sistematik yaklaşım gerektirir. “Neden çalışmıyor?” diye saatlerce kafa patlatmak yerine şu sırayı takip edin: önce error log’lara bakın, AllowOverride ayarını kontrol edin, ilgili modülün yüklü olduğunu doğrulayın ve son olarak kuralları tek tek test edin.

En önemli alışkanlık: production sunucusunda .htaccess değiştirmeden önce daima yedeğini alın. Basit bir cp .htaccess .htaccess.bak komutu, gece 2’de panikle uğraşmanızı önler. Bunun yanında değişiklikleri her zaman staging ortamında test etmeyi ihmal etmeyin.

Log seviyeleri ve rewrite trace gibi gelişmiş debug araçlarını production’da açık bırakmak hem performans sorununa hem de güvenlik riskine yol açar. Bu araçları sorun giderme sonrasında mutlaka kapatın.

Son olarak, Apache 2.4 kullananlar için hatırlatma: eski Order deny,allow sözdizimi artık çalışmıyor. Require direktifine geçiş yapmayanlar için bu, sürüm yükseltmelerinde sessiz güvenlik açıkları yaratabilir. Tüm access control kurallarınızı modern sözdizimle yazın.

Benzer Konular

Bir yanıt yazın

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