Caddy Web Sunucusu ile PHP-FPM Entegrasyonu

Modern web geliştirme dünyasında PHP hala güçlü bir şekilde varlığını sürdürüyor. WordPress, Laravel, Symfony gibi devlerin arkasında PHP var ve bu projeleri production ortamında çalıştırmak için sağlam bir web sunucusu konfigürasyonuna ihtiyacın var. Nginx yıllarca bu işin standardı oldu, Apache öncesinden beri sektörde. Ama artık masada yeni ve son derece yetenekli bir oyuncu var: Caddy. Bu yazıda Caddy ile PHP-FPM’i nasıl entegre edeceğini, production ortamına nasıl taşıyacağını ve yaygın sorunları nasıl çözeceğini ele alacağız.

PHP-FPM Nedir, Neden Önemli?

PHP-FPM (FastCGI Process Manager), PHP’nin FastCGI implementasyonudur. Klasik mod_php yaklaşımının aksine, PHP-FPM bağımsız bir process olarak çalışır ve web sunucusu ile FastCGI protokolü üzerinden haberleşir. Bu mimarinin avantajları somuttur:

  • Process yönetimi: Worker process havuzlarını dinamik olarak yönetir
  • İzolasyon: Her PHP uygulaması farklı bir pool üzerinde, farklı kullanıcı yetkisiyle çalışabilir
  • Performans: Persistent worker processler sayesinde her request’te PHP’yi başlatma yükü ortadan kalkar
  • Kaynak kontrolü: Her pool için ayrı memory ve process limitleri tanımlayabilirsin

Caddy ile PHP-FPM kombinasyonu, özellikle otomatik HTTPS, minimal konfigürasyon ve modern HTTP/2 desteği açısından Nginx alternatifine güçlü bir rakip sunuyor.

Ortam Kurulumu

Önce gerekli paketleri kuralım. Ubuntu 22.04 LTS üzerinde çalışıyoruz ama Debian tabanlı her sistemde aynı adımlar geçerli.

# Sistem güncellemesi
sudo apt update && sudo apt upgrade -y

# PHP-FPM kurulumu (PHP 8.2 için)
sudo apt install -y php8.2-fpm php8.2-cli php8.2-mysql php8.2-curl 
    php8.2-gd php8.2-mbstring php8.2-xml php8.2-zip php8.2-bcmath

# PHP-FPM servisini başlat ve otomatik başlatmayı aktif et
sudo systemctl enable php8.2-fpm
sudo systemctl start php8.2-fpm

# Caddy kurulumu için resmi repo ekle
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | 
    sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | 
    sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

Kurulum tamamlandıktan sonra servislerin durumunu kontrol et:

# Servis durumlarını kontrol et
sudo systemctl status caddy
sudo systemctl status php8.2-fpm

# PHP-FPM socket'in oluştuğunu doğrula
ls -la /run/php/php8.2-fpm.sock

Temel Caddyfile Konfigürasyonu

Caddy’nin konfigürasyon dosyası olan Caddyfile, Nginx’in karmaşık syntax’ına kıyasla son derece okunaklı. En temel PHP entegrasyonu şu şekilde görünür:

# /etc/caddy/Caddyfile

example.com {
    root * /var/www/html
    
    # PHP dosyalarını PHP-FPM'e yönlendir
    php_fastcgi unix//run/php/php8.2-fpm.sock
    
    # Dosya sunucusunu aktif et
    file_server
    
    # Logları yapılandır
    log {
        output file /var/log/caddy/access.log
        format json
    }
}

Bu kadar. Nginx’te 20 satır sürecek bir konfigürasyonu Caddy 6-7 satırda hallediyor. php_fastcgi direktifi aslında bir shortcut; arkasında reverse_proxy ve FastCGI ayarlarını otomatik olarak yapılandırıyor.

Konfigürasyonu yeniden yükle:

sudo systemctl reload caddy

# Veya konfigürasyonu önce doğrula
caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

WordPress Konfigürasyonu

WordPress production ortamı için daha kapsamlı bir konfigürasyona ihtiyaç var. Güvenlik başlıkları, önbellek kontrolleri ve WordPress’e özgü yönlendirmeler dahil tam bir örnek:

