PHP ve Apache Entegrasyonu: Sık Karşılaşılan Hatalar ve Çözümleri

PHP ve Apache’yi aynı sunucuda çalıştırmak kulağa basit gelir. Paketi kur, servisi başlat, siteyi aç. Ama pratikte bu ikili bazen birbirini o kadar iyi anlamaz ki saatlerce hata ayıklamak zorunda kalırsınız. Yıllar içinde onlarca farklı sunucuda karşılaştığım sorunları ve çözümlerini bu yazıda derledim. Hem yeni başlayanlar hem de “ben bunu biliyorum” deyip sonra aynı hataya düşen deneyimliler için faydalı olacak şeyler var.

PHP ve Apache Nasıl Çalışır: Temel Kavramlar

Hata ayıklamaya başlamadan önce bu ikili arasındaki ilişkiyi net anlamak gerekiyor. Apache, PHP’yi birkaç farklı şekilde çalıştırabilir.

mod_php: PHP, Apache modülü olarak yüklenir. Performanslıdır ama Apache’nin her worker’ı PHP’yi yükler. Bellek açısından pahalıdır.

PHP-FPM (FastCGI Process Manager): PHP ayrı bir process havuzu olarak çalışır. Apache, mod_proxy_fcgi aracılığıyla PHP-FPM’e bağlanır. Modern sistemlerde tercih edilen yöntem budur.

CGI/FastCGI: Eski yöntemdir, artık pek tercih edilmez.

Bu farkı bilmeden hata ayıklamaya çalışmak, hangi motoru aradığınızı bilmeden araç kutusunu karıştırmak gibidir.

Hata 1: PHP Dosyaları Tarayıcıda Ham Kod Olarak Görünüyor

Bu, yeni kurulumlarda en sık karşılaşılan hatadır. Tarayıcı PHP kodunu işlemek yerine direkt gösterir.

Neden olur?

Apache, .php uzantılı dosyaları PHP’ye yönlendirmiyor demektir. Ya mod_php yüklenmemiş, ya PHP-FPM bağlantısı kurulmamış, ya da ilgili konfigürasyon dosyası etkinleştirilmemiştir.

Önce yüklü modülleri kontrol edin:

apache2ctl -M | grep php
# veya CentOS/RHEL için:
httpd -M | grep php

Eğer çıktıda php_module veya proxy_fcgi_module yoksa sorun buradadır.

mod_php kullanıyorsanız:

# Ubuntu/Debian
sudo a2enmod php8.2
sudo systemctl restart apache2

# Konfigürasyonu kontrol edin
cat /etc/apache2/mods-enabled/php8.2.conf

PHP-FPM kullanıyorsanız, virtualhost konfigürasyonunuzda şu satırların olması gerekir:

# /etc/apache2/sites-available/siteadiniz.conf
<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/html

    <FilesMatch .php$>
        SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost"
    </FilesMatch>
</VirtualHost>

Konfigürasyonu uyguladıktan sonra Apache’yi test edin ve yeniden başlatın:

sudo apachectl configtest
sudo systemctl reload apache2

Hata 2: 500 Internal Server Error ve Hiçbir Log Yok

Bu hata özellikle sinir bozucudur çünkü kullanıcıya hiçbir şey söylemez. Logları kontrol ettiğinizde de bazen boş gelir.

İlk yapılacak şey: Log seviyesini artırmak

