Nginx ile WordPress 404 Hatası: Rewrite Sorunları ve Çözümleri

WordPress kurulumundan sonra her şey güzel görünüyor, admin paneline giriyorsun, birkaç yazı yayımlıyorsun ve ardından fark ediyorsun: tek sayfa adreslerine tıkladığında 404 hatası alıyorsun. Ana sayfa çalışıyor, ama /2024/01/merhaba-dunya/ gibi bir adrese gittiğinde Nginx sana dümdüz “404 Not Found” döndürüyor. Bu, Nginx üzerinde WordPress kuran hemen herkesin karşılaştığı klasik bir rewrite sorunudur ve çözümü düşündüğünden çok daha basit, ama neden olduğunu anlamadan uyguladığın çözüm bir süre sonra başka sorunlara yol açabilir.

Sorunun Kökü: Nginx ve Apache Farkı

Apache kullanırken WordPress, .htaccess dosyasına birkaç satır yazarak mod_rewrite üzerinden tüm istekleri index.php‘ye yönlendiriyor. Nginx’te .htaccess diye bir kavram yok. Nginx konfigürasyon dosyalarını çalışma zamanında okumaz, her şey ana konfigürasyonda tanımlanmak zorunda.

WordPress’in kalıcı bağlantı (permalink) sistemi şu şekilde çalışır: Bir kullanıcı /kategori/teknoloji/nginx-sorunlari/ adresine istek atıyor. Bu fiziksel olarak sunucuda var olan bir dosya ya da dizin değil. WordPress, bu isteği index.php?category_name=teknoloji&p=... gibi bir sorgu parametresine çeviriyor. Bunu yapabilmesi için Nginx’in, fiziksel olarak var olmayan her isteği index.php‘ye iletmesi gerekiyor.

Nginx bu yönlendirmeyi yapmıyorsa ne olur? Nginx dosyayı arar, bulamaz, 404 döndürür. İşte sorun tam olarak bu.

Nginx Log’larını Okumak

Bir sorunu çözmeden önce log’lara bakmak şart. Nginx’in error log’u genellikle şu konumdadır:

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

# CentOS/RHEL sistemlerde
tail -f /var/log/nginx/error.log

# Site bazlı log tanımlandıysa
tail -f /var/log/nginx/wordpress.error.log

404 hatası aldığında log’da tipik olarak şunu görürsün:

2024/01/15 14:23:11 [error] 1234#1234: *89 open() "/var/www/html/kategori/teknoloji" failed (2: No such file or directory), client: 192.168.1.100, server: example.com, request: "GET /kategori/teknoloji HTTP/1.1", host: "example.com"

Bu log satırı sana çok şey söylüyor: Nginx /var/www/html/kategori/teknoloji adresinde fiziksel bir dosya veya dizin arıyor ve bulamıyor. try_files direktifi ya hiç tanımlanmamış ya da yanlış tanımlanmış demektir.

Access log’unu da kontrol etmek faydalı olabilir:

tail -f /var/log/nginx/access.log | grep " 404 "

Temel Çözüm: try_files Direktifi

WordPress için doğru Nginx konfigürasyonunun kalbi try_files direktifidir. Şöyle çalışır: Nginx önce isteğin fiziksel bir dosyaya karşılık gelip gelmediğini kontrol eder, sonra dizin olup olmadığına bakar, ikisi de yoksa isteği index.php‘ye iletir.

Minimum çalışan bir WordPress konfigürasyonu şu şekilde görünmeli:

server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/html/wordpress;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ .php$ {
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Buradaki kritik satır: try_files $uri $uri/ /index.php?$args;

Bu direktif şunu söylüyor:

  • $uri: İstenen URL’yi fiziksel dosya olarak ara
  • $uri/: İstenen URL’yi fiziksel dizin olarak ara
  • /index.php?$args: İkisi de yoksa isteği index.php‘ye mevcut query string parametreleriyle birlikte ilet

$args yerine $query_string da kullanabilirsin, ikisi de aynı şeyi ifade eder.

Sık Yapılan Hatalar ve Gerçek Dünya Senaryoları

Senaryo 1: try_files Yanlış Yazılmış

Birçok konfigürasyon kopyala-yapıştır yoluyla gelir ve bazen şu hatalı versiyonla karşılaşırsın:

# YANLIŞ - Bu çalışmaz
location / {
    try_files $uri $uri/ /index.php;
}

Bu versiyonda $args eksik. Sonuç olarak WordPress /index.php‘ye ulaşıyor ama hangi sayfanın gösterileceğine dair hiçbir parametre gelmiyor. WordPress ana sayfayı gösteriyor gibi davranabilir ya da garip bir şekilde yine 404 üretiyor. Doğrusu:

# DOGRU
location / {
    try_files $uri $uri/ /index.php?$args;
}

Senaryo 2: PHP-FPM Socket Yanlış

PHP bloğunu konfigüre ederken socket yolunu yanlış yazmak çok yaygın bir hata. WordPress admin sayfaları da dahil her PHP isteği 502 Bad Gateway veya beyaz ekran döndürüyor:

# Sistemde hangi PHP-FPM socket'lerinin aktif olduğunu bul
find /run/php/ -name "*.sock" 2>/dev/null
ls -la /var/run/php-fpm/ 2>/dev/null

# systemd ile çalışan servisler için
systemctl list-units | grep php

Ardından bulduğun socket yolunu konfigürasyona yaz:

location ~ .php$ {
    # PHP 8.1 için
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
    
    # Veya TCP ile
    # fastcgi_pass 127.0.0.1:9000;
    
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
    fastcgi_read_timeout 300;
}

Senaryo 3: root Direktifi Yanlış Konumda

Bu sorun özellikle birden fazla location bloğu olan konfigürasyonlarda ortaya çıkar:

# SORUNLU YAPILANDIRMA
server {
    listen 80;
    server_name example.com;
    
    location / {
        root /var/www/html/wordpress;
        try_files $uri $uri/ /index.php?$args;
    }
    
    location ~ .php$ {
        # root burada tanımlı degil!
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

PHP location bloğunda $document_root boş gelir çünkü root direktifi sadece location / bloğunda tanımlanmış. Doğru kullanım:

server {
    listen 80;
    server_name example.com;
    root /var/www/html/wordpress;  # Server blogu seviyesinde tanimla
    index index.php;
    
    location / {
        try_files $uri $uri/ /index.php?$args;
    }
    
    location ~ .php$ {
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Senaryo 4: WordPress Alt Dizinde Kurulu

WordPress bazen example.com/blog/ gibi bir alt dizine kurulur. Bu durumda konfigürasyon biraz farklı olmalı:

server {
    listen 80;
    server_name example.com;
    root /var/www/html;

    # WordPress alt dizin kurulumu icin
    location /blog/ {
        try_files $uri $uri/ /blog/index.php?$args;
    }

    location ~ .php$ {
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Ayrıca WordPress admin panelinde Ayarlar > Genel kısmında hem “WordPress Adresi” hem de “Site Adresi” doğru set edilmeli.

Senaryo 5: Multisite Kurulumu

WordPress Multisite, özellikle subdirectory modunda ek kurallar gerektiriyor:

server {
    listen 80;
    server_name example.com *.example.com;
    root /var/www/html/wordpress;
    index index.php;

    # Multisite subdirectory modu
    if (!-e $request_filename) {
        rewrite /wp-admin$ $scheme://$host$uri/ permanent;
        rewrite ^(/[^/]+)?(/wp-.*) $2 last;
        rewrite ^(/[^/]+)?(/.*.php) $2 last;
    }

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ .php$ {
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

Konfigürasyonu Test Etmek

Değişiklik yaptıktan sonra her zaman konfigürasyonu test et, sonra reload et:

# Konfigürasyon sözdizimini kontrol et
nginx -t

# Basarili çıktı şöyle görünmeli
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

# Reload et (tam restart yapmadan)
systemctl reload nginx

# Veya
nginx -s reload

Eğer nginx -t hata döndürürse çıktıyı dikkatlice oku, hangi satırda ve hangi dosyada hata olduğunu söyler.

Debug Modunu Açmak

Sorun hala devam ediyorsa Nginx’in debug log’unu aktif edebilirsin. Ama dikkat: debug log çok hızlı büyür, production’da kullanma.

# nginx.conf ana konfigürasyonda
error_log /var/log/nginx/debug.log debug;

Veya sadece belirli bir IP’nin debug log’unu almak için:

events {
    debug_connection 192.168.1.100;  # Sadece bu IP icin debug
}

Debug log’da şöyle satırlar görürsün:

grep "trying to use file|trying to use dir|http script" /var/log/nginx/debug.log | tail -50

Bu çıktı, try_files direktifinin hangi adımda ne yaptığını adım adım gösterir.

WordPress Permalink Ayarlarını Kontrol Et

Nginx tarafında her şey doğru olsa bile WordPress admin panelinde permalink ayarlarını bir kez kaydetmek sorunları çözebilir. Bu, WordPress’in kendi rewrite kurallarını yeniden oluşturmasını sağlar:

Ayarlar > Kalıcı Bağlantılar sayfasına git, herhangi bir değişiklik yapmadan Değişiklikleri Kaydet butonuna tıkla.

Ama Nginx’te .htaccess işlev görmediğinden bu adım tek başına yetmez, Nginx konfigürasyonu doğru olmalı.

Güvenlik ile Birlikte Tam Konfigürasyon

Gerçek dünyada production ortamı için minimum güvenlik önlemleriyle birlikte tam bir konfigürasyon şöyle görünmeli:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;
    root /var/www/html/wordpress;
    index index.php;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Güvenlik headerleri
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;

    # Hassas dosyalara erisimi engelle
    location ~ /. {
        deny all;
    }

    location ~* /(?:uploads|files)/.*.php$ {
        deny all;
    }

    # Statik dosyalar icin cache
    location ~* .(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        expires 30d;
        add_header Cache-Control "public, no-transform";
        try_files $uri =404;
    }

    # Ana rewrite kurali
    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    # wp-admin icin özel ayar (gerekirse IP kisitlama eklenebilir)
    location /wp-admin/ {
        try_files $uri $uri/ /wp-admin/index.php?$args;
    }

    # PHP isleme
    location ~ .php$ {
        # Guvenlik: sadece var olan PHP dosyalarini isle
        try_files $uri =404;
        
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
        fastcgi_read_timeout 300;
    }
}

Sık Sorulan: “Her Şeyi Doğru Yaptım Ama Hala 404”

Bu noktada birkaç şeyi daha kontrol etmek gerekiyor:

Dosya izinleri:

# WordPress dizin ve dosya izinlerini kontrol et
ls -la /var/www/html/wordpress/
stat /var/www/html/wordpress/index.php

# Nginx hangi kullaniciyla calistiyor?
ps aux | grep nginx

# Nginx kullanicisina (genellikle www-data) erisim izni ver
chown -R www-data:www-data /var/www/html/wordpress/
find /var/www/html/wordpress -type d -exec chmod 755 {} ;
find /var/www/html/wordpress -type f -exec chmod 644 {} ;

Birden fazla server block varsa öncelik sırası:

# Aktif konfigürasyonlari listele
ls -la /etc/nginx/sites-enabled/

# nginx -T ile tüm konfigürasyonu derlenmiş haliyle gör
nginx -T | grep -A 20 "server_name example.com"

Eğer birden fazla server block aynı domain’i yakalıyorsa Nginx hangisinin aktif olduğunu default_server direktifine göre belirler ve beklenmedik bir konfigürasyon devreye girmiş olabilir.

PHP-FPM’in çalışıp çalışmadığı:

systemctl status php8.1-fpm
# Veya
ps aux | grep php-fpm

# Socket'in varlığını dogrula
ls -la /run/php/php8.1-fpm.sock

Hızlı Tanı Komutu

Sorun yaşadığında şu komut dizisini çalıştırarak hızlıca durum tespiti yapabilirsin:

echo "=== Nginx Status ===" && systemctl is-active nginx
echo "=== PHP-FPM Status ===" && systemctl is-active php8.1-fpm 2>/dev/null || systemctl is-active php-fpm 2>/dev/null
echo "=== Nginx Config Test ===" && nginx -t
echo "=== Son 20 Error Log ===" && tail -20 /var/log/nginx/error.log
echo "=== WordPress Index ===" && ls -la /var/www/html/wordpress/index.php 2>/dev/null || echo "index.php bulunamadi"
echo "=== Socket ===" && ls /run/php/*.sock 2>/dev/null || echo "Socket yok"

Bu çıktıyı bir bakışta okuyunca sorunun nereden kaynaklandığını genellikle hemen anlarsın.

Sonuç

Nginx üzerinde WordPress 404 sorununun büyük çoğunluğu tek bir eksik konfigürasyondan kaynaklanıyor: try_files $uri $uri/ /index.php?$args; satırı. Bu satır yoksa ya da yanlış yazılmışsa, Nginx fiziksel dosyayı bulamadığı her istekte 404 döndürüyor.

Çözüm adımlarını özetlemek gerekirse: önce log’lara bak ve hatanın gerçekten bir rewrite sorunu mu yoksa başka bir şey mi olduğunu doğrula, ardından try_files direktifinin doğru yazıldığından emin ol, root direktifinin server bloğu seviyesinde tanımlandığını kontrol et, PHP-FPM socket yolunun doğru olduğunu doğrula, konfigürasyonu nginx -t ile test et ve systemctl reload nginx ile uygula.

Production ortamında bu değişiklikleri yaparken her zaman mevcut konfigürasyonun yedeğini al. Küçük bir yazım hatası sitenin tamamen erişilemez hale gelmesine neden olabilir ve gece yarısı log dosyasına bakarken hangi satırı değiştirdiğini hatırlamak zorunda kalmazsın.

Bir yanıt yazın

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