# WordPress için kapsamlı Caddyfile

wordpress.example.com {
    root * /var/www/wordpress
    
    # Güvenlik başlıkları
    header {
        X-Content-Type-Options nosniff
        X-Frame-Options DENY
        X-XSS-Protection "1; mode=block"
        Referrer-Policy "strict-origin-when-cross-origin"
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        -Server
    }
    
    # Statik dosyalar için önbellek
    @static {
        file
        path *.css *.js *.ico *.gif *.jpg *.jpeg *.png *.svg *.webp *.woff *.woff2
    }
    header @static Cache-Control "max-age=31536000, immutable"
    
    # Hassas dosyalara erişimi engelle
    @forbidden {
        path /wp-config.php
        path /xmlrpc.php
        path /.htaccess
        path /readme.html
        path /license.txt
        path /wp-admin/install.php
    }
    respond @forbidden 403
    
    # WordPress için PHP-FPM
    php_fastcgi unix//run/php/php8.2-fpm.sock
    
    file_server
    
    encode gzip
    
    log {
        output file /var/log/caddy/wordpress-access.log
        format json
    }
}

WordPress permalink yapısının düzgün çalışması için dikkat etmen gereken nokta: php_fastcgi direktifi, try_files benzeri bir davranışı otomatik olarak hallediyor. WordPress’in .htaccess dosyasındaki kuralların karşılığını Caddy otomatik üstleniyor.

Laravel Uygulaması için Konfigürasyon

Laravel’in public dizini web root’u olmalı ve tüm istekler index.php‘ye yönlendirilmeli. Ayrıca .env dosyasına erişim kesinlikle engellenmeli:

# Laravel için Caddyfile

