Static export Next.js kamu jalan normal kalau diklik-klik, tapi membuka /about langsung atau refresh halaman malah 404. Penyebabnya ada di struktur file yang ditulis Next.js, dan solusinya cukup satu baris di next.config.js.
Solusi cepat
// next.config.js
module.exports = {
output: 'export',
trailingSlash: true,
}
Build ulang, deploy folder out/ yang baru, dan /about akan jalan di static file server mana pun.
Kenapa URL-nya 404
Secara default, export menulis satu file HTML per route, dengan nama sesuai route-nya:
out/
├── 404.html
├── about.html
├── blog/
│ └── hello.html
└── index.html
Klik <Link> jalan karena router Next.js melakukan navigasi di browser. Router mengambil file data route-nya (about.txt), bukan memuat /about sebagai halaman. Kunjungan langsung atau refresh mengirim request sungguhan ke /about. Static server biasa mencari file atau folder bernama about, tidak menemukan keduanya, lalu mengembalikan 404. Server tidak tahu bahwa /about seharusnya berarti about.html.
Ini hasil export default yang dilayani dengan python3 -m http.server:
/ 200
/about 404
/about/ 404
/about.html 200
/blog/hello 404
Apa yang diubah trailingSlash: true
Dengan trailingSlash: true, setiap route mendapat folder sendiri dengan index.html di dalamnya:
out/
├── 404.html
├── 404/
│ └── index.html
├── about/
│ └── index.html
├── blog/
│ └── hello/
│ └── index.html
└── index.html
Semua static server tahu harus melayani index.html untuk sebuah folder, jadi server Python yang sama sekarang mengembalikan:
/ 200
/about 301 → /about/
/about/ 200
/blog/hello 301 → /blog/hello/
/blog/hello/ 200
Server me-redirect /about ke /about/, lalu melayani halamannya. <Link> juga merender href="/about/", jadi link internal langsung mengarah ke URL kanonis tanpa redirect.
Blog ini juga berjalan sebagai static export Next.js dengan trailingSlash: true karena alasan yang sama.
Atau perbaiki di sisi server
Kalau kamu mau tetap memakai URL tanpa garis miring di akhir, biarkan config-nya dan ajari server untuk mencoba .html. Di nginx:
location / {
try_files $uri $uri.html $uri/ =404;
}
error_page 404 /404.html;
Hasilnya terhadap export default:
nginx biasa dengan try_files
/about 404 200
/blog/hello 404 200
/nope 404 404
/about/ dengan garis miring tetap 404 di sini, karena tidak ada folder about/. Kalau orang menautkan situs kamu dengan garis miring di akhir, trailingSlash: true adalah solusi yang lebih simpel.
Di S3 atau bucket lain tanpa aturan rewrite, cara ini tidak bisa dipakai, jadi pakai trailingSlash: true.
Kenapa jalan di lokal tapi rusak di production
npx serve out otomatis memetakan /about ke about.html. Terhadap export default, server ini mengembalikan 200 untuk /about, /about/, dan /blog/hello, jadi bug-nya tidak pernah muncul saat dites di lokal. Sebagian host melakukan pemetaan yang sama dan sebagian tidak. Sebelum deploy ke tempat baru, tes dengan server yang tidak melakukannya, misalnya python3 -m http.server dari dalam out/.
next export sudah dihapus
Jawaban lama menyuruh kamu menjalankan next build && next export. Di Next.js versi sekarang, command itu gagal:
`next export` has been removed in favor of 'output: export' in next.config.js.
Set output: 'export' di config lalu jalankan next build. Hasil export-nya ada di out/.
Kesalahan yang sering terjadi
Deploy folder out/ yang lama. trailingSlash mengubah file di disk, jadi kamu harus build ulang. Pastikan out/about/index.html ada sebelum upload.
Menulis link manual tanpa garis miring. <a href="/about"> biasa tetap jalan lewat redirect, tapi setiap klik butuh satu round trip tambahan. Pakai <Link>, atau tulis garis miringnya sendiri.
Mengubah setting di situs yang sudah live tanpa redirect. Pindah ke trailingSlash: true mengubah semua URL kanonis dari /about ke /about/. Mesin pencari menganggap keduanya halaman berbeda, jadi pastikan URL lama ter-redirect (static server biasanya melakukannya untuk folder, seperti contoh di atas) dan update sitemap kamu.
Kesimpulan
Static export Next.js mengembalikan 404 saat dikunjungi langsung karena /about disimpan sebagai about.html, dan static server tidak menambahkan ekstensinya. Set trailingSlash: true supaya setiap route menjadi about/index.html, atau tambahkan try_files $uri $uri.html di nginx kalau kamu memegang server-nya.