livereload.js Terikut ke Production Saat `hugo deploy`: Penyebab dan Solusi
Di Hugo, livereload.js seharusnya hanya muncul saat mode development — file ini bertanggung jawab membuat browser otomatis refresh setiap kali ada perubahan kode, fitur yang sangat membantu saat menulis konten atau mengubah template. Tapi tidak jarang file ini justru ikut ter-deploy ke production meskipun deployment sudah memakai hugo deploy, command resmi Hugo untuk publish. Masalah ini sering disangka bug Hugo, padahal akar masalahnya adalah kesalahan pemahaman terhadap alur build dan deploy yang sebenarnya terpisah secara tegas. Artikel ini membedah mekanisme di balik penyuntikan livereload, kenapa hugo deploy tidak pernah melakukan build ulang, dan bagaimana menyusun workflow yang membuat masalah ini tidak mungkin terjadi lagi.
Pemisahan Konsep: Build vs Deploy di Hugo
Hugo memisahkan dua proses ini secara tegas, dan memahami pemisahan ini adalah kunci untuk seluruh pembahasan di artikel ini:
| Proses | Fungsi |
|---|---|
| Build | Menghasilkan static files ke folder public/ |
| Deploy | Meng-upload isi folder public/ apa adanya ke remote |
flowchart LR
A[Source: content, layouts, config] -->|hugo / hugo server| B[Folder public/]
B -->|hugo deploy| C[Remote: S3, GCS, dll]
style B fill:#f9f9f9Command yang berkaitan dengan kedua proses ini:
| Command | Peran |
|---|---|
hugo | Build untuk production |
hugo server | Build untuk development + LiveReload aktif |
hugo deploy | Hanya meng-upload isi public/, tidak melakukan build |
Satu fakta yang krusial dan jadi akar dari seluruh masalah di artikel ini: hugo deploy tidak pernah melakukan build ulang. Ia murni proses upload — apapun yang ada di folder public/ saat command ini dijalankan, itulah yang akan terkirim ke remote, baik isinya valid untuk production maupun tidak.
Bagaimana Hugo Menyuntikkan livereload.js saat hugo server
Sebelum membahas penyebab masalahnya, penting memahami mekanisme di balik kenapa livereload.js bisa muncul di output HTML sama sekali. Ini bukan file statis yang sengaja kamu taruh di folder static/ — Hugo menyuntikkannya secara otomatis ke setiap halaman HTML saat server development berjalan.
sequenceDiagram
participant Dev as Developer
participant Hugo as hugo server
participant Browser
Dev->>Hugo: Jalankan hugo server
Hugo->>Hugo: Set .Site.IsServer = true
Hugo->>Hugo: Render HTML + suntik <script src="/livereload.js">
Hugo->>Browser: Serve halaman dengan livereload aktif
Dev->>Hugo: Ubah file content/layout
Hugo->>Browser: Kirim sinyal lewat WebSocket
Browser->>Browser: Auto-refresh halamanMekanismenya bergantung pada satu variabel internal: .Site.IsServer. Variabel ini bernilai true hanya ketika Hugo dijalankan lewat hugo server, dan false untuk build biasa lewat hugo. Saat .Site.IsServer bernilai true, Hugo menyuntikkan elemen <script> yang menunjuk ke livereload.js — script kecil yang membuka koneksi WebSocket ke server development, mendengarkan sinyal perubahan file, lalu memicu refresh otomatis di browser.
Karena penyuntikan ini bersifat otomatis dan terjadi di level engine Hugo (bukan sesuatu yang kamu tulis manual di template, kecuali kamu memang menambahkannya sendiri), banyak developer tidak sadar bahwa script ini benar-benar tertanam langsung di HTML hasil build — bukan dimuat terpisah lewat request tambahan yang mudah diabaikan.
livereload.jssendiri sebenarnya disajikan langsung oleh proseshugo serveryang sedang berjalan, bukan file fisik yang tersimpan di folderpublic/. Yang ikut ter-deploy ke production bukan filelivereload.js-nya, melainkan referensi<script>tag ke file itu di HTML hasil build — yang di production akan gagal dimuat (404) sekaligus menjadi indikasi bahwa output yang ter-upload bukan build production yang benar.
Penyebab livereload.js Terikut ke Production
Kasus yang paling sering terjadi mengikuti pola workflow seperti ini:
hugo server
# (stop server dengan Ctrl+C)
hugo deploy
sequenceDiagram
participant Dev as Developer
participant FS as Folder public/
participant Remote
Dev->>FS: hugo server (isi public/ dengan livereload.js disuntik)
Dev->>Dev: Stop server, public/ TIDAK dibersihkan
Dev->>Remote: hugo deploy
Note over Remote: Upload isi public/ apa adanya<br/>termasuk script livereload.jsPenjelasannya bertahap:
hugo servermembangun HTML dalam mode development, dan otomatis menyuntikkanlivereload.jskarena.Site.IsServerbernilaitrue- Output ini disimpan ke folder
public/di disk — sama seperti hasilhugobuild biasa hugo deploydijalankan setelahnya, dan karena ia tidak pernah melakukan build ulang (seperti dijelaskan di bagian sebelumnya), ia menganggap isipublic/sudah valid dan langsung meng-upload-nya- Akibatnya, artifact hasil development — lengkap dengan referensi
livereload.js— ikut terkirim ke production
Akar masalahnya bukan hugo deploy yang salah berperilaku, melainkan asumsi keliru bahwa hugo deploy akan “tahu” untuk membangun ulang versi production terlebih dahulu. Padahal sesuai pemisahan konsep di bagian sebelumnya, deploy memang murni tugas upload.
Arti Pesan “No changes required.”
Saat menjalankan hugo deploy, terkadang muncul pesan:
No changes required.
Pesan ini sering disalahartikan sebagai error atau tanda ada yang salah, padahal sebenarnya berarti sebaliknya — deploy berjalan normal, hanya saja tidak ada perubahan yang perlu di-upload. Di balik layar, hugo deploy membandingkan isi folder public/ lokal dengan apa yang sudah ada di remote, biasanya berbasis checksum atau hash tiap file, lalu hanya meng-upload file yang berbeda.
flowchart TD
A[hugo deploy dijalankan] --> B[Hitung hash setiap file di public/ lokal]
B --> C[Bandingkan dengan hash file di remote]
C --> D{Ada perbedaan?}
D -- Tidak --> E["No changes required."]
D -- Ya --> F[Upload hanya file yang berbeda]Pesan ini bukan error — tapi justru di sinilah jebakannya muncul. Kalau folder public/ yang dibandingkan itu berisi hasil build dari hugo server (lengkap dengan livereload.js), dan kebetulan isi itu sudah pernah ter-upload sebelumnya, Hugo tetap menganggapnya sebagai state yang valid dan konsisten — padahal sebenarnya artifact yang salah sudah dianggap final sejak upload pertama.
Peran Environment: development vs production
Hugo menentukan banyak perilaku build — termasuk apakah livereload disuntikkan atau tidak — berdasarkan environment yang aktif. Penting dipahami bahwa environment ini sebenarnya cuma sebuah string biasa yang dibaca lewat hugo.Environment di template, bukan mekanisme switch ajaib yang otomatis mengubah perilaku tanpa kondisi eksplisit di kode.
Cek environment yang aktif saat ini:
hugo env
Output yang diharapkan untuk build production:
Environment: production
Default environment Hugo sebenarnya bergantung pada command yang dipakai:
| Command | Environment Default |
|---|---|
hugo | production |
hugo server | development |
| Override manual | HUGO_ENV=production hugo ... |
Kalau environment yang aktif masih development saat kamu bermaksud build untuk production — entah karena lupa, atau karena script build custom yang tidak eksplisit men-set environment — konsekuensinya:
- LiveReload tetap berpotensi aktif tergantung kondisi template
.Site.IsServerbisa saja masihtruekalau prosesnya berasal darihugo server- Risiko
livereload.jsikut ke output produksi jadi sangat tinggi
.Site.IsServerdanhugo.Environmentadalah dua hal yang berbeda meski sering tampak berkaitan..Site.IsServermurni soal apakah proses berasal darihugo serveratau bukan, sementarahugo.Environmentadalah label string yang bisa di-override manual lewatHUGO_ENV. Keduanya sebaiknya dicek bersamaan di template untuk kondisi yang benar-benar aman, bukan hanya mengandalkan salah satu saja.
Kesalahan Umum yang Sering Terjadi
Mengira hugo deploy Otomatis Rebuild
# ANTI-PATTERN: asumsi hugo deploy akan build ulang dulu
hugo deploy
# Tidak ada rebuild yang terjadi -- public/ diupload apa adanya
# BENAR: build eksplisit sebelum deploy
hugo --minify
hugo deploy
Deploy Setelah hugo server
# ANTI-PATTERN: public/ masih berisi artifact development
hugo server
# stop server
hugo deploy
# BENAR: bersihkan dan build ulang sebelum deploy
rm -rf public
hugo --minify
hugo deploy
Menggunakan –minify di hugo deploy
# ANTI-PATTERN: --minify bukan flag untuk hugo deploy
hugo deploy --minify
# Error: unknown flag: --minify
# BENAR: --minify dipakai saat build, bukan saat deploy
hugo --minify
hugo deploy
Ketiga kesalahan ini sebenarnya berakar dari satu kesalahpahaman yang sama: menganggap hugo deploy punya kemampuan atau tanggung jawab yang sebenarnya dimiliki oleh command hugo (build), padahal keduanya memang sengaja dipisah Hugo sebagai dua command yang independen.
Workflow Build dan Deploy yang Benar
Langkah aman untuk production menggabungkan tiga hal sekaligus: membersihkan artifact lama, build ulang dengan environment yang benar, baru deploy.
rm -rf public && HUGO_ENV=production hugo --minify && hugo deploy
flowchart TD
A[rm -rf public] --> B[HUGO_ENV=production hugo --minify]
B --> C{Build berhasil?}
C -- Tidak --> D[Perbaiki error, ulangi dari awal]
C -- Ya --> E[hugo deploy]
E --> F[Verifikasi: grep livereload di public/]Penjelasan tiap langkah:
rm -rf publicmenghapus seluruh artifact lama, termasuk kemungkinan sisa hasilhugo serversebelumnya — langkah ini menghilangkan akar masalah secara langsungHUGO_ENV=production hugo --minifymembangun ulang dari awal dengan environment yang eksplisit diset ke production, memastikan.Site.IsServerbernilaifalsehugo deploymeng-upload output yang sudah bersih dan benar-benar mencerminkan build production
Jadikan rangkaian perintah ini sebagai satu script (misalnyadeploy.shatau target diMakefile), bukan diketik manual setiap kali. Menghilangkan langkah manual sama dengan menghilangkan kemungkinan lupa salah satu tahapnya.
Verifikasi Sebelum Deploy
Sebelum menjalankan hugo deploy, lakukan pengecekan eksplisit untuk memastikan tidak ada referensi livereload.js yang tertinggal di output:
grep -R "livereload" public/
Kalau tidak ada hasil yang muncul, output aman untuk production. Kalau ada hasil yang muncul, itu tanda jelas bahwa folder public/ masih berisi artifact dari hugo server dan perlu di-build ulang sebelum deploy dilanjutkan.
Untuk tim yang menjalankan deploy lebih dari sekali atau melibatkan lebih dari satu orang, pertimbangkan menambahkan pengecekan ini sebagai langkah eksplisit di script deploy itu sendiri — bukan sekadar diingat manual setiap kali:
#!/bin/bash
set -e
rm -rf public
HUGO_ENV=production hugo --minify
if grep -R "livereload" public/ > /dev/null; then
echo "ERROR: livereload.js terdeteksi di build production, deploy dibatalkan."
exit 1
fi
hugo deploy
Dengan set -e dan pengecekan eksplisit sebelum baris hugo deploy, script ini berhenti otomatis kalau ditemukan indikasi build yang salah — mencegah deploy yang salah terjadi tanpa disadari, alih-alih hanya mengandalkan kewaspadaan manual.
Best Practice Inject LiveReload di Template
Kalau kamu pernah menambahkan livereload secara manual di template (bukan mengandalkan penyuntikan otomatis Hugo), hindari hardcode tanpa kondisi sama sekali:
<!-- ANTI-PATTERN: livereload selalu disertakan tanpa kondisi -->
<script src="/livereload.js"></script>
Pendekatan yang benar memakai conditional berbasis .Site.IsServer:
{{ if .Site.IsServer }}
{{ partial "livereload.html" . }}
{{ end }}
Untuk lapisan keamanan tambahan, kombinasikan dengan pengecekan environment secara eksplisit — mengingat seperti dibahas sebelumnya, .Site.IsServer dan hugo.Environment adalah dua hal yang berbeda dan idealnya dicek bersamaan:
{{ if and .Site.IsServer (eq hugo.Environment "development") }}
{{ partial "livereload.html" . }}
{{ end }}
Dengan kombinasi dua kondisi ini, livereload hanya akan muncul kalau keduanya benar — proses berasal dari hugo server dan environment yang aktif memang development. Ini memberi lapisan proteksi tambahan dibanding hanya mengandalkan satu kondisi saja, terutama untuk setup yang kompleks di mana environment bisa di-override secara manual.
Kombinasi dua kondisi (.Site.IsServerdanhugo.Environment) lebih aman daripada satu kondisi saja, tapi tetap tidak menggantikan kebiasaan membersihkan folderpublic/sebelum build production. Pertahanan berlapis selalu lebih baik daripada mengandalkan satu mekanisme tunggal.
Tabel Ringkasan Command dan Perilakunya
| Command | Build Ulang? | .Site.IsServer | Environment Default | Risiko livereload.js |
|---|---|---|---|---|
hugo server | Ya, terus-menerus | true | development | Selalu disuntikkan (memang seharusnya) |
hugo | Ya, sekali | false | production | Tidak ada, kecuali template hardcode |
hugo --minify | Ya, sekali | false | production | Tidak ada |
hugo deploy | Tidak | Tidak relevan | Tidak relevan | Bergantung sepenuhnya pada isi public/ sebelumnya |
Ringkasan
- Hugo memisahkan build (menghasilkan
public/) dan deploy (menguploadpublic/) secara tegas —hugo deploytidak pernah melakukan build ulang.livereload.jsdisuntikkan otomatis ke HTML saat.Site.IsServerbernilaitrue, yang hanya terjadi ketika proses berasal darihugo server.- Penyebab paling umum adalah menjalankan
hugo deploysetelahhugo servertanpa build ulang di antaranya — artifact development ikut terupload apa adanya.- Pesan “No changes required.” bukan error, tapi bisa menyesatkan kalau artifact yang dibandingkan memang sudah salah sejak upload pertama.
hugo.Environmentadalah string yang dibaca dariHUGO_ENVatau default command, berbeda dari.Site.IsServer— keduanya idealnya dicek bersamaan di template untuk kondisi paling aman.- Workflow aman:
rm -rf publicuntuk membersihkan artifact lama, build ulang eksplisit dengan environment production, baru jalankanhugo deploy.grep -R "livereload" public/adalah verifikasi cepat sebelum deploy — pertimbangkan menjadikannya langkah otomatis di script deploy, bukan sekadar diingat manual.- Di template, gunakan
{{ if and .Site.IsServer (eq hugo.Environment "development") }}untuk proteksi berlapis terhadap kondisi environment yang ambigu.