Apache Virtual Host Yapılandırma Hataları ve Çözümleri
Web sunucusu yönetiminde en sık karşılaşılan sorunların başında Apache virtual host yapılandırma hataları gelir. Bir site aniden 403 döndürmeye başlar, başka bir sitenin içeriği yanlış domain üzerinde görünür ya da SSL sertifikası tamamen farklı bir virtual host’a bağlanır. Bu tür sorunlar production ortamında saatlerce aranabilir. Bu yazıda gerçek senaryolar üzerinden virtual host hatalarını nasıl tespit edip çözeceğimizi adım adım inceleyeceğiz.
Virtual Host Nedir ve Neden Karmaşıklaşır?
Apache’de virtual host, tek bir IP adresi vezerinde birden fazla web sitesi çalıştırmanızı sağlar. Name-based ve IP-based olmak üzere iki tipi vardır. Modern ortamlarda neredeyse her zaman name-based kullanılır çünkü IPv4 adresleri kıymetlidir.
Sorunların büyük çoğunluğu şu üç nedenden kaynaklanır:
- Yanlış ServerName veya ServerAlias tanımı: Domain eşleşmesi yapılamaz
- Dosya izinleri ve SELinux/AppArmor: Apache dizini okuyamaz
- Virtual host sıralaması: İlk tanımlanan virtual host beklenmedik istekleri yakalar
Apache’nin virtual host seçim mantığını anlamadan sorunları çözmek çok zorlaşır. Bir istek geldiğinde Apache şu sırayla karar verir: önce IP ve port eşleşmesi, sonra ServerName ve ServerAlias eşleşmesi. Eğer hiçbir ServerName eşleşmezse ilk tanımlanan virtual host devreye girer. Bu davranış pek çok kafa karışıklığının ana sebebidir.
Hata Ayıklamaya Başlamadan Önce: Temel Araçlar
Herhangi bir virtual host sorununa bakmadan önce kullanmanız gereken birkaç temel komut vardır.
# Tüm virtual host'ları listele ve hangi config dosyasından geldiğini göster
apache2ctl -S
# veya CentOS/RHEL için
httpd -S
# Config dosyasının syntax hatası içerip içermediğini kontrol et
apache2ctl configtest
# veya
httpd -t
# Aktif olarak yüklenen tüm modülleri listele
apache2ctl -M
# Apache'nin hangi user ile çalıştığını öğren
ps aux | grep apache
ps aux | grep httpd
apache2ctl -S çıktısı altın değerindedir. Örnek çıktı şöyle görünür:
VirtualHost configuration:
*:443 is a NameVirtualHost
default server ornek.com (/etc/apache2/sites-enabled/ornek.com.conf:1)
port 443 namevhost ornek.com (/etc/apache2/sites-enabled/ornek.com.conf:1)
port 443 namevhost www.ornek.com (/etc/apache2/sites-enabled/ornek.com.conf:2)
*:80 is a NameVirtualHost
default server ornek.com (/etc/apache2/sites-enabled/000-default.conf:1)
Bu çıktıdan hangi virtual host’un default olarak seçildiğini, hangi portların dinlendiğini ve config dosyalarının tam yolunu görebilirsiniz.
Senaryo 1: Yanlış Siteye Yönlendirme
En klasik senaryo şudur: Yeni bir site eklediniz, ama yenisite.com‘a girince eski sitenin içeriği görünüyor.
# Önce DNS çözümlemesinin doğru olduğunu doğrula
dig yenisite.com +short
nslookup yenisite.com
# curl ile hangi virtual host'un cevap verdiğini test et
curl -v -H "Host: yenisite.com" http://sunucu-ip-adresi/
# Apache'nin virtual host sıralamasını kontrol et
apache2ctl -S | grep -A5 "port 80"
Büyük ihtimalle sorun şuradadır: yenisite.com.conf dosyası /etc/apache2/sites-enabled/ altında mevcut değildir ya da a2ensite komutu çalıştırılmamıştır.
# Mevcut durumu kontrol et
ls -la /etc/apache2/sites-available/
ls -la /etc/apache2/sites-enabled/
# Siteyi etkinleştir (Debian/Ubuntu)
a2ensite yenisite.com.conf
systemctl reload apache2
# CentOS/RHEL'de conf dosyasının doğru dizinde olduğundan emin ol
ls -la /etc/httpd/conf.d/
Eğer site enabled ama hala yanlış içerik görünüyorsa, config dosyasını inceleyin:
# YANLIŞ - ServerName eksik veya hatalı
<VirtualHost *:80>
DocumentRoot /var/www/yenisite
# ServerName satırı yok!
</VirtualHost>
# DOGRU
<VirtualHost *:80>
ServerName yenisite.com
ServerAlias www.yenisite.com
DocumentRoot /var/www/yenisite
ErrorLog ${APACHE_LOG_DIR}/yenisite-error.log
CustomLog ${APACHE_LOG_DIR}/yenisite-access.log combined
</VirtualHost>
Senaryo 2: 403 Forbidden Hatası
403 hatası genellikle iki nedenden kaynaklanır: dosya izinleri veya Apache Require direktifi. Hata logunu ilk kontrol noktanız yapın.
# Hata logunu gerçek zamanlı izle
tail -f /var/log/apache2/error.log
# veya site özelinde log varsa
tail -f /var/log/apache2/yenisite-error.log
# Tipik 403 log satırı şöyle görünür:
# [authz_core:error] AH01630: client denied by server configuration: /var/www/yenisite
Dizin izinlerini kontrol etmek için:
# DocumentRoot'un tüm üst dizinlerini kontrol et
# Apache, /var/www/yenisite'ye ulaşmak için tüm üst dizinlerde x (execute) yetkisi ister
namei -l /var/www/yenisite/index.html
# Tipik çıktı:
# f: /var/www/yenisite/index.html
# drwxr-xr-x root root /
# drwxr-xr-x root root var
# drwxr-xr-x root root www
# drwxr-x--- www-data www-data yenisite <-- burada sorun var, other için x yok
# -rw-r--r-- www-data www-data index.html
# İzinleri düzelt
chmod 755 /var/www/yenisite
# veya özyinelemeli olarak
find /var/www/yenisite -type d -exec chmod 755 {} ;
find /var/www/yenisite -type f -exec chmod 644 {} ;
Apache 2.4’te Require direktifi değişti. Eski Allow/Deny syntax’ı artık çalışmaz:
# ESKI SYNTAX (Apache 2.2) - 2.4'te 403 verir
<Directory /var/www/yenisite>
Order allow,deny
Allow from all
</Directory>
# YENI SYNTAX (Apache 2.4)
<Directory /var/www/yenisite>
Require all granted
</Directory>
SELinux aktifse (özellikle CentOS/RHEL sistemlerde) dosya etiketleri de sorun çıkarır:
# SELinux durumunu kontrol et
getenforce
sestatus
# Apache ile ilgili SELinux hatalarını görüntüle
ausearch -m avc -ts recent | grep httpd
sealert -a /var/log/audit/audit.log
# Dosyaların doğru SELinux etiketine sahip olduğunu kontrol et
ls -Z /var/www/yenisite/
# Etiketleri düzelt
chcon -R -t httpd_sys_content_t /var/www/yenisite/
# veya kalıcı çözüm için
semanage fcontext -a -t httpd_sys_content_t "/var/www/yenisite(/.*)?"
restorecon -Rv /var/www/yenisite/
Senaryo 3: SSL Virtual Host Sorunları
SSL yapılandırmalarında en yaygın sorun, birden fazla SSL virtual host tanımlanırken SNI (Server Name Indication) göz ardı edilmesidir. Eski tarayıcılar SNI desteklemez ama modern ortamda bu genellikle sorun değildir.
# SSL virtual host'ların doğru yapılandırıldığını test et
openssl s_client -connect yenisite.com:443 -servername yenisite.com 2>/dev/null | grep -E "subject|issuer"
# Hangi sertifikanın sunulduğunu kontrol et
openssl s_client -connect yenisite.com:443 </dev/null 2>/dev/null | openssl x509 -noout -subject -dates
# curl ile SSL virtual host'u test et
curl -vI https://yenisite.com 2>&1 | grep -E "SSL|subject|expire"
Tipik bir SSL virtual host yapılandırma hatası:
# SORUNLU - Her iki virtual host da aynı sertifikayı kullanıyor gibi görünür
<VirtualHost *:443>
ServerName birinci.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/birinci.crt
SSLCertificateKeyFile /etc/ssl/private/birinci.key
DocumentRoot /var/www/birinci
</VirtualHost>
<VirtualHost *:443>
ServerName ikinci.com
# SSLEngine satırı eksik!
SSLCertificateFile /etc/ssl/certs/ikinci.crt
SSLCertificateKeyFile /etc/ssl/private/ikinci.key
DocumentRoot /var/www/ikinci
</VirtualHost>
# DOGRU yapılandırma
<VirtualHost *:443>
ServerName birinci.com
ServerAlias www.birinci.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/birinci.crt
SSLCertificateKeyFile /etc/ssl/private/birinci.key
SSLCertificateChainFile /etc/ssl/certs/birinci-chain.crt
DocumentRoot /var/www/birinci
ErrorLog ${APACHE_LOG_DIR}/birinci-ssl-error.log
</VirtualHost>
<VirtualHost *:443>
ServerName ikinci.com
ServerAlias www.ikinci.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ikinci.crt
SSLCertificateKeyFile /etc/ssl/private/ikinci.key
SSLCertificateChainFile /etc/ssl/certs/ikinci-chain.crt
DocumentRoot /var/www/ikinci
ErrorLog ${APACHE_LOG_DIR}/ikinci-ssl-error.log
</VirtualHost>
Senaryo 4: Port Çakışması ve Dinleme Sorunları
Apache bazen başlamaz veya belirli portlarda yanıt vermez. Bu durumda port çakışması yaşanıyor olabilir.
# 80 ve 443 portlarını hangi servisin kullandığını öğren
ss -tlnp | grep -E ':80|:443'
# veya eski sistemlerde
netstat -tlnp | grep -E ':80|:443'
# Apache'nin hangi portları dinlediğini kontrol et
grep -r "Listen" /etc/apache2/ports.conf /etc/apache2/sites-enabled/
# veya CentOS için
grep -r "Listen" /etc/httpd/conf/httpd.conf /etc/httpd/conf.d/
# Örnek çıktı - sorunlu durum:
# LISTEN 0 128 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1234))
# nginx port 80'i zaten tutuyor, Apache başlayamıyor
ports.conf dosyasını kontrol edin ve virtual host portlarıyla uyumlu olduğundan emin olun:
# /etc/apache2/ports.conf içeriği böyle olmalı
cat /etc/apache2/ports.conf
# Listen direktiflerini kontrol et
# Eğer bir virtual host *:8080 kullanıyorsa ports.conf'ta Listen 8080 olmalı
Log Analizi ile Sorun Tespiti
Apache logları doğru okunduğunda sorunun kaynağını çok hızlı bulabilirsiniz. Log formatını anlamak kritik önem taşır.
# Error log'da en sık tekrarlayan hataları bul
grep "error" /var/log/apache2/error.log | awk '{print $NF}' | sort | uniq -c | sort -rn | head -20
# Belirli bir virtual host'a ait 500 hatalarını filtrele
grep "yenisite.com" /var/log/apache2/access.log | awk '$9 == "500"' | tail -50
# Son 1 saatteki hataları göster
awk -v date="$(date -d '1 hour ago' '+%d/%b/%Y:%H:%M:%S')" '$0 > date' /var/log/apache2/error.log
# Virtual host başına istek sayısını hesapla
awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn
# Hangi virtual host en fazla 404 üretiyor
grep " 404 " /var/log/apache2/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
Log Level ayarı da önemlidir. Production’da warn seviyesi genellikle yeterlidir ama sorun ayıklarken debug seviyesine almak çok daha fazla bilgi sunar:
# Virtual host içinde log seviyesini artır (sadece o site için)
<VirtualHost *:80>
ServerName sorunlu-site.com
DocumentRoot /var/www/sorunlu-site
LogLevel debug
ErrorLog ${APACHE_LOG_DIR}/sorunlu-site-debug.log
</VirtualHost>
# Config'i yeniden yükle ve debug logunu izle
systemctl reload apache2
tail -f /var/log/apache2/sorunlu-site-debug.log
Senaryo 5: .htaccess Dosyası Çalışmıyor
Bu çok yaygın bir sorundur. WordPress veya başka bir CMS kuruyorsunuz, URL yeniden yazma çalışmıyor ve 404 hatası alıyorsunuz.
# mod_rewrite modülünün yüklü olduğunu doğrula
apache2ctl -M | grep rewrite
# Çıktı: rewrite_module (shared) görünmüyorsa:
a2enmod rewrite
systemctl restart apache2
Virtual host yapılandırmasında AllowOverride direktifi None olarak ayarlıysa .htaccess tamamen devre dışıdır:
# YANLIS - .htaccess tamamen devre disi
<VirtualHost *:80>
ServerName wordpress-site.com
DocumentRoot /var/www/wordpress
<Directory /var/www/wordpress>
Options Indexes FollowSymLinks
AllowOverride None # <-- Bu satır .htaccess'i devre disi birakiyor
Require all granted
</Directory>
</VirtualHost>
# DOGRU - .htaccess aktif
<VirtualHost *:80>
ServerName wordpress-site.com
DocumentRoot /var/www/wordpress
<Directory /var/www/wordpress>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
Yapılandırma Değişikliklerini Test Etme
Production sunucusunda değişiklik yapmadan önce test etme alışkanlığı edinmek sizi büyük sorunlardan kurtarır.
# Config syntax kontrolü - en temel adım
apache2ctl configtest
# Çıktı: Syntax OK olmalı
# Değişiklikleri restart gerektirmeden uygula
# (yeni bağlantılar için geçerli, mevcut bağlantılar etkilenmez)
systemctl reload apache2
# Belirli bir config dosyasını ayrı ayrı test et
apache2ctl -f /etc/apache2/sites-available/yenisite.com.conf -t
# Virtual host yapılandırmasının nasıl göründüğünü kontrol et
apache2ctl -D DUMP_VHOSTS
# veya
apache2ctl -S
# Değişiklikten önce mevcut yapılandırmayı yedekle
cp /etc/apache2/sites-available/ornek.com.conf /etc/apache2/sites-available/ornek.com.conf.bak.$(date +%Y%m%d)
Sık Yapılan Hatalar ve Hızlı Kontrol Listesi
Bir virtual host sorunu yaşadığınızda şu adımları sırayla uygulayın:
- apache2ctl -S çalıştırın: Virtual host sıralamasını ve hangi config dosyasından geldiğini görün
- apache2ctl configtest çalıştırın: Syntax hatalarını elemek için
- Error log’u kontrol edin:
tail -f /var/log/apache2/error.log - DocumentRoot’un varlığını doğrulayın: Config’de yazılan dizin gerçekten mevcut mu
- Dosya izinlerini kontrol edin:
namei -l /var/www/siteniz/komutuyla tüm yolu gözden geçirin - ServerName’in doğruluğunu test edin:
curl -H "Host: siteniz.com" http://sunucu-ip/ - DNS çözümlemesini doğrulayın:
dig siteniz.comkomutuyla IP’nin doğru olduğunu teyit edin - Port dinlemesini kontrol edin:
ss -tlnp | grep apache - SELinux/AppArmor durumunu değerlendirin: CentOS/RHEL sistemlerde sık atlanan bir adım
- mod_rewrite aktif mi:
.htaccesssorunlarında ilk bakılacak yer
Önleyici Tedbirler
Sorunları başlamadan engellemek her zaman çözmekten daha verimlidir.
# Tüm virtual host config dosyalarını tek seferde kontrol eden basit bir script
for conf in /etc/apache2/sites-enabled/*.conf; do
echo "=== $conf ==="
apache2ctl -f "$conf" -t 2>&1
done
# Cron job ile periyodik config kontrolü
# /etc/cron.d/apache-config-check dosyasına ekle:
# 0 * * * * root apache2ctl configtest >> /var/log/apache-config-check.log 2>&1
# Virtual host log dosyalarının döngüsünü sağla
# /etc/logrotate.d/apache2 dosyasının mevcut olduğundan emin ol
cat /etc/logrotate.d/apache2
Yeni bir virtual host oluştururken kullanabileceğiniz şablon yapısı:
<VirtualHost *:80>
ServerName ornek.com
ServerAlias www.ornek.com
DocumentRoot /var/www/ornek.com/public_html
<Directory /var/www/ornek.com/public_html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/ornek.com-error.log
CustomLog ${APACHE_LOG_DIR}/ornek.com-access.log combined
# HTTP'den HTTPS'e yönlendir
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>
<VirtualHost *:443>
ServerName ornek.com
ServerAlias www.ornek.com
DocumentRoot /var/www/ornek.com/public_html
<Directory /var/www/ornek.com/public_html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/ornek.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/ornek.com/privkey.pem
ErrorLog ${APACHE_LOG_DIR}/ornek.com-ssl-error.log
CustomLog ${APACHE_LOG_DIR}/ornek.com-ssl-access.log combined
</VirtualHost>
Sonuç
Virtual host yapılandırma sorunları karmaşık görünse de sistematik bir yaklaşımla her zaman çözülebilir. En kritik nokta şudur: önce apache2ctl -S ve apache2ctl configtest çalıştırın, sonra error log’u inceleyin. Bu üç adım sorunların yüzde seksenini açıklığa kavuşturur.
Apache’nin davranışını doğru anlamak da en az araçları bilmek kadar önemlidir. Özellikle “ilk eşleşen virtual host kazanır” mantığını ve AllowOverride direktifinin .htaccess üzerindeki etkisini kavradıktan sonra çoğu sorunun neden oluştuğunu anlık olarak görebilirsiniz.
Son olarak, her değişiklikten önce yedek almanızı, config test etmeden restart veya reload yapmamanızı ve log seviyesini sorun ayıklama sırasında geçici olarak artırmanızı şiddetle öneririm. Bu alışkanlıklar sizi hem gece yarısı çağrılarından hem de gereksiz downtime’lardan korur.
