npm run build yerelde sorunsuz tamamlandı, .next klasörü oluştu. FTP ile bunu public_html içine attınız, alan adını açtınız ve karşınıza ya "Index of /" dizin listesi ya da bembeyaz bir sayfa çıktı. Bir arama sonrası "cPanel'de Setup Node.js App var" bilgisine ulaşıp uygulamayı oradan tanımladınız; bu kez de 503 Service Unavailable ya da sonsuza kadar dönen bir yükleme ikonu.
Bu noktada aranan cevap genelde "hangi ayarı kaçırdım" oluyor. Oysa sorun çoğu zaman bir ayar değil, mimari bir uyumsuzluk: paylaşımlı hosting hesapları HTML, CSS, JavaScript ve PHP servis etmek üzere kurgulanmıştır. Next.js'in sunucu tarafı ise sürekli ayakta duran bir Node.js süreci ister — kalıcı bellek, bir port ve süreç yöneticisi. Ekonomik paylaşımlı paketlerin çoğunda bunların hiçbiri yoktur.
Bu rehber önce dürüst cevabı verir: hangi Next.js projesi paylaşımlı hostingde çalışır, hangisi hiçbir ayarla çalışmaz. Ardından iki yol ayrımını anlatır — projeyi statik export edilebilir hale getirip cPanel'e yüklemek, ya da Node destekli bir pakete veya sanal sunucuya geçmek. Aradaki kararı verebilmeniz için hangi Next.js özelliğinin export'u imkânsız kıldığını madde madde çıkarıyoruz.
Kısa Cevap: Hangi Next.js Projesi Paylaşımlı Hostingde Çalışır?#
Karar tek bir soruya iner: projeniz derleme anında (build time) tamamen HTML dosyalarına dönüştürülebiliyor mu?
| Proje tipi | Paylaşımlı hosting | Node destekli paket | VDS / sunucu |
|---|---|---|---|
| Tamamen statik sayfalar (blog, kurumsal site, dokümantasyon) | Çalışır | Çalışır | Çalışır |
Statik sayfa + istemci tarafı veri çekme (harici API'ye fetch) | Çalışır | Çalışır | Çalışır |
generateStaticParams ile üretilmiş dinamik rotalar | Çalışır | Çalışır | Çalışır |
| API route / Route Handler (kendi backend'i) | Çalışmaz | Çalışır | Çalışır |
| Server Actions (form gönderimi sunucuda) | Çalışmaz | Çalışır | Çalışır |
| SSR — her istekte sunucuda render | Çalışmaz | Çalışır | Çalışır |
ISR / revalidate ile arka planda yenileme | Çalışmaz | Çalışır | Çalışır |
middleware.ts | Çalışmaz | Çalışır | Çalışır |
next/image varsayılan optimizasyon | Çalışmaz | Çalışır | Çalışır |
Tablonun özeti şu: statik içerik üreten bir Next.js sitesi paylaşımlı hostingde mükemmel çalışır; kendi sunucu tarafını kullanan bir Next.js uygulaması ise çalışmaz. İkinci grup için ayar aramak zaman kaybıdır.
Neden SSR ve API Route'ları Paylaşımlı Hostingde Çalışmaz?#
Paylaşımlı hosting hesabınızda istek şu yolu izler: Apache ya da LiteSpeed gelen HTTP isteğini alır, adres bir dosyaya karşılık geliyorsa onu gönderir, .php ise PHP yorumlayıcısını çağırıp çıktısını döndürür. Süreç isteğin sonunda biter, bellekte hiçbir şey kalmaz.
Next.js'in sunucu tarafı bunun tersini ister:
- Kalıcı süreç.
next startbir Node.js sürecidir ve site ayakta olduğu sürece çalışır. Paylaşımlı hesaplarda uzun ömürlü süreçler genellikle birkaç dakika içinde sonlandırılır. - Port dinleme. Node uygulaması bir portu dinler (varsayılan 3000). Paylaşımlı hesaplarda rastgele port açma yetkiniz yoktur.
- Reverse proxy. Dinlenen portu 443'e bağlayacak bir ters vekil katmanı gerekir; bu yapılandırma sunucu yöneticisine aittir.
- Bellek payı. Node süreci boşta bile 80-150 MB tutar. PHP tabanlı paketlerin süreç ve bellek limitleri bu senaryoya göre boyutlandırılmamıştır.
Bu maddelerin hiçbiri "kötü hosting" göstergesi değil; sadece farklı bir çalışma modeli. .next klasörünü public_html içine kopyalamak da bu yüzden hiçbir zaman işe yaramaz: o klasör tarayıcıya sunulacak bir site değil, Node sürecinin okuyacağı bir derleme çıktısıdır.
Tek istisna, cPanel'in Setup Node.js App aracıdır. Passenger üzerinden Node uygulaması ayağa kaldırır ve gerçekten çalışır — ama her pakette bulunmaz, bellek limitleri düşüktür ve sharp gibi native modüllerde sorun çıkarabilir. Bu yolu değerlendiriyorsanız cPanel'de Node.js uygulaması yayınlama yazısı kurulumun tamamını anlatıyor.
Projeniz Statik Export Edilebilir mi? Kontrol Listesi#
Karar vermeden önce projeyi tarayın. Aşağıdaki tablo, output: 'export' ile uyumsuz olan özellikleri ve pratik alternatiflerini listeliyor:
| Özellik | Export ile uyumlu mu | Alternatif |
|---|---|---|
middleware.ts | Hayır | Yönlendirmeleri .htaccess'e taşıyın |
Server Actions ("use server") | Hayır | Formu harici bir API'ye veya form servisine gönderin |
| Route Handler / API route (dinamik) | Hayır | Ayrı bir backend, serverless fonksiyon veya harici servis |
cookies(), headers() (sunucu tarafında) | Hayır | İstemci tarafında document.cookie ile okuyun |
revalidate / ISR | Hayır | Her içerik değişiminde yeniden build alın |
export const dynamic = 'force-dynamic' | Hayır | Veriyi istemcide çekin |
generateStaticParams olmayan dinamik rota | Hayır | Parametreleri build anında üretin |
| Draft / Preview Mode | Hayır | CMS önizlemesini ayrı ortamda çalıştırın |
next/image varsayılan loader | Hayır | images.unoptimized: true veya harici loader |
next.config içindeki rewrites, redirects, headers | Hayır | .htaccess kuralları |
generateStaticParams ile dinamik rota | Evet | — |
İstemci bileşenleri, useEffect ile veri çekme | Evet | — |
generateMetadata (statik veriyle) | Evet | — |
| CSS Modules, Tailwind, sass | Evet | — |
Taramayı elle yapmayın; birkaç komut yeterli:
# Middleware var mı?
ls middleware.ts middleware.js 2>/dev/null
# Server Actions kullanılıyor mu?
grep -rn '"use server"' app/ src/ 2>/dev/null
# Sunucu tarafı istek başlıkları okunuyor mu?
grep -rn "next/headers" app/ src/ 2>/dev/null
# Dinamik render zorlanıyor mu?
grep -rn "force-dynamic\|revalidate" app/ src/ 2>/dev/null
# API route klasörleri
find app -type d -name api 2>/dev/null
Çıktıların hepsi boşsa proje büyük olasılıkla export edilebilir. Değilse, iki seçenek var: ilgili özellikleri istemci tarafına taşıyıp export'a uygun hale getirmek, ya da Node çalıştıran bir ortama geçmek. Kararı verirken paylaşımlı hosting ile VPS farkı karşılaştırması maliyet tarafını netleştirir.
Statik Export Yapılandırması: next.config Ayarları#
Next.js 13.3'te next export komutu yumuşak biçimde kullanımdan kaldırıldı, Next.js 14'te ise tamamen silindi. Artık export bir komut değil, bir yapılandırma seçeneğidir. Hâlâ next export yazan rehberler görürseniz o içerik eskimiştir.
// next.config.mjs
const nextConfig = {
output: 'export',
images: {
unoptimized: true,
},
trailingSlash: true,
};
export default nextConfig;
Üç ayarın da bir gerekçesi var:
output: 'export'—next buildkomutunun sonundaout/klasörünü üretir. Bu klasör doğrudan yüklenebilir statik siteyi içerir.images.unoptimized: true— Varsayılan görsel optimizasyonu çalışma anında bir sunucu ister. Bu ayar olmadan build, "default loader export ile uyumlu değil" benzeri bir hatayla durur.trailingSlash: true— Bu ayarla/hakkimizdarotasıout/hakkimizda/index.htmlolarak yazılır. Apache dizin isteklerindeindex.htmldosyasını otomatik sunduğu için ekstra kural gerekmeden çalışır. Kapalıyken çıktıout/hakkimizda.htmlolur ve uzantısız adres için.htaccesstarafındaMultiViewsya da açık yeniden yazma kuralı gerekir.
Ardından derleyin:
npm ci
npm run build
ls out
out klasörü oluşmadıysa build çıktısındaki uyarıları okuyun; Next.js hangi sayfanın export'u engellediğini genellikle dosya yoluyla birlikte söyler.
out Klasörünü cPanel'e Yükleme#
Buradan sonrası, herhangi bir statik siteyi yayınlamaktan farksızdır — süreç HTML sitesini hostinge yükleme yazısındakiyle aynıdır.
outklasörünün içeriğini zip'leyin. Klasörün kendisini değil; aksi halde sitesiteniz.com/out/altında kalır.- cPanel → File Manager →
public_htmliçine girin, varsa eski dosyaları temizleyin. - Zip'i yükleyin ve Extract ile açın.
index.html,_next/klasörü ve404.htmldosyasınınpublic_htmliçinde, kök seviyede durduğunu doğrulayın.- Alan adını açın ve tarayıcı konsolunu kontrol edin.
Yüklemeyi terminalden yapıyorsanız rsync hem daha hızlı hem de silinenleri temizler:
rsync -avz --delete out/ kullanici@sunucu:/home/kullanici/public_html/
--delete bayrağı hedefte olup kaynakta olmayan dosyaları siler. Eski build'in artık kullanılmayan _next/static dosyalarının birikip inode kotanızı doldurmasını bu bayrak engeller.
Alt Dizin veya Ek Alan Adına Kurulum#
Siteyi siteniz.com/uygulama gibi bir alt yolda yayınlayacaksanız basePath ayarı zorunludur; yoksa tüm CSS ve JS dosyaları kökten aranır ve 404 döner.
const nextConfig = {
output: 'export',
basePath: '/uygulama',
assetPrefix: '/uygulama',
images: { unoptimized: true },
trailingSlash: true,
};
404 Sayfası ve Önbellek Kuralları#
Next.js export'u 404.html üretir ama Apache'nin bunu kullanacağını bilmesi gerekir. public_html/.htaccess dosyasına şunu ekleyin:
ErrorDocument 404 /404.html
# Hash'li varlıklar uzun süre önbelleklenebilir
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 month"
</IfModule>
# HTML dosyaları önbelleklenmemeli, yoksa deploy görünmez
<FilesMatch "\.html$">
Header set Cache-Control "no-cache, must-revalidate"
</FilesMatch>
_next/static altındaki dosya adları içerik özetini taşır; her build'de değişirler, bu yüzden bir yıl önbelleklenmeleri güvenlidir. HTML dosyaları ise aynı adı koruduğu için önbelleklenirse yeni sürüm ziyaretçiye günlerce ulaşmaz. Kural yazımının ayrıntıları için .htaccess yönlendirme ve kural yazımı yazısına bakabilirsiniz.
Dinamik Rotalar: generateStaticParams Olmadan Export Çalışmaz#
app/blog/[slug]/page.tsx gibi bir rotanız varsa, Next.js hangi slug değerleri için HTML üreteceğini bilemez ve build şu türde bir hatayla durur: "Page /blog/[slug] is missing generateStaticParams() so it cannot be used with output: export."
Çözüm, olası değerleri build anında listelemektir:
// app/blog/[slug]/page.tsx
export const dynamicParams = false;
export async function generateStaticParams() {
const yazilar = await fetch('https://cms.siteniz.com/api/yazilar')
.then((r) => r.json());
return yazilar.map((yazi: { slug: string }) => ({ slug: yazi.slug }));
}
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
return <article>{slug}</article>;
}
dynamicParams = false ayarı, listede olmayan bir slug istendiğinde 404 döndürülmesini sağlar. Export modunda listede olmayan bir sayfa zaten üretilmez; bu satır davranışı açık hale getirir.
Bu yaklaşımın doğal sonucu şudur: her yeni içerik için yeniden build almanız gerekir. Günde birkaç yazı yayınlanan bir blogda bu kabul edilebilir, ama kullanıcıların anlık içerik ürettiği bir platformda değildir. İkinci durumda export doğru araç değildir.
Görseller: next/image Neden Sorun Çıkarıyor?#
next/image bileşeni normalde çalışma anında görselleri yeniden boyutlandırır ve WebP/AVIF'e çevirir. Bu işi yapan bir sunucu olmadığı için export modunda kapatılması gerekir. unoptimized: true ayarı bileşeni çalışır durumda tutar, ama görselleri olduğu gibi servis eder — yani boyutlandırmayı derleme öncesinde siz halletmelisiniz.
Pratikte üç seçenek var:
| Yöntem | Nasıl çalışır | Ne zaman tercih edilir |
|---|---|---|
unoptimized: true | Görseller olduğu gibi servis edilir | Görsel sayısı az, boyutlar zaten optimize |
| Harici loader (Cloudinary, imgix vb.) | URL üzerinden boyutlandırma | Görsel yoğun siteler |
Build öncesi optimizasyon (sharp betiği) | WebP çıktıları repoya girer | Tam kontrol istendiğinde |
Görselleri bir CDN üzerinden servis etme fikrini değerlendiriyorsanız CDN mi daha iyi yoksa hosting mi yazısı ne zaman gerçekten fark yarattığını anlatıyor.
Nuxt, Vite ve Create React App İçin Karşılığı#
Aynı mantık diğer framework'lerde de geçerlidir: derleme çıktısı statik dosyalarsa paylaşımlı hosting yeter.
| Framework | Komut | Çıktı klasörü | Not |
|---|---|---|---|
| Next.js | next build (output: 'export') | out/ | SSR/API varsa export edilemez |
| Nuxt | nuxt generate | .output/public/ | nuxt build çıktısı Node ister |
| Vite (React/Vue) | vite build | dist/ | Varsayılan olarak tamamen statik |
| Create React App | react-scripts build | build/ | Varsayılan olarak tamamen statik |
| Astro | astro build | dist/ | Adaptör eklenmediyse statik |
Vite ve CRA gibi tek sayfa uygulamalarında ek bir adım vardır: yönlendirme tarayıcı tarafında yapıldığı için /hakkimizda adresine doğrudan girildiğinde sunucu böyle bir dosya bulamaz ve 404 verir. Çözüm, var olmayan yolları index.htmle yönlendirmektir:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
Next.js statik export'unda bu kural gerekmez, çünkü her rota için gerçek bir HTML dosyası üretilir. Bu, export'un tek sayfa uygulamasına göre SEO avantajıdır.
Export Yetmiyorsa: Node Destekli Hosting mi, VDS mi?#
Projeniz sunucu tarafına gerçekten ihtiyaç duyuyorsa iki seçenek kalır.
| Kriter | cPanel Node.js App (Passenger) | VDS / sanal sunucu |
|---|---|---|
| Kurulum zorluğu | Düşük, arayüzden | Orta, komut satırı bilgisi ister |
| Bellek | Paket limitiyle sınırlı | Ayrılan RAM kadar |
| Node sürümü seçimi | Panelin sunduğu sürümler | İstediğiniz sürüm |
Native modüller (sharp vb.) | Sorun çıkarabilir | Sorunsuz |
| Süreç yönetimi | Passenger yönetir | pm2 veya systemd |
| SSL | AutoSSL ile otomatik | Let's Encrypt kurulumu |
| Root erişimi | Yok | Var |
Sanal sunucu tarafında tipik kurulum şöyle işler: uygulama pm2 ile arka planda ayağa kaldırılır, Nginx da 443 portundan gelen istekleri uygulamanın portuna aktarır.
npm ci --omit=dev
npm run build
pm2 start npm --name "site" -- start
pm2 save
pm2 startup
Süreç yönetiminin ayrıntıları için pm2 ile Node.js süreç yönetimi, önündeki vekil katmanı için Nginx reverse proxy yapılandırması yazıları doğrudan bu adımları anlatıyor.
Sık Karşılaşılan Hatalar ve Çözümleri#
| Belirti | Sebep | Çözüm |
|---|---|---|
| Build "missing generateStaticParams" diyor | Dinamik rota, parametre listesi yok | generateStaticParams ekleyin |
| "Image Optimization ... not compatible with export" | Varsayılan görsel loader | images.unoptimized: true |
| "Middleware cannot be used with output: export" | middleware.ts var | Dosyayı kaldırın, kuralları .htaccess'e taşıyın |
| Ana sayfa açılıyor, alt sayfalar 404 | trailingSlash kapalı | trailingSlash: true + yeniden build |
| Sayfa geliyor, CSS/JS 404 | Alt dizine kurulum, basePath yok | basePath ve assetPrefix tanımlayın |
| Site eski hâlinde kalıyor | HTML önbelleklenmiş | HTML için no-cache başlığı ekleyin |
| Form gönderilince hiçbir şey olmuyor | Server Action export'ta çalışmaz | Harici API veya form servisi kullanın |
out klasörü hiç oluşmuyor | output: 'export' yok / build hata verdi | Konfigürasyonu ve build çıktısını kontrol edin |
Karar sürecini tek cümleye indirgemek gerekirse: projenizde bir tane bile sunucu tarafı özellik varsa ve bunu kaldıramıyorsanız, paylaşımlı hostingte harcadığınız her dakika kayıptır. Buna karşılık tamamen statik bir Next.js sitesi, paylaşımlı bir pakette hem hızlı hem de son derece ucuza çalışır; çünkü sunulan şey düz HTML dosyalarından ibarettir.
Sıkça Sorulan Sorular#
.next klasörünü doğrudan public_html içine yükleyebilir miyim?#
Hayır. .next klasörü tarayıcıya sunulacak bir site değil, Node.js sürecinin okuyacağı derleme çıktısıdır; içinde sunucu tarafı paketler ve manifest dosyaları bulunur. Paylaşımlı hostingte bu klasörü yüklemek dizin listesi ya da boş sayfa dışında bir sonuç vermez. Yüklenmesi gereken klasör, output: 'export' ayarıyla üretilen out klasörüdür.
Statik export ile SEO performansım düşer mi?#
Aksine, genellikle iyileşir. Statik export her rota için gerçek bir HTML dosyası üretir; arama motoru sayfayı JavaScript çalıştırmadan okur ve sunucu tarafı gecikmesi olmadığı için yanıt süresi çok düşer. Meta etiketleri generateMetadata ile derleme anında yazıldığı için sayfa başına özel başlık ve açıklama da korunur.
Statik export edilen siteye iletişim formu nasıl eklerim?#
Sunucu tarafı olmadığı için formu harici bir uç noktaya göndermeniz gerekir. En yaygın üç yol: form gönderim servisleri, sitenizle aynı alan adında çalışan küçük bir PHP betiği, ya da ayrı bir alan adında barındırılan bir API. PHP betiği yöntemi paylaşımlı hostingte tamamen ücretsiz çalışır; yalnızca CSRF ve spam koruması eklemeyi unutmayın.
İçeriğim değiştiğinde her seferinde yeniden build almak zorunda mıyım?#
Evet, içerik derleme anında HTML'e gömüldüğü için yeni içeriğin yayına girmesi yeni bir build gerektirir. Bu yükü otomatikleştirebilirsiniz: CMS'iniz bir webhook tetikler, sürekli entegrasyon aracı npm run build çalıştırır ve out klasörünü sunucuya rsync ile gönderir. İçerik günde birkaç kez değişiyorsa bu akış rahatlıkla yeter.
cPanel'in Setup Node.js App aracıyla tam Next.js çalıştırabilir miyim?#
Bazı durumlarda evet. Passenger, uygulamayı ayağa kaldırır ve SSR ile API route'ları çalışabilir. Ancak bellek limitleri düşüktür, Node sürüm seçimi panelin sunduklarıyla sınırlıdır ve sharp gibi derlenmiş modüller sorun çıkarabilir. Küçük projeler için makul, trafik alan bir uygulama için sanal sunucu daha öngörülebilir bir tercihtir.
Nuxt veya Vite projem için de aynı kurallar geçerli mi?#
Büyük ölçüde evet. Belirleyici olan, derleme çıktısının statik dosyalar olup olmadığıdır. Nuxt'ta nuxt generate komutu .output/public klasörünü üretir ve bu klasör paylaşımlı hostingte çalışır; nuxt build çıktısı ise Node süreci ister. Vite ve Create React App projeleri varsayılan olarak statiktir, yalnızca yönlendirme için .htaccess kuralı eklemeniz gerekir.