Giriş
Bir sunucuda 30 WordPress sitesi barındırdığınızı düşünün. Her sitenin kendi vhost dosyası var. Bir güvenlik açığı buldunuz: wp-content/uploads altında PHP çalıştırılabiliyor. Çözüm “kuralı ekle” gibi görünür ama asıl sorun, kuralı her vhost’a uygulamaya devam etmenin imkânsızlığıdır. Bu yazıda, nginx’te çoklu vhost güvenliğinin (config drift) gerçek tehlikesini ve otomatikleştirme yaklaşımını anlatacağım.
1. Regex location sıralaması: İlk eşleşen kazanır
nginx’te iki tip location vardır: prefix (önek) ve regex (~). Önemli kural: regex location’lar, en uzun prefix’ten sonra işlenir ve ilk eşleşen regex kazanır. Bu, sıralamanın güvenlik için kritik olduğu anlamına gelir.
server {
# PHP çalıştırma — son eşleşen regex değil, ilk eşleşen kazanır
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
# UPLOADS İÇİNDE PHP'Yİ ENGELLE
# Bu regex, yukarıdakinden DAHA ÖNCE SIRALANMALI
location ~* ^/wp-content/(uploads|cache|backup|plugins-|themes-)/.*\.php$ {
deny all;
}
}
Dikkat: location ~* (büyük-küçük harf duyarsız) yukarıda ~ \.php$‘den önce gelirse, uploads içindeki PHP isteği en kısa süre değil ilk eşleşen regex olduğu için engellenir. Eğer sıralamayı ters yaparsanız PHP location’ı devreye girer ve engel hiç çalışmaz.
2. Symlink ve normalize edilmemiş yolların tuzağı
# Bu, yalnızca literal yolları yakalar; ../ veya /uploads.php gibi
# normalleştirilmemiş istekler ya da dosya dışı uzantılar (ör. .phtml)
# aynı engeli gerektirir. Kapsamlı bir liste gerekir.
location ~* ^/wp-content/(.+\.(php|phtml|php3|php4|php5|php7))$ {
deny all;
}
3. Ana sorun: Config drift
30 vhost’un her birine bu kuralı elle kopyalamak, bir süre sonra bazı sitelerde kuralın unutulmasına yol açar. “Yeni site eklendi, kural yok.” Bu duruma config drift denir: zaman içinde yapılandırmanın aşınması.
Çözüm, kuralı include ile tek noktadan yönetmek ve otomatik diff-denetimi yapmaktır:
# /etc/nginx/wp-security.inc — tüm vhost'lardan include edilir
location ~* ^/wp-content/(uploads|cache|backup|plugins-|themes-)/.*\.php$ {
deny all;
}
# vhost içinde
server {
include /etc/nginx/wp-security.inc;
}
4. Otomatik denetim scripti
Kuralların her vhost’ta olduğunu doğrulayan küçük bir audit scripti:
#!/bin/bash
# /usr/local/bin/nginx-audit.sh
MISSING=0
for conf in /etc/nginx/sites-available/*.conf; do
if ! grep -q "wp-security.inc" "$conf" 2>/dev/null; then
echo "EKSIK: $conf -> wp-security.inc include edilmemis"
MISSING=1
fi
done
[ "$MISSING" -eq 0 ] && echo "OK: tum vhost'lar kurali iceriyor"
5. Boş modül ekleme ve `nginx -t` güvencesi
Değişiklikleri uygularken asla reload’u atlamayın; önce syntax testi yapın:
sudo nginx -t && sudo systemctl reload nginx
6. Cevap: Minimal, modüler, centerli
- Tek gerçek kaynak: Güvenlik kurallarını tek
.incdosyasında tutun. - Her yerden include: Yeni vhost bile kuralları devralır.
- Otomatik audit: Drift’i CI’da veya cron ile yakalayın.
- Regex sırasına dikkat: Engelleme rule regex’lerini PHP run rule’den önce tutun.
Paylaşımlı hosting’de tek bir aşınmış kural, tüm güvenlik duvarını sıfırlar. Bu yüzden moderinlik ve otomasyon bir lüks değil, gerekliliktir.