İçeriğe geç
Süreçlerinize uygun yazılımı birlikte planlayalım. Demo ve teklif için arayın: +90 546 737 48 29

TR EN DE

Apache ve .htaccess'ten Nginx'e geçiş

Apache'de site sahibi, klasöre koyduğu bir .htaccess dosyasıyla yönlendirme, parola koruması ya da önbellek ayarı yapabilir; sunucu bu dosyayı her istekte okur. Nginx'te böyle bir dosya yoktur: tüm kurallar sunucunun kendi yapılandırma dosyalarında durur ve sunucu yeniden yüklendiğinde bir kez okunur.

Bu yüzden Apache'den Nginx'e geçiş bir "dosya kopyalama" işi değil, bir çeviri işidir: .htaccess içindeki her kuralın Nginx'teki karşılığı yazılır. Bu rehber en yaygın kuralları yan yana örneklerle verir, sık düşülen tuzakları ve geçişten sonra neyin sınanacağını anlatır. Nginx'e yeniyseniz önce Nginx nedir rehberine göz atın.

Kısaca

  • Nginx .htaccess dosyalarını okumaz; kurallar sunucu yapılandırmasına taşınır.
  • Çoğu RewriteRule için rewrite gerekmez: try_files ve return yeterlidir.
  • PHP, mod_php yerine PHP-FPM ile çalışır; php_value satırları başka yere taşınır.
  • Her değişiklik nginx -t ile sınanır, sonra yeniden yüklenir.

İpucu

Geçişi canlı sitede değil, önce bir deneme ortamında ya da ayrı bir alt alan adında yapın; eski Apache yapılandırmasını ve .htaccess dosyalarını geri dönüş için saklayın.

Bu sayfada

