Nginx ile PHP-FPM Entegrasyon Sorunları ve Çözümleri

Nginx ile PHP-FPM entegrasyonu kurduğunuzda her şey mükemmel görünebilir, ama ilk gerçek trafik geldiğinde ya da uygulamayı production’a taşıdığınızda işler sarpa sarabilir. “502 Bad Gateway” hataları, beyaz ekranlar, yavaş yanıt süreleri… Bunların hepsi aslında birkaç temel sorunun belirtisi. Bu yazıda en sık karşılaşılan Nginx-PHP-FPM entegrasyon sorunlarını, nasıl teşhis edeceğinizi ve nasıl çözeceğinizi anlatacağım.

Temel Mimariyi Anlamak

Sorunları çözmeden önce bu ikili arasındaki iletişimin nasıl çalıştığını net kavramak lazım. Nginx gelen HTTP isteklerini alır, statik dosyaları kendisi servis eder, PHP dosyalarını ise FastCGI protokolü üzerinden PHP-FPM’e iletir. PHP-FPM bu isteği işler ve sonucu Nginx’e döndürür, Nginx de istemciye gönderir.

İletişim iki şekilde gerçekleşebilir:

  • Unix socket: /run/php/php8.1-fpm.sock gibi bir dosya üzerinden, aynı sunucudaysa daha hızlı
  • TCP socket: 127.0.0.1:9000 üzerinden, farklı sunucularda veya konteyner senaryolarında kullanılır

Bu ayrımı aklınızda tutun çünkü sorun giderirken hangi yöntemi kullandığınız önemli.

En Sık Karşılaşılan Sorun: 502 Bad Gateway

Bu hata Nginx’in PHP-FPM’e ulaşamadığı anlamına gelir. İlk yapmanız gereken şey logları kontrol etmek.

# Nginx hata logunu izle
tail -f /var/log/nginx/error.log

# PHP-FPM logunu izle
tail -f /var/log/php8.1-fpm.log

# Ya da systemd journal üzerinden
journalctl -u php8.1-fpm -f
journalctl -u nginx -f

Logda şöyle bir şey görüyorsanız:

connect() to unix:/run/php/php8.1-fpm.sock failed (2: No such file or directory)

Bu durumda socket dosyası yok demektir. PHP-FPM servisi çalışmıyor olabilir ya da socket yolu yanlış yapılandırılmış olabilir.

# PHP-FPM servis durumunu kontrol et
systemctl status php8.1-fpm

# Servis çalışmıyorsa başlat
systemctl start php8.1-fpm

# Socket dosyasının varlığını kontrol et
ls -la /run/php/

# PHP-FPM pool konfigürasyonundaki socket yolunu kontrol et
grep -r "listen" /etc/php/8.1/fpm/pool.d/

Eğer servis çalışıyor ama socket bulunamıyorsa, Nginx konfigürasyonundaki fastcgi_pass direktifi ile PHP-FPM pool konfigürasyonundaki listen değeri uyuşmuyor olabilir.

Nginx konfigürasyonunuz şöyle görünüyorsa:

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

PHP-FPM pool dosyanızda (/etc/php/8.1/fpm/pool.d/www.conf) şu değerin eşleşmesi gerekir:

listen = /run/php/php8.1-fpm.sock

Bu iki yol birebir aynı olmazsa bağlantı kurulamaz.

İzin Sorunları: Sessiz Katil

502 hatalarının bir diğer yaygın nedeni socket dosyası üzerindeki izin sorunları. Nginx genellikle www-data kullanıcısıyla, PHP-FPM ise farklı bir kullanıcıyla çalışıyor olabilir.

# Socket dosyasının izinlerini kontrol et
ls -la /run/php/php8.1-fpm.sock
# Çıktı örneği: srw-rw---- 1 www-data www-data

# Nginx'in hangi kullanıcıyla çalıştığını öğren
ps aux | grep nginx

# PHP-FPM'in hangi kullanıcıyla çalıştığını öğren
ps aux | grep php-fpm

PHP-FPM pool konfigürasyonunda socket izinlerini açıkça belirtmek en sağlıklı yol:

[www]
user = www-data
group = www-data
listen = /run/php/php8.1-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

Bu değişiklikten sonra mutlaka PHP-FPM’i yeniden başlatın:

systemctl restart php8.1-fpm

SCRIPT_FILENAME Hatası: PHP Çalışmıyor, HTML Dönüyor