# Apache error log konumunu bulun
grep -i "ErrorLog" /etc/apache2/apache2.conf
grep -i "ErrorLog" /etc/apache2/sites-enabled/*.conf

# Logları canlı takip edin
sudo tail -f /var/log/apache2/error.log
sudo tail -f /var/log/apache2/access.log

Eğer hala yetersiz bilgi görüyorsanız, virtualhost konfigürasyonuna log seviyesi ekleyin:

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/html
    LogLevel debug
    ErrorLog ${APACHE_LOG_DIR}/example-error.log
    CustomLog ${APACHE_LOG_DIR}/example-access.log combined
</VirtualHost>

PHP tarafında hata görüntülemeyi açın (geliştirme ortamı için):

# php.ini dosyasını bulun
php --ini | grep "Loaded Configuration"

# Gerekli satırları düzenleyin
sudo nano /etc/php/8.2/apache2/php.ini

php.ini içinde şu değerleri ayarlayın:

display_errors = On
display_startup_errors = On
error_reporting = E_ALL
log_errors = On
error_log = /var/log/php/php_errors.log

Dikkat: display_errors = On sadece geliştirme ortamında kullanın. Prodüksiyon’da mutlaka kapalı olsun.

Hata 3: .htaccess Direktifleri Çalışmıyor

WordPress, Laravel, CodeIgniter gibi framework’lerin büyük çoğunluğu .htaccess dosyasına bağımlıdır. URL rewriting çalışmıyorsa ya AllowOverride kapalıdır ya da mod_rewrite etkin değildir.

# mod_rewrite durumunu kontrol edin
apache2ctl -M | grep rewrite

# Etkin değilse etkinleştirin
sudo a2enmod rewrite
sudo systemctl restart apache2

AllowOverride direktifini kontrol edin. Bu, hem ana konfigürasyonda hem de virtualhost’ta olabilir:

# /etc/apache2/sites-available/siteadiniz.conf
<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/html

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

AllowOverride None ise .htaccess dosyası tamamen görmezden gelinir. AllowOverride All yapınca dikkat edin: Bu kullanıcılara oldukça geniş yetkiler tanır. Daha güvenli alternatif AllowOverride FileInfo Options kullanmaktır.

Gerçek dünya senaryosu: Bir müşterinin Laravel uygulaması canlıya alındığında “Route not found” hatası veriyordu. .htaccess yerindeydi, mod_rewrite etkindu. Sorun şuydu: /etc/apache2/apache2.conf içindeki global bloğu AllowOverride None diyordu ve virtualhost’taki AllowOverride All bunu ezemiyor gibi görünüyordu. Öncelik sırası karışıklığından kaynaklanan klasik bir hatadır bu. Global konfigürasyonu düzelttikten sonra her şey çalıştı.

Hata 4: PHP-FPM Socket veya Port Bağlantı Hataları

PHP-FPM kullanıyorsanız ve şöyle bir hata görüyorsanız:

[proxy_fcgi:error] AH01079: failed to make connection to backend

ya da

Connect to unix:/run/php/php8.2-fpm.sock failed

PHP-FPM’in durumunu kontrol edin:

# PHP-FPM servisi çalışıyor mu?
sudo systemctl status php8.2-fpm

# Socket dosyası var mı?
ls -la /run/php/

# PHP-FPM log dosyasını kontrol edin
sudo tail -f /var/log/php8.2-fpm.log

Socket izin sorunları çok yaygındır. Apache’nin socket’e erişebilmesi için gerekli izinleri ayarlayın:

# /etc/php/8.2/fpm/pool.d/www.conf dosyasında
sudo nano /etc/php/8.2/fpm/pool.d/www.conf

Bu dosyada şu satırları kontrol edin ve düzenleyin:

listen = /run/php/php8.2-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

# Pool kullanıcısı Apache kullanıcısıyla eşleşmeli
user = www-data
group = www-data

Değişiklikten sonra her iki servisi yeniden başlatın:

sudo systemctl restart php8.2-fpm
sudo systemctl restart apache2

Hata 5: PHP Sürüm Karmaşası

Birden fazla PHP sürümü kurulu sistemlerde hangi sürümün kullanıldığını bilmek kritiktir. Özellikle Ubuntu/Debian’da update-alternatives mekanizması bazen karıştırıcı olabilir.

# Sistemdeki tüm PHP sürümlerini listeleyin
ls /usr/bin/php*
update-alternatives --list php

# Apache'nin hangi PHP'yi kullandığını öğrenin
php -v
apache2ctl -M | grep php

# Phpinfo dosyası oluşturun (güvenli bir konumda, test sonrası silin)
echo "<?php phpinfo(); ?>" | sudo tee /var/www/html/phpinfo.php
# Tarayıcıda kontrol edin, sonra silin!
sudo rm /var/www/html/phpinfo.php

PHP-FPM ile birden fazla sürüm çalıştırıyorsanız, her virtualhost farklı sürüme yönlenebilir. Bu güçlü bir özellik ama yanlış yapılandırılırsa kafa karıştırır:

# PHP 7.4 kullanan bir site
<VirtualHost *:80>
    ServerName eski-site.com
    DocumentRoot /var/www/eski-site

    <FilesMatch .php$>
        SetHandler "proxy:unix:/run/php/php7.4-fpm.sock|fcgi://localhost"
    </FilesMatch>
</VirtualHost>

# PHP 8.2 kullanan yeni site
<VirtualHost *:80>
    ServerName yeni-site.com
    DocumentRoot /var/www/yeni-site

    <FilesMatch .php$>
        SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost"
    </FilesMatch>
</VirtualHost>

Hata 6: upload_max_filesize ve post_max_size Sorunları

Kullanıcılar dosya yükleyemiyor ya da yükledikten sonra “413 Request Entity Too Large” hatası alıyorsa iki ayrı yerde konfigürasyon yapmanız gerekiyor: PHP ve Apache.

PHP tarafı:

sudo nano /etc/php/8.2/fpm/php.ini
# veya mod_php için:
sudo nano /etc/php/8.2/apache2/php.ini

İlgili satırları düzenleyin:

upload_max_filesize = 64M
post_max_size = 128M
max_execution_time = 300
max_input_time = 300
memory_limit = 256M

Önemli not: post_max_size her zaman upload_max_filesize‘dan büyük olmalıdır. Aksi takdirde büyük dosya yüklemeleri sessizce başarısız olur, hata bile vermez.

Apache tarafında ise LimitRequestBody direktifi vardır:

# Virtualhost içine veya .htaccess'e ekleyin
LimitRequestBody 134217728
# 128MB = 128 * 1024 * 1024 = 134217728 byte

Değişikliklerden sonra:

sudo systemctl restart php8.2-fpm
sudo systemctl restart apache2

Hata 7: SSL ve PHP Session Sorunları

HTTPS’e geçtikten sonra oturum açma işlemleri çalışmıyorsa veya oturumlar sürekli kaybediliyorsa SSL ile ilgili session konfigürasyonu sorununa bakın.

Apache HTTPS konfigürasyonu ile PHP session’ları arasında sık karşılaşılan bir sorun: HTTP’den HTTPS’e yönlendirme yapıldığında session çerezi kaybolur. Bunun sebebi genellikle Secure flag’inin eksik olmasıdır.

# php.ini'de session güvenlik ayarları
session.cookie_secure = On
session.cookie_httponly = On
session.cookie_samesite = Strict
session.use_strict_mode = On

Bir de mod_headers ile HSTS başlığı eklemeyi ihmal etmeyin:

# Modülü etkinleştirin
sudo a2enmod headers

# Virtualhost konfigürasyonu
<VirtualHost *:443>
    ServerName example.com
    
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "SAMEORIGIN"
</VirtualHost>

Hata 8: Yanlış Dosya İzinleri

“Permission denied” hatası log dosyasında görünüyorsa ve PHP dosyaları açılamıyorsa dosya izinlerine bakın. Bu basit görünen hata, özellikle deployment pipeline’larından gelen dosyalarda çok yaygındır.

# Hatalı dosya sahipliğini kontrol edin
ls -la /var/www/html/

# Apache kullanıcısını öğrenin
grep -i "User|Group" /etc/apache2/envvars
# veya
ps aux | grep apache | head -3

# Doğru sahipliği ayarlayın
sudo chown -R www-data:www-data /var/www/html/
sudo find /var/www/html -type d -exec chmod 755 {} ;
sudo find /var/www/html -type f -exec chmod 644 {} ;

Özel durum: Yazılabilir dizinler (cache, upload, log klasörleri) için:

sudo chmod -R 775 /var/www/html/storage
sudo chmod -R 775 /var/www/html/cache
sudo chown -R www-data:www-data /var/www/html/storage

Gerçek dünya senaryosu: CI/CD pipeline’ından deploy edilen bir uygulama production’da “file not found” veya “permission denied” veriyordu. Geliştirici kendi kullanıcısıyla dosyaları kopyalamıştı, sahiplik developer_username:developer_username olarak kalmıştı. Apache www-data kullanıcısıyla çalıştığı için hiçbir dosyaya erişemiyordu. Deploy script’ine otomatik chown komutu eklenerek çözüldü.

Apache ve PHP Log Analizi: Sistematik Yaklaşım

Hata ayıklamada log okumak en kritik beceridir. Rastgele bakmak yerine sistematik bir yaklaşım benimseyin.

# Son 100 satır hata logu
sudo tail -100 /var/log/apache2/error.log

# Belirli bir hata türünü filtreleyin
sudo grep "PHP" /var/log/apache2/error.log | tail -50
sudo grep "AH01079|AH00898" /var/log/apache2/error.log

# Zaman aralığına göre filtreleyin (örneğin bugünkü hatalar)
sudo grep "$(date '+%a %b %d')" /var/log/apache2/error.log

# Access log'dan 500 hatalarını bulun
sudo awk '$9 == "500"' /var/log/apache2/access.log | tail -20

# PHP-FPM log analizi
sudo tail -f /var/log/php8.2-fpm.log

# Tüm logları aynı anda takip edin (birden fazla terminal açın veya tmux kullanın)
sudo tail -f /var/log/apache2/error.log /var/log/php8.2-fpm.log /var/log/apache2/access.log

Logda dikkat edilecek kritik ifadeler:

  • AH00526: Syntax error in configuration
  • AH01079: Backend connection failure (PHP-FPM sorunları)
  • PHP Fatal error: Kodda düzeltilemez hata
  • PHP Warning: Kod çalışıyor ama sorun var
  • Permission denied: Dosya izin sorunu
  • No such file or directory: Yol ya da dosya bulunamadı

Konfigürasyon Doğrulama ve Test

Herhangi bir değişiklikten sonra mutlaka yapılması gereken kontroller:

# Apache konfigürasyon syntaxını test edin
sudo apachectl -t
# veya
sudo apache2ctl configtest

# PHP-FPM konfigürasyonunu test edin
sudo php-fpm8.2 -t

# Tüm etkin modülleri listeleyin
apache2ctl -M 2>/dev/null | sort

# Sanal host konfigürasyonlarını listeleyin
sudo apache2ctl -S

# PHP modüllerini kontrol edin
php -m
php -m | grep -i "curl|pdo|mysql|gd|mbstring"

Konfigürasyon değişikliği sonrası tam restart yerine reload kullanmayı tercih edin. Bu aktif bağlantıları kesmez:

sudo systemctl reload apache2
# Eğer reload çalışmazsa:
sudo systemctl restart apache2

Performans ile İlgili Sık Yapılan Hatalar

PHP-FPM worker sayısı yanlış ayarlandığında Apache loglarında “connect() to unix socket failed (11: Resource temporarily unavailable)” hatası görürsünüz.

sudo nano /etc/php/8.2/fpm/pool.d/www.conf

Ayarlanması gereken temel parametreler:

  • pm = dynamic: İş yüküne göre worker sayısı otomatik ayarlanır
  • pm.max_children = 50: Maksimum worker sayısı (RAM’e göre belirleyin, her worker ~30-50MB RAM kullanır)
  • pm.start_servers = 5: Başlangıçta açılacak worker sayısı
  • pm.min_spare_servers = 5: Minimum boşta bekleyen worker
  • pm.max_spare_servers = 10: Maksimum boşta bekleyen worker
  • pm.max_requests = 500: Her worker bu kadar istek sonrası yeniden başlar (bellek sızıntılarına karşı)

PHP-FPM durumunu canlı olarak izlemek için status sayfasını etkinleştirin:

# www.conf dosyasında
pm.status_path = /fpm-status

# Apache virtualhost'ta bu path'i kısıtlayın
<Location /fpm-status>
    Require local
    ProxyPass "fcgi://localhost/fpm-status"
</Location>

Sık Atlanan ama Kritik Bir Nokta: PHP CLI ve Web PHP Farkı

Birçok sysadmin terminalde php -v yazıp sürümü görünce “tamam” der. Oysa komut satırındaki PHP ile Apache’nin kullandığı PHP tamamen farklı php.ini dosyalarını okuyabilir.

# CLI php.ini konumu
php --ini | grep "Loaded Configuration"

# Apache/FPM php.ini konumu
php-fpm8.2 --ini | grep "Loaded Configuration"

# ya da phpinfo() çıktısına bakın
# "Configuration File (php.ini) Path" ve "Loaded Configuration File" satırlarına dikkat edin

Terminalde php script.php çalışıyor ama web üzerinden hata veriyorsa ilk bakılacak yer bu farklılıktır. Uzantı yüklü mü, değer nedir soruları için doğru php.ini‘yi düzenlediğinizden emin olun.

Sonuç

PHP ve Apache entegrasyonundaki hataların büyük çoğunluğu birkaç temel kategoriye girer: yanlış modül yapılandırması, dosya izin sorunları, PHP-FPM bağlantı hataları ve php.ini ayarlarının gözden kaçırılması. Bu yazıda ele aldığım hataların ortak bir özelliği var: Logları düzgün okumadan çözmeye çalışmak çok zaman kaybettirir.

Sistematik yaklaşım şu sırayı izlemeli: Apache hata loguna bak, PHP-FPM loguna bak, apachectl configtest ile syntax kontrol et, apache2ctl -M ile modülleri doğrula, gerekiyorsa phpinfo() ile çalışma zamanı konfigürasyonunu kontrol et.

Prodüksiyon sunucusunda değişiklik yapmadan önce her zaman staging ortamında test edin. Konfigürasyon dosyalarında değişiklik öncesi yedek alın. reload kullanarak aktif bağlantıları kesmeyin. Ve display_errors = On ayarını prodüksiyonda asla açık bırakmayın.

Bu hataların büyük kısmıyla ben de defalarca karşılaştım. Kimi zaman saatlerce uğraştıktan sonra sorunun tek bir satır konfigürasyon farkından kaynaklandığını gördüm. Umarım bu yazı o saatleri birkaç dakikaya indirmenize yardımcı olur.

Benzer Konular

Bir yanıt yazın

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