Geçişin mantığı ve sırası

  1. Temel farkı anlayın

    • Kuralların yeri: Apache'de kurallar klasörlere dağılmış .htaccess dosyalarında olabilir. Nginx'te hepsi sunucu yapılandırmasındadır: genellikle site başına bir dosya ve içinde bir server { … } bloğu. Dosyaların yeri dağıtıma göre değişir (/etc/nginx/conf.d yaygındır; /etc/nginx/sites-available Debian/Ubuntu düzenidir).
    • Yürürlüğe girme: .htaccess kaydedildiği anda geçerlidir. Nginx'te değişiklik nginx -t ile sınanır ve yeniden yükleme (reload) ile geçerli olur. Hatalı yapılandırma sınamada yakalanır; çalışan site etkilenmez.
    • Yetki: Yapılandırmayı değiştirmek sunucu yöneticisi yetkisi ister. Paylaşımlı barındırmada bu erişim çoğunlukla yoktur; orada kuralları barındırma firması ya da panel uygular.
    • Yaklaşım: Apache'de her şey RewriteRule ile çözülür. Nginx'te istek önce bir location ile eşleştirilir, sonra o konumun yönergeleri (directive) uygulanır.
    Karşılaştırma: Apache kuralları klasörlerdeki .htaccess dosyalarından her istekte okur; Nginx tek bir sunucu yapılandırmasını yeniden yüklemede okur : Büyüt
  2. Geçişi planlayın

    1. Envanter çıkarın: Sitedeki tüm .htaccess dosyalarını bulun; alt klasörlerdekiler (yönetim paneli, yükleme klasörü) kolay unutulur. Apache sanal sunucu (VirtualHost) dosyasındaki kurallara da bakın.
    2. Kuralları sınıflayın: yönlendirme, adres yeniden yazma, erişim kısıtı, başlıklar, PHP ayarları. Artık kullanılmayanları taşımayın.
    3. Nginx karşılıklarını yazın (aşağıdaki bölümler).
    4. Sınayın: nginx -t, ardından deneme ortamında test listesi.
    5. Geçin ve izleyin: hata günlüğünü ilk günlerde yakından takip edin.
    Bash
    # Sitedeki tüm .htaccess dosyalarını listele (yolu kendi site dizininizle değiştirin)
    find /var/www/ornek.com -name ".htaccess" -type f
    
    # Nginx yapılandırmasını sına; sorun yoksa yeniden yükle
    sudo nginx -t && sudo systemctl reload nginx
    Akış şeması: envanter çıkar, kuralları sınıfla, Nginx karşılığını yaz, nginx -t ile sına, deneme ortamında test et, geç ve izle : Büyüt
  3. location eşleşme önceliğini öğrenin

    .htaccess kuralları yukarıdan aşağıya sırayla çalışır. Nginx'te ise bir isteği tek bir location karşılar ve hangisinin seçileceği yazılış sırasına değil şu önceliğe bağlıdır:

    1. location = /adres: tam eşleşme varsa hemen seçilir.
    2. Önek (prefix) konumları arasından en uzun eşleşen bulunur. Bu konum ^~ ile yazılmışsa seçilir ve düzenli ifadelere bakılmaz.
    3. Düzenli ifadeli konumlar (~ büyük/küçük harfe duyarlı, ~* duyarsız) dosyadaki sırayla denenir; ilk eşleşen seçilir.
    4. Hiçbir düzenli ifade eşleşmezse 2. adımda bulunan en uzun önek konumu kullanılır.

    Sonuç: location /yonetim/ içine yazdığınız parola koruması, /yonetim/index.php isteğine uygulanmaz; çünkü bu isteği location ~ \.php$ karşılar. Çözüm, konumu ^~ ile yazıp PHP konumunu içine yerleştirmektir (erişim kısıtı bölümündeki örneğe bakın).

    Şema: location eşleşme önceliği; önce tam eşleşme, sonra ^~ ile yazılmış en uzun önek, sonra dosya sırasıyla düzenli ifadeler, en son en uzun önek : Büyüt
  4. PHP'yi PHP-FPM'e bağlayın

    Apache çoğu kurulumda PHP'yi kendi içinde çalıştırır (mod_php). Nginx'in içinde PHP yoktur; .php isteklerini ayrı çalışan PHP-FPM hizmetine fastcgi_pass ile iletir.

    Nginx
    location ~ \.php$ {
        # Dosya yoksa PHP-FPM'e gönderme
        try_files $uri =404;
    
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    
        # Soket yolu dağıtıma ve PHP sürümüne göre değişir
        fastcgi_pass unix:/run/php/php-fpm.sock;
    }

    try_files $uri =404; satırı, var olmayan bir .php adresinin PHP-FPM'e gönderilmesini önler. Soket yolu ve havuz ayarları için Nginx ve PHP-FPM rehberine bakın.

    Şema: Apache'de PHP sunucunun içinde çalışır (mod_php); Nginx'te .php istekleri fastcgi_pass ile ayrı çalışan PHP-FPM hizmetine iletilir : Büyüt

Karşılık tablosu

İşApache / .htaccessNginx
Ön denetleyiciRewriteCond !-f, !-d + RewriteRuletry_files $uri $uri/ /index.php?$query_string;
HTTPS ve wwwRewriteCond %{HTTPS} + RewriteRule [R=301]ayrı server bloğu + return 301
Tek adres yönlendirmeRedirect 301location = … { return 301 …; }
Temiz adresRewriteRule … [L]rewrite … last; ya da location
Parola korumasıAuthType Basic, AuthUserFileauth_basic, auth_basic_user_file
IP kısıtıRequire ipallow / deny
Dizin listelemeOptions -Indexesautoindex off; (varsayılan zaten kapalı)
Hata sayfasıErrorDocumenterror_page
Önbellek başlıklarımod_expires (ExpiresByType)expires
Yanıt başlığıHeader setadd_header
PHPmod_phpPHP-FPM + fastcgi_pass
PHP ayarıphp_value, php_flagPHP-FPM havuz ayarı ya da .user.ini

Ön denetleyici (front controller)

Çoğu PHP çatısı ve içerik yönetim sistemi, var olmayan her adresi index.php dosyasına yollar. Apache'de bu, "dosya değilse, klasör değilse" koşullu bir RewriteRule ile yapılır:

