Projeyi FTP ile yüklediniz, dosyalar yerinde, izinler doğru. Ama ornek.com adresine girdiğinizde ya boş bir dizin listesi ya da "Index of /" yazan çıplak bir sayfa karşılıyor sizi. Site sadece ornek.com/public/ yazdığınızda açılıyor. Aynı sorun bir React projesinde ornek.com/dist/, bir Laravel kurulumunda ornek.com/public/, elle yüklenmiş bir tema demosunda ornek.com/site/ biçiminde karşınıza çıkar.
Bunun sebebi basit: web sunucusu bir alan adına istek geldiğinde diskte tek bir klasöre bakar. Bu klasöre document root (kök dizin) denir ve paylaşımlı hostingte genellikle /home/kullanici/public_html olur. Projenizin açılış dosyası bu klasörün bir alt seviyesindeyse sunucu onu göremez; siz de her adrese elle klasör adı eklemek zorunda kalırsınız.
Çözümün iki yolu var ve ikisi birbirinin yerine geçmez. Birincisi kök dizini kalıcı olarak taşımak, ikincisi .htaccess ile isteği içeriden alt klasöre yönlendirmek. İkinci yöntem beş dakikada uygulanır ama arkasında iki ciddi yan etki bırakır: URL'lerde çift adres oluşur ve proje kök dizininizdeki gizli dosyalar internete açık kalır. Aşağıda her iki yolu, hangi senaryoda hangisinin doğru olduğunu ve değişiklik sonrası kontrol etmeniz gerekenleri anlatıyorum.
Document Root Tam Olarak Nedir?#
Document root, bir alan adına gelen isteğin diskte hangi klasörden karşılanacağını belirleyen tek ayardır. Apache'de DocumentRoot, Nginx'te root direktifiyle tanımlanır. Ziyaretçi ornek.com/urunler/kalem.html istediğinde sunucu bu yolu kök dizinin altına ekler ve /home/kullanici/public_html/urunler/kalem.html dosyasını arar.
Kritik olan şu: kök dizinin üstünde kalan hiçbir dosyaya web üzerinden erişilemez. Bu bir kısıtlama değil, güvenlik özelliğidir. Veritabanı parolanızı tutan .env dosyasını, vendor/ klasörünü veya günlük kayıtlarınızı kök dizinin bir üstüne koyduğunuzda, biri adresi tahmin etse bile sunucu o dosyayı servis etmez. Modern çatıların (Laravel, Symfony, Slim) neden public/ diye ayrı bir klasör kullandığının cevabı budur: yalnızca o klasör dışarı açılır, gerisi diskte durur ama erişilemez.
Kök dizinin nerede olduğunu görmek için cPanel'de Domains ekranındaki "Document Root" sütununa bakabilir ya da SSH erişiminiz varsa bir PHP dosyasıyla doğrudan sorabilirsiniz:
<?php
echo $_SERVER['DOCUMENT_ROOT'];
Kendi sunucunuzu yönetiyorsanız yapılandırma dosyasından da okuyabilirsiniz:
# Apache
grep -R "DocumentRoot" /etc/apache2/sites-enabled/ /etc/httpd/conf.d/ 2>/dev/null
# Nginx
grep -R "root " /etc/nginx/sites-enabled/ /etc/nginx/conf.d/ 2>/dev/null
Kalıcı Değişiklik mi, .htaccess Hilesi mi?#
İki yöntem arasındaki farkı önden görmek, sonradan geri dönmekten çok daha ucuz:
| Ölçüt | Kalıcı kök dizin değişikliği | .htaccess yönlendirme |
|---|---|---|
| Uygulama süresi | Panel değişikliği veya destek talebi | Bir dosya, birkaç dakika |
| Kök üstündeki dosyalar korunur mu | Evet | Hayır, açıkta kalır |
| Çift URL sorunu | Yok | / ve /public/ ikisi de açılır |
| Ana alan adında uygulanabilir mi | Genelde yönetici erişimi gerekir | Evet |
| Sunucu yükü | Sıfır | Her istekte ek kural işlemesi |
| Taşınabilirlik | Sunucuya bağımlı | Proje ile birlikte taşınır |
| Önerilen kullanım | Kalıcı kurulumlar, üretim | Geçici test, yetki yoksa |
Özet kural şudur: kök dizinini değiştirebiliyorsanız değiştirin. .htaccess yöntemi, kök dizinine müdahale yetkiniz olmadığında başvurulan bir taviz çözümüdür ve bedelini güvenlik tarafında ödersiniz.
Yöntem 1: Kök Dizini Kalıcı Olarak Değiştirmek#
cPanel'de ek alan adı ve alt alan adı#
cPanel'in Domains ekranında yeni bir alan adı veya alt alan adı oluştururken kök dizinini doğrudan siz belirlersiniz. "Create A New Domain" adımında varsayılan olarak /home/kullanici/ornek.com gibi bir yol önerilir; bu kutuyu /home/kullanici/projeler/laravel/public gibi istediğiniz yolla değiştirebilirsiniz.
Var olan bir ek alan adının kök dizinini de aynı ekrandaki "Manage" bağlantısından güncelleyebilirsiniz. Ek alan adı ve park edilmiş alan adı kavramları karışıyorsa addon ve parked domain farkı yazısı ayrımı netleştirir; alt alan adı oluşturma adımları ise alt alan adı oluşturma yazısında.
Sık yapılan bir hata: kök dizini public_html dışına, ev dizininin herhangi bir yerine taşıdığınızda dosya sahipliği ve izinler doğru olmazsa 403 alırsınız. Klasörün kullanıcıya ait ve 0755 olduğundan emin olun.
Ana alan adının kök dizini#
cPanel arayüzü hesabın birincil alan adının kök dizinini değiştirmenize izin vermez. Bu değişiklik sunucu tarafında, yönetici erişimiyle yapılır. WHM'de kullanıcının sanal host tanımı /var/cpanel/userdata/ altında tutulur:
# İlgili dosyalar (SSL için ayrı bir kopya vardır)
/var/cpanel/userdata/kullanici/ornek.com
/var/cpanel/userdata/kullanici/ornek.com_SSL
Bu dosyalardaki documentroot satırı düzenlendikten sonra yapılandırmanın yeniden üretilmesi gerekir:
/usr/local/cpanel/scripts/rebuildhttpdconf
/usr/local/cpanel/scripts/restartsrv_httpd
Paylaşımlı hosting kullanıyorsanız bu adımları siz yapamazsınız; sağlayıcınıza "birincil alan adının kök dizinini /home/kullanici/proje/public yapar mısınız" diye açık bir talep açmanız yeterlidir. Talebi hangi klasörü istediğinizi tam yolla yazarak açın, gidip gelen mesaj sayısı yarıya iner.
Plesk ve doğrudan sunucu yapılandırması#
Plesk bu konuda daha esnektir: Websites & Domains → Hosting Settings ekranındaki "Document root" alanını doğrudan düzenleyip kaydedersiniz, ek bir işlem gerekmez.
Kendi VDS veya fiziksel sunucunuzu yönetiyorsanız değişiklik sanal host dosyasında yapılır. Apache tarafında:
<VirtualHost *:80>
ServerName ornek.com
ServerAlias www.ornek.com
DocumentRoot /var/www/proje/public
<Directory /var/www/proje/public>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
DocumentRoot satırını değiştirip ilgili <Directory> bloğunu güncellemeyi unutmayın; yalnızca birini değiştirmek 403 hatasının en yaygın sebebidir. Yapılandırmayı uygulamadan önce doğrulayın:
apachectl configtest
sudo systemctl reload apache2
Sanal host bloklarının yapısını daha ayrıntılı görmek isterseniz Apache virtual host tanımlama yazısı adım adım ilerliyor.
Nginx tarafında karşılığı root direktifidir:
server {
listen 80;
server_name ornek.com www.ornek.com;
root /var/www/proje/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
}
sudo nginx -t
sudo systemctl reload nginx
Yöntem 2: .htaccess ile Alt Klasöre Yönlendirme#
Kök dizinine dokunamıyorsanız, public_html içine bir .htaccess dosyası koyup gelen istekleri içeriden alt klasöre aktarabilirsiniz. Buradaki kritik nokta, yönlendirmenin dahili olmasıdır: tarayıcının adres çubuğu değişmez, ziyaretçi klasör adını görmez.
RewriteEngine On
# Zaten hedef klasörün içindeysek tekrar ekleme (sonsuz döngü koruması)
RewriteCond %{REQUEST_URI} !^/public/
# Her isteği public klasörünün altına taşı
RewriteRule ^(.*)$ public/$1 [L]
İkinci satır kritik. RewriteCond olmadan kural kendi çıktısını yeniden işler ve public/public/public/... biçiminde bir döngüye girip 500 hatası üretir.
Alan adına göre ayrıştırmak isterseniz koşulu genişletebilirsiniz:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(www\.)?ornek\.com$ [NC]
RewriteCond %{REQUEST_URI} !^/public/
RewriteRule ^(.*)$ public/$1 [L]
Bu satırların yazımı ve .htaccess dosyasının nasıl oluşturulacağı konusunda tereddüdünüz varsa htaccess yönlendirme ile htaccess dosyası oluşturma yazıları temel kalıpları içeriyor. Dosyayı sunucuda oluşturmanın en pratik yolu genellikle cPanel dosya yöneticisi üzerinden "gizli dosyaları göster" seçeneğini açmaktır.
Sembolik bağlantı alternatifi#
SSH erişiminiz varsa bazen daha temiz bir yol vardır: public_html klasörünü silip yerine hedef klasöre bakan bir sembolik bağlantı koymak.
rm -rf ~/public_html
ln -s ~/projeler/laravel/public ~/public_html
Bu yöntem .htaccess kuralı gerektirmez ve URL'lerde iz bırakmaz. Ancak sunucuda SymLinksIfOwnerMatch gibi bir kısıt varsa veya hosting sağlayıcısı sembolik bağlantıları engelliyorsa çalışmaz; ayrıca bazı yedekleme araçları bağlantıyı takip etmeyip klasörü boş görebilir. Uygulamadan önce bir yedek alın.
.htaccess Hilesinin Sessiz Yan Etkileri#
Bu bölüm yazının en önemli kısmı, çünkü .htaccess yöntemini uygulayan çoğu kişi aşağıdakileri hiç fark etmez.
1. Gizli dosyalarınız internete açık kalır. Kök dizini hâlâ public_html'dir. Projenizi public_html/laravel/ altına koyup public_html/laravel/public/ klasörüne yönlendirme yaptıysanız, ornek.com/laravel/.env adresi tarayıcıdan doğrudan indirilebilir. Aynı şey composer.json, storage/logs/laravel.log ve .git/ klasörü için de geçerlidir. Bu, veritabanı parolanızın ve uygulama anahtarınızın açıkta olması demektir.
Yönlendirme yöntemini kullanmak zorundaysanız bu dosyaları ayrıca kapatın:
# Hassas dosya ve klasörleri kapat
RedirectMatch 404 /\.git
RedirectMatch 404 /\.env
RedirectMatch 404 /storage/logs
<FilesMatch "^(\.env|composer\.(json|lock)|package-lock\.json)$">
Require all denied
</FilesMatch>
Yine de bunun bir yama olduğunu unutmayın; gerçek çözüm dosyaları kök dizinin dışına almaktır.
2. Aynı sayfa iki adresten açılır. Yönlendirme dahilidir ama ornek.com/public/sayfa adresi de çalışmaya devam eder. Arama motoru her iki adresi de bulursa aynı içeriği iki URL'de indeksleyebilir. Çözüm, her sayfada doğru kanonik etiketi yayımlamak ve /public/ ile başlayan istekleri temiz adrese kalıcı olarak yönlendirmektir:
RewriteCond %{THE_REQUEST} \s/+public/([^\s]*) [NC]
RewriteRule ^ /%1 [R=301,L]
301 ile 302 arasındaki farkın neden burada önemli olduğunu 301 mi 302 mi yönlendirme yazısında bulabilirsiniz; kalıcı taşımada 301 kullanmak arama motorunun eski adresi bırakmasını sağlar.
3. Uygulama kendi yolunu yanlış hesaplayabilir. Bazı betikler dosya yollarını $_SERVER['DOCUMENT_ROOT'] üzerinden kurar. Kök dizini değişmediği için bu değer hâlâ public_html'i gösterir ve uygulama beklediği dosyayı bulamaz. Bu tip kurulumlarda mutlak yol yerine __DIR__ tabanlı hesaplama yapan sürümü tercih edin.
4. İç içe .htaccess çakışması. Hedef klasörün kendi .htaccess dosyası varsa (Laravel'in public/.htaccess dosyası gibi) iki kural kümesi arka arkaya işlenir. Beklenmedik davranışlarda önce dıştaki kuralı geçici olarak yorum satırına alıp sorunun hangi dosyadan geldiğini ayırın.
Laravel'i Paylaşımlı Hostingte Doğru Yayınlamak#
Laravel'de en yaygın hata, tüm proje klasörünü public_html içine kopyalayıp .htaccess ile public/ klasörüne yönlendirmektir. Bu, yukarıdaki birinci maddeyi tetikler: .env dosyanız erişilebilir hale gelir.
Doğru kurulum şu şekildedir. Proje dosyalarını public_html'in dışına, örneğin /home/kullanici/laravel altına yükleyin. Sonra yalnızca public/ klasörünün içeriğini public_html içine taşıyın ve public_html/index.php dosyasındaki iki yolu güncelleyin:
require __DIR__.'/../laravel/vendor/autoload.php';
$app = require_once __DIR__.'/../laravel/bootstrap/app.php';
Ardından depolama bağlantısını yeniden kurmanız gerekir, çünkü php artisan storage:link varsayılan yolu artık geçerli değildir:
cd ~/laravel
rm -f ~/public_html/storage
ln -s ~/laravel/storage/app/public ~/public_html/storage
Klasör izinlerini de kontrol edin; storage ve bootstrap/cache yazılabilir olmalıdır:
chmod -R 775 ~/laravel/storage ~/laravel/bootstrap/cache
Bu düzende kök dizini public_html olarak kalır ama proje dosyaları zaten dışarıdadır; yani hem yetkiye ihtiyaç duymazsınız hem de güvenlik açığı oluşmaz. Kök dizinini değiştirebiliyorsanız daha da temizi, hiçbir dosyayı taşımadan kök dizini doğrudan /home/kullanici/laravel/public yapmaktır.
React, Vue ve Build Çıktısını Yayınlamak#
Vite veya benzeri bir araçla derlenen projelerde çıktı dist/, Create React App tabanlı projelerde build/ klasörüne düşer. Burada üç seçeneğiniz var:
- Kök dizini
distyapın. En temiz yöntem. Ek alan adı veya alt alan adı oluştururken kök dizinini doğrudandistklasörüne verirsiniz. - Build çıktısını
public_htmliçine kopyalayın. Yayınlama adımınıza tek satır ekleyerek çözülür ve yetki gerektirmez. - Alt dizin altında yayınlayın. Site gerçekten
ornek.com/uygulama/altında duracaksa, derleme yapılandırmasında temel yolu belirtmeniz gerekir; aksi halde varlıklar/assets/...isteyip 404 alır.
# Vite: alt dizinde yayın için temel yol
npm run build -- --base=/uygulama/
Tek sayfalık uygulamalarda ikinci bir ayrıntı daha var: istemci tarafı yönlendirme kullanıyorsanız, ornek.com/hakkimizda adresini yenilediğinizde sunucu diskte böyle bir dosya bulamaz ve 404 döner. Tüm istekleri index.html dosyasına yönlendirmeniz gerekir:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.html [L]
Node.js tarafında çalışan bir uygulama yayınlıyorsanız durum farklıdır; orada kök dizini değil uygulama kökü ve başlangıç dosyası tanımlanır. Ayrıntılar için Node.js uygulamasını cPanel'de yayınlama yazısına bakabilirsiniz.
Tek Hesapta Birden Fazla Proje Barındırmak#
Aynı hosting hesabında birkaç projeyi ayrı ayrı yayınlamak istiyorsanız, .htaccess hileleri üst üste bindirmek yerine her projeye kendi alan adını veya alt alan adını verin. Önerilen düzen şudur:
/home/kullanici/
├── public_html/ → ornek.com (ana site)
├── projeler/
│ ├── panel/public/ → panel.ornek.com (alt alan adı)
│ ├── api/public/ → api.ornek.com (alt alan adı)
│ └── magaza/dist/ → magaza.com.tr (ek alan adı)
Her alt alan adı oluştururken kök dizinini ilgili public veya dist klasörüne verirsiniz. Bu düzenin üç avantajı var: projeler birbirinin .htaccess kurallarını etkilemez, her biri kendi SSL sertifikasını alır ve bir projeyi silmek diğerlerine dokunmaz.
Var olan bir WordPress kurulumunu alt dizinden köke taşıma senaryosu ise ayrı bir konudur, çünkü orada veritabanındaki adreslerin de güncellenmesi gerekir; bunun için WordPress'i alt dizinden taşıma yazısı adım adım ilerliyor.
Değişiklik Sonrası Kontrol Listesi ve Sık Hatalar#
Kök dizinini değiştirdikten sonra beş dakikada aşağıdakileri doğrulayın:
- Ana sayfa açılıyor mu, adres çubuğunda klasör adı görünüyor mu?
- Bir alt sayfayı yenilediğinizde 404 alıyor musunuz?
ornek.com/.envveornek.com/.git/configadresleri 403 ya da 404 dönüyor mu?- SSL sertifikası hâlâ geçerli mi? Bazı panellerde kök dizin değişikliği sonrası doğrulama dosyasının yolu değiştiği için sertifika yenilemesi başarısız olabilir.
- Yükleme klasörleri (görseller, ekler) hâlâ erişilebilir mi?
Sık karşılaşılan üç hata ve sebepleri:
| Belirti | Muhtemel sebep | Çözüm |
|---|---|---|
| 500 Internal Server Error | Yeniden yazma kuralı sonsuz döngüye girdi | RewriteCond %{REQUEST_URI} !^/klasor/ koşulunu ekleyin |
| 403 Forbidden | Klasör izni yanlış veya <Directory> bloğu güncellenmedi | Klasörü 0755 yapın, sanal host bloğunu kontrol edin |
| Dizin listesi görünüyor | Kök dizinde index.php/index.html yok | Doğru klasörü hedeflediğinizden emin olun, Options -Indexes ekleyin |
Son bir uyarı: .htaccess üzerinde çalışırken her zaman dosyanın bir kopyasını alın. Yanlış tek bir satır tüm siteyi 500 hatasına düşürür ve bu durumda sitenin kendisinden geri dönemezsiniz; dosyaya FTP veya panel üzerinden erişmeniz gerekir. Değişikliği canlıya almadan önce mümkünse bir alt alan adında deneyin.
Sıkça Sorulan Sorular#
Ana alan adımın kök dizinini cPanel'den neden değiştiremiyorum?#
Birincil alan adının kök dizini hesap oluşturulurken sunucu tarafında sabitlenir ve cPanel arayüzü bu değeri düzenlemeye izin vermez. Değişiklik WHM veya sunucu yapılandırması üzerinden, yönetici yetkisiyle yapılır. Paylaşımlı hosting kullanıyorsanız sağlayıcınıza istediğiniz tam klasör yolunu yazarak bir destek talebi açmanız en hızlı yoldur. Ek alan adları ve alt alan adları için ise bu kısıt yoktur.
.htaccess ile yönlendirme yaparsam SEO'm zarar görür mü?#
Dahili yönlendirme adres çubuğunu değiştirmediği için tek başına zarar vermez. Asıl risk, aynı içeriğin hem /sayfa hem /public/sayfa adresinden açılabilir kalmasıdır. Bunu her sayfada doğru kanonik etiketi yayımlayarak ve /public/ ile başlayan istekleri 301 ile temiz adrese yönlendirerek çözersiniz. Bu iki önlemi almazsanız arama motoru hangi adresi tercih edeceğine kendi karar verir.
Kök dizinini değiştirdikten sonra SSL sertifikam bozuldu, ne yapmalıyım?#
Otomatik sertifika yenilemesi genellikle kök dizin altında bir doğrulama dosyası oluşturur. Kök dizin değişince bu yol geçersiz kalır ve yenileme başarısız olur. Panelinizden sertifikayı elle yeniden düzenleyin; cPanel kullanıyorsanız AutoSSL'i tekrar çalıştırmak çoğu durumda yeterlidir. Yeni kök dizinin altında .well-known klasörünün oluşturulabildiğinden ve bir yeniden yazma kuralıyla engellenmediğinden emin olun.
Sembolik bağlantı yöntemi paylaşımlı hostingte çalışır mı?#
Bazı sunucularda çalışır, bazılarında engellenir. Apache'nin SymLinksIfOwnerMatch ayarı açıksa bağlantı ile hedefin aynı kullanıcıya ait olması gerekir; bazı sağlayıcılar güvenlik gerekçesiyle sembolik bağlantıları tamamen kapatır. Denemeden önce mevcut public_html klasörünüzün yedeğini alın, çünkü yöntem klasörü silmenizi gerektirir. Çalışmazsa bağlantıyı kaldırıp klasörü geri koymanız yeterlidir.
Laravel projemi olduğu gibi public_html içine yüklersem ne olur?#
Site çalışır ama .env dosyanız, composer.json dosyanız ve storage/logs altındaki kayıt dosyalarınız tarayıcıdan doğrudan indirilebilir hale gelir. Bu, veritabanı parolanızın ve uygulama anahtarınızın açıkta olması demektir. Doğru yol, proje dosyalarını public_html dışına koyup yalnızca public klasörünün içeriğini web'e açmak ve index.php içindeki iki yolu güncellemektir.
Alt dizinde çalışan siteyi köke taşıdıktan sonra eski adresler ne olacak?#
Eski adresleri kalıcı yönlendirmeyle yeni adreslere bağlamanız gerekir; aksi halde arama sonuçlarındaki ve dış sitelerdeki bağlantılar 404 döner. .htaccess içinde /eski-klasor/ ile başlayan istekleri 301 ile karşılığına yönlendiren bir kural yazın. WordPress gibi veritabanında adres tutan sistemlerde ayrıca içerik içindeki bağlantıların da güncellenmesi gerekir.