Bu sorun biraz sinsi çünkü 502 vermez, PHP kodu çalışmaz ve tarayıcıya ham PHP kodu ya da boş sayfa döner. Bunun sebebi genellikle SCRIPT_FILENAME parametresinin yanlış ayarlanması.

Nginx konfigürasyonunda bu parametre kritik:

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

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

    location ~ .php$ {
        # Bu satır olmadan ya da yanlış olursa PHP çalışmaz
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
    }
}

fastcgi_params dosyasını include ettiğinizde bu parametrenin içinde olmadığını fark edersiniz. /etc/nginx/fastcgi_params dosyasını açıp kontrol edin. Eğer SCRIPT_FILENAME yoksa ya konfigürasyonunuza manuel ekleyin ya da fastcgi.conf dosyasını kullanın:

location ~ .php$ {
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
    fastcgi_index index.php;
    # fastcgi_params yerine fastcgi.conf kullan, SCRIPT_FILENAME içerir
    include fastcgi.conf;
}

Path Info ve PHP Router Sorunları

Laravel, Symfony gibi modern PHP frameworkleri URL yönlendirmesi için index.php üzerinden çalışır. Burada Nginx konfigürasyonu özellikle önemli.

Yanlış yapılandırmada şöyle bir durum olur: /api/users isteği gelir, Nginx bunu PHP’ye iletmeye çalışır ama users diye bir PHP dosyası yoktur ve 404 alırsınız. Doğru konfigürasyon şöyle olmalı:

server {
    listen 80;
    server_name laravel-app.com;
    root /var/www/laravel/public;
    index index.php;

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

    location ~ .php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        include fastcgi.conf;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    
    # Gizli dosyalara erişimi engelle
    location ~ /.(?!well-known).* {
        deny all;
    }
}

Buradaki try_files $uri =404; satırı güvenlik açısından da önemli. Bu olmadan var olmayan PHP dosyaları için de PHP-FPM’e istek gider, bu da potansiyel güvenlik açığı oluşturabilir.

PHP-FPM Pool Tükenmesi ve 504 Gateway Timeout

Siteniz yoğun trafik altında 504 veriyorsa PHP-FPM worker sayısı yetersiz kalıyor olabilir. Bu durumu teşhis edelim:

# PHP-FPM durum sayfasını aktive et (pool konfigürasyonunda)
# /etc/php/8.1/fpm/pool.d/www.conf dosyasına ekle:
# pm.status_path = /fpm-status

# Nginx'e bu path için kural ekle:
# location ~ ^/fpm-status$ {
#     access_log off;
#     allow 127.0.0.1;
#     deny all;
#     fastcgi_pass unix:/run/php/php8.1-fpm.sock;
#     include fastcgi.conf;
# }

# Sonra curl ile kontrol et
curl -s http://127.0.0.1/fpm-status

# Aktif process sayısını izlemek için
watch -n 1 'curl -s http://127.0.0.1/fpm-status'

PHP-FPM pool konfigürasyonunu sunucu kaynaklarına göre ayarlamak gerekir. İşte tipik bir production ayarı:

[www]
user = www-data
group = www-data
listen = /run/php/php8.1-fpm.sock
listen.owner = www-data
listen.group = www-data

; pm tipi: static, dynamic veya ondemand
pm = dynamic

; Maksimum toplam child process sayisi
pm.max_children = 50

; Başlangıçta açılacak process sayisi
pm.start_servers = 10

; Minimum boşta bekleyen process
pm.min_spare_servers = 5

; Maksimum boşta bekleyen process
pm.max_spare_servers = 20

; Her process kaç istek sonra yeniden başlatılsın (memory leak önlemi)
pm.max_requests = 500

; İstek log'u aktif et
access.log = /var/log/php-fpm/www.access.log

; Yavaş istek logu
slowlog = /var/log/php-fpm/www-slow.log
request_slowlog_timeout = 5s

pm.max_children değerini hesaplamak için şu formülü kullanabilirsiniz: Kullanılabilir RAM’i tek bir PHP-FPM process’inin ortalama bellek kullanımına bölün.

# Ortalama PHP-FPM process bellek kullanımını öğren
ps -ylC php-fpm8.1 --sort:rss | awk 'NR>1 { sum+=$8; count++ } END { print "Ortalama:", sum/count/1024, "MB" }'

Nginx Timeout Değerleri

