Yerelde php artisan serve ile kusursuz çalışan proje, cPanel'e yüklendikten sonra ya bomboş bir dizin listesi ya da tek satırlık bir 500 Internal Server Error veriyor. İlk refleks arama kutusuna "laravel cpanel kurulum" yazmak oluyor ve karşınıza çıkan onlarca rehber neredeyse aynı şeyi söylüyor: projeyi olduğu gibi public_html içine at, public klasörünün içindekileri bir üst dizine taşı, index.php içindeki iki satırı düzelt, bitti.
Bu yöntem gerçekten çalışır. Site açılır, formlar gönderilir, hiçbir hata görünmez. Görünmeyen şey şudur: aynı anda https://siteniz.com/.env adresi de açılır ve tarayıcı veritabanı şifrenizi, APP_KEY değerinizi, varsa ödeme sağlayıcısı ve mail servisi anahtarlarınızı düz metin olarak indirir. Yanına storage/logs/laravel.log da gelir — içinde geçmiş hataların stack trace'leri, bazen kullanıcı e-postaları ve sorgu parametreleri vardır.
Bu rehber iki şeyi yapar: önce neden bu kadar yaygın bir tavsiyenin bu kadar riskli olduğunu somut olarak gösterir, sonra paylaşımlı bir cPanel hesabında Laravel'i doğru dizin yapısıyla kurmanın tam adımlarını verir. SSH erişiminiz varsa da yoksa da uygulanabilecek iki ayrı yol anlatılıyor.
Laravel Neden Doğrudan public_html'e Atılmaz?#
Laravel, "tek giriş noktası" (front controller) mimarisiyle çalışır. Web sunucusuna gelen her istek public/index.php dosyasına düşer; framework yönlendirmeyi, oturumu ve yanıtı oradan yönetir. Bu tasarımın doğal sonucu şudur: web kökünde sadece public klasörünün içeriği bulunmalıdır. Diğer her şey — uygulama kodu, konfigürasyon, bağımlılıklar, loglar — bir üst dizinde, tarayıcının hiç ulaşamadığı yerde durur.
Paylaşımlı hostingte web kökü genellikle /home/kullanici/public_html klasörüdür ve bu klasörün altındaki her dosya, aksi belirtilmedikçe, bir URL ile indirilebilir. Laravel projesinin tamamını buraya kopyaladığınızda aşağıdaki dosyalar da adreslenebilir hale gelir:
| Dosya / klasör | İçinde ne var | Sızarsa sonucu |
|---|---|---|
.env | DB şifresi, APP_KEY, API anahtarları | Veritabanına doğrudan erişim, oturum çerezlerinin taklit edilmesi |
storage/logs/laravel.log | Hata izleri, sorgu parametreleri | Dosya yolları, tablo isimleri, bazen kullanıcı verisi |
composer.json / composer.lock | Paket sürümleri | Bilinen açığı olan sürümlerin tespiti |
database/migrations | Tam şema | Tablo ve kolon isimleri, saldırı yüzeyi haritası |
.git/ | Sürüm geçmişi | Silinmiş şifrelerin eski commit'lerden çıkarılması |
.env.backup, .env.example | Değişken adları | Hangi servislerin kullanıldığı |
Bu liste teorik bir risk tarifi değil; internette otomatik tarayıcılar tam olarak bu yolları saniyeler içinde deneyerek gezinir. Yeni bir alan adı yayına alındıktan sonra ilk .env isteğinin gelmesi genellikle birkaç saati bulmaz.
"public Klasörünü public_html'e Kopyala" Tavsiyesi Ne Zarar Verir?#
Yaygın anlatımın iki varyantı var ve ikisi de aynı yere çıkıyor:
Varyant 1 — Her şeyi public_html içine atmak. Site siteniz.com/public/ gibi çirkin bir adresten açılır. Kaynak kodun tamamı web kökündedir.
Varyant 2 — Her şeyi public_html içine atıp public içeriğini bir üst dizine taşımak. URL düzelir, ancak app/, config/, vendor/, .env hâlâ public_html altındadır. Görüntü temizdir, risk aynıdır.
Kurulumunuzun hangi durumda olduğunu tahmin etmeyin, ölçün. Kendi sunucunuza şu isteği atın:
# 200 dönüyorsa .env dosyanız internete açıktır
curl -s -o /dev/null -w "%{http_code}\n" https://siteniz.com/.env
# Log dosyası ve Composer manifesti de kontrol edilmeli
curl -s -o /dev/null -w "%{http_code}\n" https://siteniz.com/storage/logs/laravel.log
curl -s -o /dev/null -w "%{http_code}\n" https://siteniz.com/composer.json
403 veya 404 beklenen cevaptır. 200 görüyorsanız kurulum aynı gün düzeltilmeli, ardından veritabanı şifresi ve tüm API anahtarları değiştirilmelidir — dosya bir kez indirildiyse geri alınamaz.
.htaccess ile Kapatmak Neden Yeterli Bir Çözüm Değil?#
Bu noktada akla gelen ilk çözüm, public_html/.htaccess dosyasına birkaç Deny kuralı yazmaktır:
<FilesMatch "^\.env|composer\.(json|lock)$">
Require all denied
</FilesMatch>
Bu kural işe yarar, ama tek başına güvenlik katmanı olarak kabul edilemez. Nedenleri:
- Tek dosyaya bağlı.
.htaccessyanlışlıkla silinir, bir eklenti tarafından üzerine yazılır veya yedekten geri yüklenirken kaybolursa koruma sessizce ortadan kalkar. Yanlış bir yönlendirme kuralı yazdığınızda site çöker; kural yanlış olduğunda ise hiçbir alarm vermez. - Liste tutmak gerekir.
.envengellersiniz,.env.bakunutulur.storage/kapatılır,bootstrap/cacheunutulur. - Sunucu yazılımına bağlıdır. Apache'nin
AllowOverrideayarı kısıtlıysa kurallarınız hiç okunmaz. LiteSpeed büyük ölçüde uyumludur ama Nginx tabanlı bir kurulumda.htaccesstamamen yok sayılır.
Dosyayı web kökünün dışına koymak ise bir yapılandırmaya değil, dizin yapısına dayanır. Yanlışlıkla "kapanması" mümkün değildir. .htaccess kurallarının nasıl yazıldığını merak ediyorsanız .htaccess yönlendirme kuralları yazısı ayrı bir konu olarak bunu ele alıyor.
Kuruluma Başlamadan Önce: Hosting Gereksinimleri#
Laravel'in bir paylaşımlı hesapta çalışıp çalışmayacağı, dosyaları yüklemeden önce beş dakikada anlaşılır. Sürüm gereksinimleri Laravel'in kendi sürümüne göre değişir: Laravel 11 ve 12 en az PHP 8.2, Mart 2026'da çıkan Laravel 13 ise en az PHP 8.3 ister (13.3 ve sonrası pratikte PHP 8.4 gerektiren Symfony bileşenleri getirir).
| Gereksinim | Nasıl kontrol edilir | Eksikse |
|---|---|---|
| PHP 8.2+ | cPanel → MultiPHP Manager | Alan adı için sürümü yükseltin |
mbstring, openssl, pdo_mysql, tokenizer, xml, ctype, json, bcmath, fileinfo, curl | cPanel → Select PHP Version → Extensions | Eksik olanı işaretleyin |
| SSH / Terminal | cPanel → Terminal veya SSH Access | Composer'ı yerelde çalıştırın |
symlink() fonksiyonu | php -r "var_dump(function_exists('symlink'));" | storage:link yerine gerçek klasör kullanın |
proc_open, proc_get_status | Composer sunucuda çalışacaksa gerekir | Yerelde vendor üretip yükleyin |
| Bellek limiti ≥ 256 MB | php -i | grep memory_limit | Composer sunucuda çalışmayabilir |
Kontrolleri terminalden yapmak en hızlısıdır:
# cPanel'de PHP CLI sürümü, web sürümünden FARKLI olabilir
php -v
/opt/cpanel/ea-php83/root/usr/bin/php -v
# Yüklü eklentileri listele
php -m | sort
# Composer var mı?
composer --version || php ~/composer.phar --version
Terminal sekmesini hiç görmediyseniz cPanel'de SSH erişimi ve terminal kullanımı yazısı erişimi açmayı anlatıyor. PHP sürümünü alan adı bazında değiştirme konusunda ise PHP sürüm yönetimi rehberi işinizi görür.
Doğru Dizin Yapısı: Proje Kökü public_html Dışında#
Hedeflediğimiz yerleşim şu:
/home/kullanici/
├── laravel-app/ <-- proje kökü, WEB'DEN ERİŞİLEMEZ
│ ├── app/
│ ├── bootstrap/
│ ├── config/
│ ├── database/
│ ├── resources/
│ ├── routes/
│ ├── storage/
│ ├── vendor/
│ ├── artisan
│ └── .env
└── public_html/ <-- web kökü, SADECE public içeriği
├── index.php (yolları düzeltilmiş)
├── .htaccess
├── robots.txt
├── favicon.ico
└── build/ (Vite çıktısı)
Ek alan adı (addon domain) kullanıyorsanız web kökü genellikle /home/kullanici/public_html/ikincisite.com olur. Mantık değişmez: proje kökü o klasörün dışında, örneğin /home/kullanici/ikincisite-app içinde durur.
Adım Adım Kurulum (SSH Erişimi Varsa)#
1. Projeyi sunucuya alın#
Git kullanıyorsanız en temiz yol doğrudan klonlamaktır. cPanel'in kendi Git arayüzü de aynı işi görür; ayrıntılar cPanel Git sürüm kontrolü yazısında.
cd ~
git clone https://github.com/kullanici/projem.git laravel-app
cd laravel-app
Git yoksa projeyi yerelde zip'leyip File Manager ile yükleyin ve ~/laravel-app içine çıkarın. Zip'e node_modules klasörünü koymayın; sunucuda hiçbir işe yaramaz ve inode kotanızı tüketir.
2. Bağımlılıkları kurun#
composer install --no-dev --optimize-autoloader
--no-dev bayrağı test ve geliştirme paketlerini atlar; bu hem yükleme süresini hem de disk ve inode kullanımını ciddi biçimde düşürür. Bellek hatası alırsanız:
php -d memory_limit=-1 ~/composer.phar install --no-dev --optimize-autoloader
3. .env dosyasını oluşturun ve anahtarı üretin#
cp .env.example .env
php artisan key:generate
chmod 600 .env
chmod 600 ikinci bir savunma katmanıdır: dosya zaten web kökünün dışındadır, ama aynı sunucudaki diğer hesaplardan okunmasını da engeller.
4. public içeriğini web köküne taşıyın#
# Varsa eski içeriği yedekleyin
mv ~/public_html ~/public_html_eski
mkdir ~/public_html
# Sadece public klasörünün İÇİNDEKİLERİ kopyalanır
cp -r ~/laravel-app/public/. ~/public_html/
Dikkat edin: public klasörünün kendisi değil, içeriği kopyalanıyor. Sondaki /. ifadesi gizli dosyaların (.htaccess) da kopyalanmasını sağlar; unutulursa yönlendirme kuralları gelmez ve ana sayfa dışındaki her adres 404 verir.
5. index.php içindeki yolları düzeltin#
Bu, tüm kurulumun kilit adımıdır. ~/public_html/index.php dosyasını açın. Laravel 11, 12 ve 13'te ilgili satırlar şöyle görünür:
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
require $maintenance;
}
require __DIR__.'/../vendor/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';
$app->handleRequest(Request::capture());
Proje kökü artık bir üst dizinde değil, kardeş dizinde olduğu için üç yolun da güncellenmesi gerekir. Yolu tek bir değişkende toplamak, ileride taşıma yaparken tek satır değiştirmenizi sağlar:
$base = __DIR__.'/../laravel-app';
if (file_exists($maintenance = $base.'/storage/framework/maintenance.php')) {
require $maintenance;
}
require $base.'/vendor/autoload.php';
$app = require_once $base.'/bootstrap/app.php';
$app->handleRequest(Request::capture());
Laravel 10 ve öncesindeyseniz dosyanın sonu $kernel = $app->make(Kernel::class); ile devam eder; o satırlara dokunmanız gerekmez, sadece yukarıdaki üç __DIR__ yolunu değiştirin.
6. İzinleri ayarlayın#
Paylaşımlı hostingte PHP, hesabınızın kullanıcısı olarak çalışır. Bu nedenle 777 vermeye gerek yoktur ve verilmemelidir; klasörü sunucudaki diğer süreçlere de açar.
cd ~/laravel-app
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;
chmod -R 775 storage bootstrap/cache
chmod 600 .env
chmod 755 ~/public_html
İzin bitlerinin ne anlama geldiğini tazelemek isterseniz Linux dosya izinleri yazısı sayı-harf karşılıklarını ayrıntılı anlatıyor.
7. storage bağlantısını kurun#
php artisan storage:link komutu, sembolik bağı public/storage altına oluşturur — ama sizin web kökünüz artık public değil. Bağı elle kurun:
ln -s /home/kullanici/laravel-app/storage/app/public /home/kullanici/public_html/storage
ls -l ~/public_html/storage
ls -l çıktısında ok işaretiyle hedef yol görünmelidir. Hedef yolu mutlak yazın; göreli yol (../laravel-app/...) File Manager üzerinden yapılan taşımalarda kolayca kırılır. Sunucuda symlink() kapalıysa alternatif, FILESYSTEM_DISK ayarını değiştirip yüklemeleri doğrudan public_html/uploads altına almaktır.
SSH Yoksa: Composer'sız Kurulum#
Ekonomik paketlerde terminal kapalı olabilir. Bu durumda ağır işleri yerelde yapıp sunucuya hazır çıktı gönderirsiniz:
- Yerelde
composer install --no-dev --optimize-autoloaderçalıştırın —vendor/klasörü oluşsun. - Yerelde
php artisan key:generate --showile bir anahtar üretin ve çıktıyı not edin. node_moduleshariç tüm projeyi zip'leyin.vendor/dahil olmalı.- cPanel File Manager ile zip'i
/home/kullanici/altına yükleyiplaravel-appklasörüne çıkarın. .envdosyasını File Manager'ın "Edit" özelliğiyle oluşturun;APP_KEYalanına 2. adımdaki değeri yapıştırın.publiciçeriğinipublic_htmle kopyalayın veindex.phpyollarını yukarıdaki gibi düzeltin.- Migration'ları çalıştıramayacağınız için şemayı yerelde
mysqldumpile alıp phpMyAdmin üzerinden içe aktarın.
Bu yöntemin tek dezavantajı, her güncellemede vendor klasörünü yeniden yüklemek zorunda kalmanızdır. Uzun vadede terminal erişimi olan bir pakete geçmek hem hızlı hem de daha az hataya açıktır.
Veritabanı ve Üretim Ayarları#
cPanel'de veritabanı ve kullanıcı adları hesap adınızla ön eklenir: kullanici_laravel gibi. Bu ön eki .env içine olduğu gibi yazın; kullanıcıyı veritabanına eklemeyi ve "ALL PRIVILEGES" vermeyi unutmayın. Bağlantı bilgilerini nereden alacağınız konusunda veritabanı bilgileri nereden bulunur yazısı kısa bir referans.
| Anahtar | Üretim değeri | Neden |
|---|---|---|
APP_ENV | production | Geliştirme uyarılarını ve bazı debug davranışlarını kapatır |
APP_DEBUG | false | Hata sayfasında dosya yolları ve .env içeriği gösterilmez |
APP_URL | https://siteniz.com | asset() ve url() yardımcılarının doğru adres üretmesi için |
LOG_LEVEL | error | Log dosyasının diski ve inode kotasını doldurmasını önler |
SESSION_DRIVER | database veya file | Paylaşımlı hostingte Redis genellikle yoktur |
QUEUE_CONNECTION | database | Daemon çalıştırılamadığı için Redis/SQS uygun değildir |
Ardından şema ve önbellek:
php artisan migrate --force
php artisan optimize
--force bayrağı, production ortamında onay sorusunu atlar; bu yüzden migration dosyalarınızın doğruluğundan emin olun. php artisan optimize komutu Laravel 11+ sürümlerinde config, route, view ve event önbelleklerini birlikte üretir. Her .env düzenlemesinden sonra php artisan config:clear çalıştırmayı unutmayın — önbellek üretildikten sonra .env okunmaz ve değişiklikleriniz görünmez hale gelir. Bu, kurulum sonrası en sık yaşanan kafa karışıklığıdır.
Zamanlanmış Görevler ve Kuyruklar: Paylaşımlı Hostingin Sınırları#
Laravel'in zamanlayıcısı tek bir cron kaydıyla çalışır. cPanel → Cron Jobs bölümüne dakikalık bir görev ekleyin ve web PHP'sini değil, CLI yolunu kullanın:
* * * * * /opt/cpanel/ea-php83/root/usr/bin/php /home/kullanici/laravel-app/artisan schedule:run >> /dev/null 2>&1
Kuyruklar farklı bir hikâye. php artisan queue:work kalıcı bir süreçtir; paylaşımlı hostingte uzun ömürlü süreçler genellikle birkaç dakika içinde sonlandırılır. Pratik çözüm, kuyruğu cron ile aralıklı boşaltmaktır:
*/5 * * * * /opt/cpanel/ea-php83/root/usr/bin/php /home/kullanici/laravel-app/artisan queue:work --stop-when-empty --max-time=240 >> /dev/null 2>&1
Horizon, Reverb, WebSocket sunucusu ve Octane paylaşımlı pakette çalışmaz — hepsi kalıcı süreç ister. Uygulamanız bunlara bağımlıysa doğru adım hosting ayarlarıyla uğraşmak değil, bir sanal sunucuya geçmektir.
Sık Karşılaşılan Hatalar ve Çözümleri#
| Belirti | Sebep | Çözüm |
|---|---|---|
| Bembeyaz sayfa, 500 | storage/logs yazılamıyor | chmod -R 775 storage bootstrap/cache |
No application encryption key has been specified | APP_KEY boş | php artisan key:generate, sonra config:clear |
Failed to open stream: vendor/autoload.php | index.php yolları düzeltilmemiş | 5. adımdaki $base değişikliği |
| Ana sayfa açılıyor, alt sayfalar 404 | .htaccess kopyalanmamış | cp ~/laravel-app/public/.htaccess ~/public_html/ |
| CSS/JS 404, karışık içerik uyarısı | APP_URL yanlış veya http:// | APP_URL düzelt + php artisan optimize:clear |
419 Page Expired | Session yazılamıyor | SESSION_DRIVER=database + sessions tablosu |
| Yüklenen görseller 404 | storage symlink yok/kırık | Mutlak yolla ln -s yeniden kur |
.env değişikliği etkisiz | Config önbelleği eski | php artisan config:clear |
Hata mesajını hiç göremiyorsanız kaynak, uygulama logu değil web sunucusu logudur. cPanel → Errors ekranı veya ~/logs altındaki dosyalar genellikle asıl PHP hatasını verir; cPanel hata kayıtlarını okuma yazısı bu ekranı ayrıntılı anlatıyor.
Son bir güvenlik kontrolü: kurulum bittikten sonra makalenin başındaki curl testlerini tekrar çalıştırın. .env, composer.json ve log dosyası artık 404 dönmelidir. Dönüyorsa yapı doğru; dönmüyorsa public_html içinde hâlâ proje dosyaları var demektir.
Sıkça Sorulan Sorular#
Laravel paylaşımlı hostingte gerçekten sorunsuz çalışır mı?#
Standart bir web uygulaması için evet. Yönlendirme, Blade şablonları, Eloquent, oturum yönetimi ve dosya yükleme paylaşımlı bir cPanel hesabında sorunsuz çalışır. Sınır, kalıcı süreç gerektiren bileşenlerdedir: Horizon, Reverb, Octane ve sürekli çalışan kuyruk işçileri paylaşımlı pakette desteklenmez. Trafiğiniz arttıkça PHP işlem limiti de darboğaza dönüşür.
Proje kökünü public_html dışına koyamıyorsam ne yapmalıyım?#
Bazı çok kısıtlı paketlerde ana dizine yazma yetkisi olmayabilir. Bu durumda proje kökünü public_html/uygulama gibi bir alt klasöre koyup o klasörü .htaccess ile tamamen kapatmak geçici bir çözümdür. Ancak koruma tek bir dosyaya bağlı kalır; kalıcı çözüm ana dizine yazma izni veren bir pakete geçmektir.
vendor klasörünü FTP ile yüklemek neden çok uzun sürüyor?#
vendor içinde on binlerce küçük dosya bulunur ve FTP her dosya için ayrı bir bağlantı turu yapar. Klasörü tek bir zip olarak yükleyip sunucuda çıkarmak süreyi dakikalara indirir. Ayrıca --no-dev bayrağıyla kurulum yapmak dosya sayısını gözle görülür biçimde azaltır ve inode kotanızı korur.
php artisan komutlarını terminal olmadan çalıştırabilir miyim?#
Doğrudan hayır. Bazı geliştiriciler bunun için geçici bir rota tanımlayıp Artisan::call() kullanır, ancak bu rota internete açık kalırsa ciddi bir güvenlik açığıdır. Daha güvenli yol, komutu cPanel'in Cron Jobs ekranından tek seferlik çalıştırıp ardından kaydı silmektir; çıktıyı e-posta olarak alabilirsiniz.
Deploy sonrası site eski hâlini gösteriyorsa ne yapmalıyım?#
Neredeyse her zaman önbellek kaynaklıdır. php artisan optimize:clear komutu config, route, view ve event önbelleklerini birlikte temizler. Sunucuda LiteSpeed ya da opcode önbelleği varsa cPanel üzerinden onu da sıfırlayın. Tarayıcı tarafında sert yenileme yapmadan önce sunucu tarafını temizlemek zaman kazandırır.
Laravel için hangi PHP sürümünü seçmeliyim?#
Laravel 11 ve 12 için PHP 8.2 alt sınırdır; Laravel 13 en az PHP 8.3 ister. Ancak alt sınırı seçmek yerine, hosting panelinizde bulunan en güncel kararlı sürümü tercih edin — güvenlik yamaları ve performans iyileştirmeleri oradadır. Sürümü değiştirdikten sonra gerekli eklentilerin yeni sürümde de işaretli olduğunu mutlaka kontrol edin.