.htaccess (Apache)
RewriteEngine On
# İstenen şey gerçek bir dosya ya da klasör değilse index.php'ye gönder
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]

Nginx'te aynı iş tek satırdır. try_files sırayla dosyayı, klasörü dener; ikisi de yoksa isteği son değere yönlendirir:

Nginx
root /var/www/ornek.com/public;
index index.php index.html;

# Biçim 1: uygulama yolu REQUEST_URI'den okuyor (çoğu çatı)
location / {
    try_files $uri $uri/ /index.php?$query_string;
}

# Biçim 2: uygulama yolu bir parametreden okuyorsa (index.php?url=...)
# Yukarıdaki "location /" yerine şu ikisini kullanın:
#
# location / {
#     try_files $uri $uri/ @uygulama;
# }
# location @uygulama {
#     rewrite ^/(.*)$ /index.php?url=$1 last;
# }

Uygulamanız yolu bir sorgu parametresinden okuyorsa (ör. index.php?url=…), örnekteki ikinci biçimi kullanın. rewrite, özgün sorgu dizgisini yeni adrese kendiliğinden ekler (Apache'deki QSA karşılığı).

HTTPS ve www yönlendirmesi

.htaccess (Apache)
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.ornek.com%{REQUEST_URI} [L,R=301]

Nginx'te bunun için koşul yazılmaz. Yönlendirilecek her ad için ayrı bir server bloğu açılır ve içine yalnızca return 301 konur; asıl site kendi bloğunda kalır:

Nginx
# 1) HTTP üzerinden gelen her şey -> HTTPS + www
server {
    listen 80;
    server_name ornek.com www.ornek.com;
    return 301 https://www.ornek.com$request_uri;
}

# 2) HTTPS ama www'siz -> www
server {
    listen 443 ssl;
    server_name ornek.com;
    ssl_certificate     /etc/letsencrypt/live/ornek.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ornek.com/privkey.pem;
    return 301 https://www.ornek.com$request_uri;
}

# 3) Asıl site
server {
    listen 443 ssl;
    server_name www.ornek.com;
    ssl_certificate     /etc/letsencrypt/live/ornek.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ornek.com/privkey.pem;

    root /var/www/ornek.com/public;
    index index.php index.html;

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

HTTPS üzerinden gelen ornek.com isteğini yönlendirebilmek için o blokta da geçerli sertifika gerekir. Sertifika kurulumu için Nginx'te HTTPS rehberine bakın.

Tek adres yönlendirme ve temiz adresler

.htaccess (Apache)
# Tek adres yönlendirme
Redirect 301 /eski-sayfa.html https://www.ornek.com/yeni-sayfa

# Bir bölümü topluca taşıma
RedirectMatch 301 ^/blog/(.*)$ https://www.ornek.com/yazilar/$1

# Temiz adres: /urun/42 -> urun.php?id=42
RewriteEngine On
RewriteRule ^urun/([0-9]+)/?$ urun.php?id=$1 [L,QSA]
Nginx
# Tek adres yönlendirme
location = /eski-sayfa.html {
    return 301 https://www.ornek.com/yeni-sayfa;
}

# Bir bölümü topluca taşıma (sorgu parametreleri korunur)
location ~ ^/blog/(.*)$ {
    return 301 https://www.ornek.com/yazilar/$1$is_args$args;
}

# Temiz adres: /urun/42 -> /urun.php?id=42
# Desen / ile başlar; özgün sorgu dizgisi kendiliğinden eklenir
rewrite ^/urun/([0-9]+)/?$ /urun.php?id=$1 last;
  • .htaccess içindeki desenlerde baştaki eğik çizgi yoktur (^urun/…); Nginx'te adres her zaman / ile başlar (^/urun/…). Çeviride en sık unutulan ayrıntı budur.
  • Yalnızca yönlendirme yapacaksanız rewrite … permanent yerine return 301 kullanın; daha yalın ve okunaklıdır.
  • last, yeni adresle location aramasını yeniden başlatır; böylece /urun.php PHP konumuna ulaşır.
  • Onlarca eski adres yönlendirecekseniz her biri için ayrı location yazmak yerine map yönergesiyle bir eşleme tablosu kurmak daha düzenlidir.

Dizin koruma, IP kısıtı ve gizli dosyalar

.htaccess (Apache)
# /yonetim/.htaccess : parola koruması
AuthType Basic
AuthName "Panel"
AuthUserFile /home/ornek/.htpasswd
Require valid-user

# /rapor/.htaccess : yalnız belirli IP adresleri (Apache 2.4)
Require ip 203.0.113.10
Require ip 10.0.0.0/24

# Kök .htaccess : dizin listelemeyi kapat, nokta ile başlayan adresleri engelle
Options -Indexes
RewriteEngine On
RewriteRule (^|/)\.(?!well-known) - [F]
Nginx
# Nokta ile başlayan dosya ve klasörler (.htaccess, .env, .git ...) kapalı;
# sertifika doğrulaması için .well-known açık kalır. Diğer düzenli ifadeli konumlardan önce yazın.
location ~ /\.(?!well-known) {
    deny all;
}

# Parola koruması. ^~ : düzenli ifadeli konumlara (ör. \.php$) bakılmaz;
# bu yüzden PHP konumu içeride yeniden tanımlanır ve koruma .php dosyalarını da kapsar.
location ^~ /yonetim/ {
    auth_basic "Panel";
    auth_basic_user_file /etc/nginx/.htpasswd;

    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/php-fpm.sock;
    }
}

# Yalnız belirli IP adresleri
location ^~ /rapor/ {
    allow 203.0.113.10;
    allow 10.0.0.0/24;
    deny all;

    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/php-fpm.sock;
    }
}

