Rilis · 2 menit baca · 19 Oktober 2026

Favicon yang dirusak OpenNext

Situs Next.js di Cloudflare Workers menyajikan favicon rusak di production. Ternyata proses build membaca file biner sebagai teks, lalu menyimpan kerusakannya di cache permanen.

Tab browser kartun dengan ikon piksel rusak di bawah kaca pembesar, dan pipa yang mengubah bit rapi jadi coretan
U+FFFD
isi byte favicon yang rusak
88 → 9 KB
ukuran favicon setelah dioptimasi di perbaikan yang sama
1
aturan sekarang: file biner disimpan di public/

01 · Konteksnya

Ini situs company profile sebuah agensi kreatif, dibangun dengan Next.js 15.5.

Situsnya berjalan di Cloudflare Workers lewat @opennextjs/cloudflare. Di depannya ada cache di R2 dan cache regional yang bertahan lama. Cepat dan murah, tapi ternyata juga pintar menyimpan kesalahan.

02 · Akar masalahnya

Favicon-nya disiapkan saat build, lalu isinya dibaca sebagai teks UTF-8.

Setiap byte yang tidak sah dalam UTF-8 berubah menjadi karakter pengganti U+FFFD. Hasil yang rusak itu lalu disimpan di cache sebagai file permanen. Akibatnya, setiap pengunjung mendapat file rusak sampai cache-nya berganti. Kodenya sendiri tidak terlihat salah.

03 · Perbaikannya

Jauhkan file biner dari proses yang mungkin menganggapnya teks.

Yang saya ubah:

  • favicon, gambar, font, dan PDF masuk ke public/ sebagai aset statis
  • route yang mengembalikan data biner dipaksa dinamis
  • di stack ini, saya tidak memakai konvensi file icon.* dan opengraph-image.*
// app/report/route.ts — handler yang mengembalikan data biner
export const dynamic = "force-dynamic";

04 · Cek dengan hash

Kalau aset hanya rusak di production, bandingkan isinya, bukan tampilannya.

Hitung hash file yang kamu kirim, lalu hitung hash file dari URL live. Kalau hasilnya berbeda, ada yang mengubah file itu di antara build dan browser. Di perbaikan yang sama, ukuran favicon juga turun dari 88 KB menjadi 9 KB.

shasum -a 256 public/favicon.ico
curl -s https://example.com/favicon.ico | shasum -a 256

05 · Simpan data lead lebih dulu

Proyek yang sama mengajarkan satu pola untuk form kontak.

Endpoint-nya bekerja dengan urutan ini. Pertama, ia memeriksa request dan membatasi jumlah kiriman per IP dengan hash ber-salt. Kedua, ia menyimpan data lead. Terakhir, ia mengirim email berisi PDF dan memperbarui statusnya. Kalau layanan email sedang mati, data lead tetap aman. Simpan dulu hal yang tidak boleh hilang.

06 · Ticker yang tidak melompat

Detail kecil, masih di proyek yang sama.

Ticker logo melambat saat di-hover. Caranya dengan mengubah playbackRate di Web Animations, bukan durasi animasinya, jadi posisinya tidak melompat. Animasinya berhenti kalau pengguna memilih reduced motion.

Tulisan berikutnyaKlien Supabase tanpa cookie