504 hatalarının bir diğer sebebi Nginx’in PHP-FPM’den yanıt bekleme süresi. PHP-FPM konfigürasyonundaki timeout değerleriyle Nginx’inkiler uyumsuz olabilir.

http {
    # Upstream tanımla
    upstream php_fpm {
        server unix:/run/php/php8.1-fpm.sock;
        
        # Keepalive bağlantıları (performans için)
        keepalive 16;
    }

    server {
        location ~ .php$ {
            fastcgi_pass php_fpm;
            include fastcgi.conf;
            
            # FastCGI timeout değerleri
            fastcgi_connect_timeout 60s;
            fastcgi_send_timeout 300s;
            fastcgi_read_timeout 300s;
            
            # Buffer ayarları
            fastcgi_buffer_size 128k;
            fastcgi_buffers 256 16k;
            fastcgi_busy_buffers_size 256k;
            fastcgi_temp_file_write_size 256k;
        }
    }
}

Özellikle ağır işlemler yapan (rapor üretme, dosya işleme gibi) uygulamalarda fastcgi_read_timeout değerini artırmak gerekebilir. Ama bu değeri körü körüne artırmak yerine önce yavaş sorguları optimize edin.

Log Analizi: Sorunun Kaynağını Bulmak

Sistemli bir log analizi birçok sorunu hızla lokalize eder. PHP-FPM’in slowlog özelliği gerçekten değerli:

# Slowlog aktif değilse pool konfigürasyonuna ekle ve restart et
# slowlog = /var/log/php-fpm/www-slow.log  
# request_slowlog_timeout = 5s

# Slowlog'u izle
tail -f /var/log/php-fpm/www-slow.log

# Son 100 satırı getir
tail -n 100 /var/log/php-fpm/www-slow.log

# En çok tetiklenen dosyaları bul
grep "script_filename" /var/log/php-fpm/www-slow.log | 
    awk '{print $NF}' | sort | uniq -c | sort -rn | head -20

Nginx access logunu analiz etmek için:

# 5xx hataları listele
awk '$9 >= 500' /var/log/nginx/access.log | tail -50

# En çok 502 veren URL'leri bul
awk '$9 == 502 {print $7}' /var/log/nginx/access.log | 
    sort | uniq -c | sort -rn | head -20

# Belirli bir zaman dilimindeki hataları filtrele
awk '/14/Jan/2024:15:/ && $9 >= 500' /var/log/nginx/access.log

# Ortalama yanıt sürelerini hesapla (log formatında $request_time varsa)
awk '{sum+=$NF; count++} END {print "Ortalama:", sum/count, "sn"}' /var/log/nginx/access.log

Nginx log formatını düzenleyerek daha fazla bilgi toplayabilirsiniz:

http {
    log_format detailed '$remote_addr - $remote_user [$time_local] '
                        '"$request" $status $body_bytes_sent '
                        '"$http_referer" "$http_user_agent" '
                        'rt=$request_time '
                        'uct="$upstream_connect_time" '
                        'uht="$upstream_header_time" '
                        'urt="$upstream_response_time"';

    access_log /var/log/nginx/access.log detailed;
}

Böylece upstream (PHP-FPM) yanıt süresini doğrudan görebilirsiniz.

OPcache Sorunları

PHP kodu yavaş çalışıyorsa ama PHP-FPM’de belirgin bir sorun yoksa, OPcache konfigürasyonuna bakın. Devre dışı OPcache production’da ciddi performans sorunlarına yol açar.

# OPcache durumunu kontrol et
php -r "var_dump(opcache_get_status());" | head -30

# PHP konfigürasyonunda OPcache ayarlarını görüntüle
php -i | grep -i opcache

OPcache için önerilen production ayarları (/etc/php/8.1/fpm/conf.d/10-opcache.ini):

opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.revalidate_freq=0
opcache.fast_shutdown=1
opcache.enable_cli=0

Dikkat: validate_timestamps=0 deployment’ta önbelleği elle temizlemenizi gerektirir:

# PHP-FPM restart ile OPcache temizlenir
systemctl reload php8.1-fpm

# Ya da kod içinden (deployment scriptinde)
php -r "opcache_reset();"

Upstream Konfigürasyonu ile Yük Dengeleme

Birden fazla PHP-FPM instance kullanıyorsanız ya da farklı uygulamalar için ayrı pool’lar oluşturduysanız:

upstream php_primary {
    server unix:/run/php/php8.1-fpm-app1.sock weight=5;
    server unix:/run/php/php8.1-fpm-app2.sock weight=3;
    keepalive 32;
}

upstream php_background {
    server unix:/run/php/php8.1-fpm-worker.sock;
    keepalive 8;
}

server {
    location ~ ^/api/ {
        fastcgi_pass php_primary;
        include fastcgi.conf;
    }
    
    location ~ ^/admin/ {
        fastcgi_pass php_background;
        include fastcgi.conf;
    }
}

Güvenlik: PHP Dosyası Olmayan İstekleri Engelleme

Bu kritik bir güvenlik ayarı. Saldırganlar resim yükleme açıklarını kullanarak PHP kodu çalıştırmaya çalışır. location ~ .php$ kuralı bu durumu engellemek için yeterli değil. Nginx’te şu yapılandırma daha güvenli:

location ~ .php$ {
    # Dosya gerçekten var mı kontrol et
    try_files $uri =404;
    
    # Path info ile PHP injection engellemek için
    fastcgi_split_path_info ^(.+.php)(/.*)$;
    
    # SCRIPT_FILENAME'in gerçek bir PHP dosyası olduğunu doğrula
    if (!-f $document_root$fastcgi_script_name) {
        return 404;
    }
    
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
    include fastcgi.conf;
}

# Yükleme dizinindeki PHP çalıştırmayı engelle
location ~* /uploads/.*.php$ {
    deny all;
}

Hızlı Sorun Giderme Kontrol Listesi

Bir sorunla karşılaştığınızda şu sırayı takip etmek işleri hızlandırır:

  • Nginx ve PHP-FPM servislerinin çalışıp çalışmadığını kontrol et: systemctl status nginx php8.1-fpm
  • Nginx hata loguna bak: tail -f /var/log/nginx/error.log
  • PHP-FPM loguna bak: tail -f /var/log/php8.1-fpm.log
  • Socket/port erişilebilirliğini test et: curl --unix-socket /run/php/php8.1-fpm.sock http://localhost/ ya da nc -zv 127.0.0.1 9000
  • Nginx konfigürasyonunu test et: nginx -t
  • PHP-FPM konfigürasyonunu test et: php-fpm8.1 -t
  • İzin sorunlarını kontrol et: ls -la /run/php/
  • Pool durumunu izle: curl http://127.0.0.1/fpm-status?full
# Tüm bu kontrolleri bir arada yapan hızlı script
#!/bin/bash
echo "=== Servis Durumu ==="
systemctl is-active nginx php8.1-fpm

echo "=== Socket Durumu ==="
ls -la /run/php/php8.1-fpm.sock 2>/dev/null || echo "Socket bulunamadi!"

echo "=== Nginx Config Testi ==="
nginx -t 2>&1

echo "=== PHP-FPM Config Testi ==="
php-fpm8.1 -t 2>&1

echo "=== Son Nginx Hatalari ==="
tail -5 /var/log/nginx/error.log

echo "=== Son PHP-FPM Hatalari ==="
tail -5 /var/log/php8.1-fpm.log

Sonuç

Nginx ile PHP-FPM arasındaki sorunların büyük çoğunluğu birkaç temel kategoriye giriyor: yanlış socket yolu ya da izin sorunları, yetersiz worker sayısı, timeout uyumsuzlukları ve yanlış SCRIPT_FILENAME konfigürasyonu. Bu sorunların çözümü aslında oldukça sistematik; doğru logları okumayı öğrenir ve konfigürasyonu anlarsanız çoğu sorunu birkaç dakika içinde çözebilirsiniz.

Production ortamında bir şeyleri değiştirmeden önce mutlaka nginx -t ve php-fpm8.1 -t komutlarıyla konfigürasyonu test edin. Küçük bir yazım hatası tüm sitenizi durdurabilir. PHP-FPM durum sayfasını ve slowlog’u aktif etmek ise proaktif izleme açısından gerçekten değer. Sorun çıkmadan önce sistem nasıl davranıyor, normal yük nedir, bunları bilmek bir kriz anında paniği önemli ölçüde azaltır.

Son olarak, OPcache’i ihmal etmeyin. Özellikle shared hosting’den taşınan ya da geliştirme ortamından production’a çıkan uygulamalarda OPcache’i açmak ve doğru yapılandırmak performansı 5-10 kat artırabilir. Bu kadar kolay bir kazanım için görmezden gelmek olmaz.

Bir yanıt yazın

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