# Dizin listeleme: varsayılan zaten off; açıkça yazmak isteğe bağlıdır
autoindex off;
  • Parola dosyası: htpasswd ile üretilmiş dosya çoğu durumda Nginx'te de kullanılabilir. Parola özet türünün desteklendiğini giriş yaparak doğrulayın; dosyayı web kökünün dışında tutun.
  • IP kısıtı: allow ve deny yazıldığı sırayla değerlendirilir; ilk eşleşen kural geçerlidir. Sitenin önünde CDN ya da başka bir vekil sunucu (proxy) varsa Nginx ziyaretçinin değil vekilin IP adresini görür; gerçek adres ayrıca yapılandırılmadan IP kısıtı beklediğiniz gibi çalışmaz.
  • Dizin listeleme: Nginx'te autoindex varsayılan olarak kapalıdır; Options -Indexes satırının karşılığını yazmak zorunlu değildir.
  • Gizli dosyalar: Apache'nin varsayılan yapılandırması .ht ile başlayan dosyaları kendiliğinden gizler; Nginx'te böyle bir varsayılan yoktur. Geçişten sonra geride kalan .htaccess, .env ya da .git içeriği düz metin olarak indirilebilir. Nokta ile başlayan adresleri kapatan konumu mutlaka ekleyin ve diğer düzenli ifadeli konumlardan önce yazın.
  • Alt klasörlerdeki "Deny from all": Yükleme ya da yedek klasörlerindeki .htaccess korumaları da artık çalışmaz; her biri için bir location yazın. Bkz. Nginx güvenlik ayarları.

Hata sayfaları ve önbellek başlıkları

.htaccess (Apache)
# Özel hata sayfaları
ErrorDocument 404 /404.html
ErrorDocument 500 /500.html

# Önbellek başlıkları (mod_expires)
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType text/css "access plus 30 days"
    ExpiresByType application/javascript "access plus 30 days"
    ExpiresByType image/webp "access plus 90 days"
    ExpiresByType image/jpeg "access plus 90 days"
</IfModule>
Nginx
# Özel hata sayfaları
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;

# Önbellek başlıkları
location ~* \.(?:css|js)$ {
    expires 30d;
}
location ~* \.(?:webp|jpg|jpeg|png|gif|svg|ico)$ {
    expires 90d;
}