api.example.com {
    root * /var/www/laravel/public
    
    # .env ve diğer hassas dosyaları engelle
    @sensitive {
        path /.env
        path /.env.*
        path /storage/*
        path /vendor/*
    }
    respond @sensitive 403
    
    # CORS başlıkları (API için)
    @cors_preflight method OPTIONS
    handle @cors_preflight {
        header Access-Control-Allow-Origin "*"
        header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
        header Access-Control-Allow-Headers "Authorization, Content-Type, Accept"
        respond 204
    }
    
    header Access-Control-Allow-Origin "*"
    
    php_fastcgi unix//run/php/php8.2-fpm.sock {
        # Laravel için ortam değişkeni
        env APP_ENV production
    }
    
    file_server
    encode gzip zstd
    
    log {
        output file /var/log/caddy/laravel-access.log
        format json
    }
}

PHP-FPM Pool Yapılandırması

Varsayılan PHP-FPM pool konfigürasyonu production için genellikle yeterli değil. Özellikle birden fazla uygulama çalıştırıyorsan her biri için ayrı pool tanımlamalısın. Bu hem güvenlik hem de performans açısından kritik.

# Yeni bir pool konfigürasyon dosyası oluştur
sudo nano /etc/php/8.2/fpm/pool.d/wordpress.conf

Pool dosyasının içeriği:

[wordpress]
; Process kimliği - güvenlik için ayrı kullanıcı
user = www-wordpress
group = www-wordpress

; Unix socket kullan - TCP'den daha hızlı
listen = /run/php/php8.2-wordpress.sock
listen.owner = caddy
listen.group = caddy
listen.mode = 0660

; Process yönetim modu
pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500

; Log dosyaları
access.log = /var/log/php-fpm/wordpress-access.log
slowlog = /var/log/php-fpm/wordpress-slow.log
request_slowlog_timeout = 5s

; PHP ayarları
php_admin_value[memory_limit] = 256M
php_admin_value[upload_max_filesize] = 64M
php_admin_value[post_max_size] = 64M
php_admin_value[max_execution_time] = 300
php_admin_flag[display_errors] = off
php_admin_value[error_log] = /var/log/php-fpm/wordpress-error.log

Bu pool’u kullanan Caddy konfigürasyonu:

wordpress.example.com {
    root * /var/www/wordpress
    
    php_fastcgi unix//run/php/php8.2-wordpress.sock
    
    file_server
}

Pool oluşturulduktan sonra FPM’i yeniden başlat:

sudo systemctl restart php8.2-fpm

# Yeni socket'in oluştuğunu kontrol et
ls -la /run/php/

Çoklu Site Yönetimi

Tek bir sunucuda birden fazla PHP uygulaması çalıştırmak yaygın bir senaryo. Caddy bunu çok temiz bir şekilde hallediyor:

# /etc/caddy/Caddyfile - Çoklu site konfigürasyonu

site1.example.com {
    root * /var/www/site1
    php_fastcgi unix//run/php/php8.2-site1.sock
    file_server
    encode gzip
}

site2.example.com {
    root * /var/www/site2
    php_fastcgi unix//run/php/php8.2-site2.sock
    file_server
    encode gzip
}

# Tüm www olmayan trafiği www'ye yönlendir
www.example.com {
    redir https://example.com{uri} permanent
}

# Geliştirme ortamı - yerel IP ile erişim
:8080 {
    root * /var/www/dev
    php_fastcgi unix//run/php/php8.2-fpm.sock
    file_server
}

Performans Optimizasyonu

OPcache Konfigürasyonu

PHP-FPM performansının en önemli parçası OPcache. /etc/php/8.2/fpm/conf.d/10-opcache.ini dosyasını düzenle:

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

Not: validate_timestamps=0 production için idealdir ama deploy sonrası OPcache’i temizlemen gerekir.

Caddy’de Sıkıştırma ve Cache

example.com {
    root * /var/www/html
    
    # Sıkıştırma - zstd Brotli'den sonra en iyi alternatif
    encode zstd gzip
    
    # Statik dosyalar için agresif önbellek
    @assets path /assets/* /static/*
    header @assets {
        Cache-Control "public, max-age=31536000, immutable"
        Vary Accept-Encoding
    }
    
    # HTML dosyaları için önbellek yok
    @html path *.html
    header @html Cache-Control "no-cache, must-revalidate"
    
    php_fastcgi unix//run/php/php8.2-fpm.sock
    file_server
}

Güvenlik Sertleştirme

Production ortamında güvenlik başlıkları ve erişim kontrolleri kritik. Bir snippet tanımlayarak tüm sitelerde tekrar kullanabilirsin:

# /etc/caddy/Caddyfile başına snippet tanımla

(security_headers) {
    header {
        X-Content-Type-Options "nosniff"
        X-Frame-Options "SAMEORIGIN"
        X-XSS-Protection "1; mode=block"
        Referrer-Policy "strict-origin-when-cross-origin"
        Permissions-Policy "camera=(), microphone=(), geolocation=()"
        Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        -X-Powered-By
        -Server
    }
}

(block_sensitive) {
    @sensitive_files {
        path *.sql *.log *.env *.bak *.swp *.git
        path_regexp hidden /.(git|svn|hg|bzr)
    }
    respond @sensitive_files 404
}

example.com {
    import security_headers
    import block_sensitive
    
    root * /var/www/html
    php_fastcgi unix//run/php/php8.2-fpm.sock
    file_server
}

Sorun Giderme

PHP-FPM Bağlantı Sorunları

En sık karşılaşılan sorun, Caddy’nin PHP-FPM socket’ine erişememesi:

# Caddy kullanıcısını kontrol et
ps aux | grep caddy

# Socket izinlerini kontrol et
ls -la /run/php/php8.2-fpm.sock

# Caddy kullanıcısını www-data grubuna ekle (gerekirse)
sudo usermod -aG www-data caddy

# FPM socket konfigürasyonunu kontrol et
grep -r "listen.owner|listen.group|listen.mode" /etc/php/8.2/fpm/pool.d/

# Hataları takip et
sudo journalctl -u caddy -f
sudo journalctl -u php8.2-fpm -f

502 Bad Gateway Hatası

502 hatası genellikle PHP-FPM’in çalışmadığını ya da bağlantı sorununu işaret eder:

# FPM durumunu kontrol et
sudo systemctl status php8.2-fpm

# FPM process listesini gör
sudo php-fpm8.2 -t

# Socket erişim testi
sudo -u caddy stat /run/php/php8.2-fpm.sock

# Pool loglarını kontrol et
sudo tail -f /var/log/php-fpm/error.log

Yüksek Memory Kullanımı

Pool’daki worker sayısı çok yüksekse memory sorunları yaşanır:

# Mevcut PHP-FPM process sayısını kontrol et
ps aux | grep php-fpm | wc -l

# Her process'in memory kullanımı
ps aux | grep php-fpm | awk '{sum+=$6} END {print sum/1024 " MB"}'

# FPM status endpoint'i aktif et (pool konfigürasyonuna ekle)
# pm.status_path = /fpm-status
# Ardından Caddy'de bu path'i yerel erişime aç

Deployment Workflow

Production’a deploy ederken OPcache temizlenmesi ve graceful restart kritik:

#!/bin/bash
# deploy.sh

set -e

APP_DIR="/var/www/laravel"
PHP_FPM_SOCK="/run/php/php8.2-laravel.sock"

echo "Deploying application..."

cd $APP_DIR

# Git pull
git pull origin main

# Composer güncelle
composer install --no-dev --optimize-autoloader

# Laravel optimizasyonları
php artisan config:cache
php artisan route:cache
php artisan view:cache

# OPcache temizle - graceful reload ile
sudo systemctl reload php8.2-fpm

# Caddy konfigürasyonunu doğrula ve yeniden yükle
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

echo "Deployment completed successfully!"

Monitoring ve Log Yönetimi

Caddy’nin JSON log formatı log analiz araçlarıyla mükemmel uyum sağlar:

# Gerçek zamanlı hata takibi
sudo tail -f /var/log/caddy/access.log | jq 'select(.status >= 400)'

# En çok istek alan URL'ler
sudo cat /var/log/caddy/access.log | jq -r '.request.uri' | sort | uniq -c | sort -rn | head 20

# Yavaş PHP requestleri tespit et (3 saniyeden uzun)
sudo cat /var/log/caddy/access.log | jq 'select(.duration > 3) | {uri: .request.uri, duration: .duration}'

# PHP-FPM slow log'u aktif etme
# Pool konfigürasyonunda:
# slowlog = /var/log/php-fpm/slow.log
# request_slowlog_timeout = 3s
sudo tail -f /var/log/php-fpm/slow.log

Sonuç

Caddy ile PHP-FPM entegrasyonu, modern web altyapısı kurmanın en temiz yollarından biri. Nginx’e kıyasla konfigürasyon karmaşıklığı dramatik biçimde azalıyor ve otomatik HTTPS yönetimi production deployment süreçlerini büyük ölçüde sadeleştiriyor.

Bu yazıda ele aldığımız konular prodüksiyona geçiş için sağlam bir temel oluşturuyor:

  • Temel entegrasyon: php_fastcgi direktifi ile saniyeler içinde PHP desteği
  • Pool izolasyonu: Her uygulama için ayrı PHP-FPM pool ve kullanıcı
  • Güvenlik sertleştirme: Snippet’ler ile yeniden kullanılabilir güvenlik konfigürasyonları
  • Performans: OPcache, sıkıştırma ve statik dosya önbellekleme
  • Sorun giderme: Socket izinleri, 502 hataları ve memory yönetimi

Birkaç pratik tavsiye: Staging ortamında her konfigürasyon değişikliğini caddy validate ile test et, production’a geçmeden önce her zaman doğrula. PHP-FPM pool ayarlarını sunucunun RAM kapasitesine göre hesapla; kaba kural olarak pm.max_children = (Toplam RAM - OS için bırakılan RAM) / Ortalama PHP process boyutu formülünü kullanabilirsin. Log rotasyonu için logrotate konfigürasyonunu da ihmal etme, aksi takdirde log dosyaların disk doldurana kadar büyümeye devam eder.

Caddy hala Nginx kadar geniş bir ekosisteme sahip değil, belirli edge case’lerde daha az kaynak bulabilirsin. Ama standart PHP uygulamaları için sunduğu basitlik ve otomatik HTTPS yetenekleri göz önüne alındığında, yeni projelerde tercih etmen için yeterli sebep var.

Yorum yapın