PHP'nin ürettiği hata yanıtlarında (ör. uygulamanın döndürdüğü 404) error_page kendiliğinden devreye girmez; bunun için ilgili konumda fastcgi_intercept_errors on; gerekir. Önbellek süreleri ve sıkıştırma için önbellek ve sıkıştırma rehberine bakın.

php_value ve php_flag satırları

.htaccess içindeki php_value ve php_flag satırları yalnızca mod_php ile çalışır. Nginx bunları okumaz; PHP-FPM de okumaz. İki karşılığı vardır:

  • PHP-FPM havuz ayarı: php_value[…] / php_flag[…] ya da uygulamanın değiştiremeyeceği php_admin_value[…] / php_admin_flag[…]. Değişiklikten sonra PHP-FPM yeniden yüklenir.
  • .user.ini: Site klasörüne konur, yeniden yükleme gerektirmez; ancak yalnızca klasör düzeyinde değiştirilebilen ayarlar için geçerlidir ve PHP dosyayı belirli aralıklarla yeniden okuduğu için (varsayılan 300 saniye) değişiklik hemen görünmeyebilir.
.htaccess (Apache)
# Yalnız mod_php ile çalışır; Nginx + PHP-FPM bu satırları okumaz
php_value upload_max_filesize 32M
php_value post_max_size 32M
php_value memory_limit 256M
php_flag display_errors off
php.ini
; Seçenek 1: site kök dizininde .user.ini dosyası
upload_max_filesize = 32M
post_max_size = 32M
memory_limit = 256M
display_errors = Off

; Seçenek 2: PHP-FPM havuz dosyası (ör. www.conf; yeri dağıtıma göre değişir)
; php_admin_* ile verilen değeri uygulama ini_set() ile değiştiremez
php_admin_value[upload_max_filesize] = 32M
php_admin_value[post_max_size] = 32M
php_admin_value[memory_limit] = 256M
php_admin_flag[display_errors] = off

Dosya yükleme sınırını taşıyorsanız Nginx tarafını da unutmayın: client_max_body_size varsayılanı 1m'dir; yükseltilmezse büyük yüklemeler PHP'ye ulaşmadan 413 hatasıyla reddedilir.

"if" yönergesi ve diğer tuzaklar

  • if bir programlama dili koşulu değildir. location içinde if ile birlikte güvenle kullanılabilecek yönergeler return ve rewrite'tır; başka yönergelerle birlikte beklenmedik sonuçlar verebilir. RewriteCond satırlarını birebir if'e çevirmek yerine önce şunu sorun: bu iş try_files, return, ayrı bir server ya da location bloğu veya map ile yapılabilir mi? Çoğu zaman yapılabilir.
  • Sıra farkı: .htaccess kuralları sırayla işler ve birbirini etkiler; Nginx'te tek bir konum seçilir. Kuralları aynı sırayla alt alta yazmak aynı sonucu vermez.
  • Devralma: add_header gibi yönergeler, alt düzeyde aynı yönerge yazıldığında üst düzeyden devralınmaz.
  • Hazır çeviri araçları: Çevrimiçi dönüştürücüler iyi bir başlangıç taslağı verebilir, ancak çıktıları sıklıkla gereksiz if içerir. Her satırı anlayarak gözden geçirin.
  • Uygulamanın kendi .htaccess dosyası: Hazır yazılımlar (içerik yönetim sistemleri, çatılar) kurulumda .htaccess üretir ve güncellemelerde değiştirebilir. Nginx'te bu kuralların karşılığını yazılımın belgesinden alın ve güncellemelerden sonra yeniden kontrol edin.

Geçişten sonra test listesi

Bash
# Yönlendirmeler: durum kodu ve Location başlığı
curl -sI http://ornek.com/ | grep -i -E "^HTTP|^location"
curl -sI https://ornek.com/ | grep -i -E "^HTTP|^location"
curl -sI https://www.ornek.com/eski-sayfa.html | grep -i -E "^HTTP|^location"

# Gizli dosyalar: 403 ya da 404 dönmeli, içerik gelmemeli
curl -s -o /dev/null -w "%{http_code}\n" https://www.ornek.com/.htaccess
curl -s -o /dev/null -w "%{http_code}\n" https://www.ornek.com/.env
curl -s -o /dev/null -w "%{http_code}\n" https://www.ornek.com/.git/config

# Parola korumalı alan: 401 dönmeli (.php dahil)
curl -s -o /dev/null -w "%{http_code}\n" https://www.ornek.com/yonetim/index.php

# Var olmayan adres: 404
curl -s -o /dev/null -w "%{http_code}\n" https://www.ornek.com/olmayan-sayfa
  • Ana sayfa, bir iç sayfa ve temiz adresli bir sayfa (ör. ürün, yazı) açılıyor.
  • http:// ve www'siz adres, tek adımda doğru HTTPS adresine 301 ile gidiyor.
  • Eski adres yönlendirmeleri çalışıyor; sorgu parametreleri korunuyor.
  • Var olmayan adres 404 ve özel hata sayfasını veriyor; var olmayan .php adresi de öyle.
  • Yönetim paneli parola soruyor; .php dosyaları dahil.
  • /.htaccess, /.env, /.git/config gibi adresler erişilemez.
  • Yükleme klasöründe PHP çalışmıyor; dizin listesi görünmüyor.
  • Form gönderimi, giriş, dosya yükleme (büyük dosya dahil) çalışıyor.
  • Statik dosyalar doğru Cache-Control ve Content-Type ile geliyor.
  • Güvenlik başlıkları yerinde (bkz. güvenlik başlıkları).
  • Nginx ve PHP-FPM hata günlüklerinde beklenmeyen kayıt yok.

Sık sorulan sorular

.htaccess dosyamı Nginx'e olduğu gibi kopyalayabilir miyim?

Hayır. Nginx .htaccess dosyalarını okumaz ve sözdizimi farklıdır. Her kuralın karşılığı sunucu yapılandırmasına yazılır; dosyanın kendisi Nginx açısından sıradan bir metin dosyasıdır ve erişime kapatılmalıdır.

Paylaşımlı barındırmada Nginx kurallarını kendim yazabilir miyim?

Genellikle hayır; sunucu yapılandırması yönetici yetkisi ister. Bazı paneller sınırlı bir alan sunar ya da Nginx'i Apache'nin önünde çalıştırır; bu durumda .htaccess çalışmaya devam eder. Barındırma firmanıza sorun.

Nginx'i Apache'nin önünde kullanırsam .htaccess çalışır mı?

Evet. Nginx ters vekil (reverse proxy) olarak öne konup istekleri Apache'ye iletiyorsa .htaccess kurallarını yine Apache uygular. Yalnız Nginx'in doğrudan sunduğu statik dosyalar bu kurallardan geçmez.

Her RewriteCond için "if" yazmam gerekir mi?

Hayır; çoğu zaman gerekmez. Dosya var mı denetimi try_files ile, alan adı ve HTTPS koşulları ayrı server bloklarıyla, adrese göre koşullar location ile karşılanır. "if" yalnızca return ya da rewrite ile birlikte ve başka yol yoksa kullanılmalıdır.

Geçişten sonra site yavaşladı ya da 502 veriyor; nereye bakmalıyım?

Önce Nginx hata günlüğüne ve PHP-FPM günlüğüne bakın. 502 çoğunlukla PHP-FPM'in çalışmadığını ya da soket yolunun yanlış olduğunu gösterir.

BYK Yazılım Destek Ekibi
Bu rehber BYK Yazılım destek ekibi tarafından hazırlanır ve düzenli olarak gözden geçirilir. Son güncelleme: 04.10.2026.

İlgili rehberler

Web sitenizin altyapısını birlikte konuşalım

BYK Yazılım kurumsal web siteleri geliştirir. Sitenizle ilgili sorularınız için bize yazabilirsiniz.

Bize ulaşın Kurumsal web sitesi hizmetimiz