Directory Browse #
Directory Browsing (atau penelusuran direktori) adalah fitur server web yang menyajikan daftar nama file dan subdirektori secara dinamis kepada pengunjung ketika mereka mengakses jalur (path) URL berupa folder yang tidak memiliki berkas indeks (seperti index.html atau index.php). Secara default, Caddy akan mengembalikan respons HTTP 404 Not Found untuk folder tanpa berkas indeks demi alasan keamanan. Perilaku ini sangat tepat untuk melindungi struktur data aplikasi kita dari pemindaian tidak sah. Namun, untuk kegunaan tertentu seperti server berbagi berkas, repositori unduhan publik, atau portal dokumentasi tim, mengaktifkan directory listing menjadi sangat penting.
Di dalam Caddy, kita dapat mengaktifkan fitur ini secara eksplisit menggunakan opsi browse pada direktif file_server. Selain menyediakan tampilan bawaan yang fungsional dan bersih, Caddy mendukung penuh penggunaan template HTML kustom menggunakan sintaksis Go templates (text/template). Hal ini memungkinkan kita untuk mendesain antarmuka penelusuran berkas yang interaktif, berestetika premium, dan disesuaikan dengan identitas desain kita sendiri.
Mengaktifkan Directory Browse #
Untuk mengaktifkan directory listing pada Caddyfile, kita cukup menambahkan parameter browse di dalam blok konfigurasi direktif file_server:
# Mengaktifkan directory listing dasar
files.example.com {
root * /var/www/shared-files
file_server {
# Mengizinkan pengunjung menelusuri isi folder
browse
}
}
Dengan konfigurasi di atas, jika folder /var/www/shared-files/ tidak memiliki berkas index.html di dalamnya, pengunjung yang mengakses https://files.example.com/ tidak akan menerima error 404, melainkan akan disajikan halaman HTML berisi daftar berkas dan folder yang ada di direktori tersebut.
Tampilan Bawaan (Default Layout) #
Caddy menyediakan template bawaan (default template) yang dikompilasi langsung di dalam binernya. Tampilan ini sangat bersih, responsif, ramah seluler, dan mendukung skema warna gelap (dark mode) secara otomatis berdasarkan preferensi sistem operasi pengunjung.
Visualisasi Tampilan Standar Directory Listing Caddy:
┌─────────────────────────────────────────────────────┐
│ Indeks dari /downloads/ │
├─────────────────────────────────────────────────────┤
│ 📁 dokumen_penting/ - 2026-06-16 10:00 │
│ 📁 galeri_foto/ - 2026-06-15 09:30 │
│ 📄 README.md 2.4 KB 2026-06-14 14:25 │
│ 📄 panduan.pdf 8.7 MB 2026-06-12 16:40 │
│ 📄 arsip_data.zip 45.1 MB 2026-06-11 11:15 │
└─────────────────────────────────────────────────────┘
Fitur-fitur pada tampilan bawaan meliputi:
- Navigasi Naik: Tautan untuk kembali ke folder di tingkat atas (parent directory).
- Penyortiran Dinamis: Pengunjung dapat mengklik header kolom untuk mengurutkan file berdasarkan Nama, Ukuran, atau Tanggal Modifikasi.
- Ukuran Manusiawi: Konversi ukuran file otomatis ke format yang mudah dibaca (KB, MB, GB).
Membangun Template Kustom Menggunakan Go Templates #
Jika kita ingin mengubah total tampilan directory listing Caddy agar terlihat premium dan menyatu dengan desain web perusahaan kita, kita dapat membuat file HTML template kustom dan mendaftarkannya pada direktif file_server:
# Menggunakan template kustom untuk directory browse
files.example.com {
root * /var/www/files
file_server {
# Tunjuk lokasi berkas template HTML kustom kita
browse /etc/caddy/templates/modern-browse.html
}
}
Berikut adalah contoh lengkap kode HTML untuk template kustom modern (modern-browse.html) yang responsif, dilengkapi CSS bawaan dengan skema warna yang elegan, serta interaksi visual yang halus:
<!-- /etc/caddy/templates/modern-browse.html -->
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Indeks dari {{.Name}} — Portal Berkas</title>
<style>
:root {
--bg-color: #f8fafc;
--text-color: #0f172a;
--card-bg: #ffffff;
--border-color: #e2e8f0;
--accent-color: #4f46e5;
--accent-hover: #4338ca;
--text-muted: #64748b;
}
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #0f172a;
--text-color: #f8fafc;
--card-bg: #1e293b;
--border-color: #334155;
--accent-color: #6366f1;
--accent-hover: #4f46e5;
--text-muted: #94a3b8;
}
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: system-ui, -apple-system, sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
line-height: 1.5;
padding: 2rem 1rem;
}
.container {
max-width: 1024px;
margin: 0 auto;
}
header {
margin-bottom: 2rem;
}
h1 {
font-size: 1.75rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.breadcrumbs {
font-size: 0.875rem;
color: var(--text-muted);
}
.breadcrumbs a {
color: var(--accent-color);
text-decoration: none;
}
.breadcrumbs a:hover { text-decoration: underline; }
.card {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05);
overflow: hidden;
}
table {
width: 100%;
border-collapse: collapse;
text-align: left;
}
th, td {
padding: 1rem;
border-bottom: 1px solid var(--border-color);
}
th {
background-color: rgba(0,0,0,0.02);
font-weight: 600;
font-size: 0.875rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-muted);
}
tr:last-child td { border-bottom: none; }
tr:hover td {
background-color: rgba(79, 70, 229, 0.03);
}
a {
color: var(--text-color);
text-decoration: none;
font-weight: 500;
}
a:hover {
color: var(--accent-color);
}
.icon {
margin-right: 0.75rem;
display: inline-block;
font-size: 1.1rem;
}
.size, .time {
font-size: 0.875rem;
color: var(--text-muted);
}
.back-link {
font-style: italic;
color: var(--accent-color);
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>📁 Indeks dari {{.Name}}</h1>
<div class="breadcrumbs">
<a href="/">Root</a> /
{{range .Breadcrumbs}}
<a href="{{.Link}}">{{.Text}}</a> /
{{end}}
</div>
</header>
<div class="card">
<table>
<thead>
<tr>
<th>Nama Berkas</th>
<th>Ukuran</th>
<th>Modifikasi Terakhir</th>
</tr>
</thead>
<tbody>
<!-- Tautan Kembali ke Parent Directory -->
{{if ne .Path "/"}}
<tr>
<td colspan="3">
<span class="icon">⬆</span>
<a href="../" class="back-link">Kembali ke folder sebelumnya</a>
</td>
</tr>
{{end}}
<!-- Menampilkan Daftar Folder Terlebih Dahulu -->
{{range .Items}}
{{if .IsDir}}
<tr>
<td>
<span class="icon">📁</span>
<a href="{{.URL}}">{{.Name}}/</a>
</td>
<td class="size">—</td>
<td class="time">{{.ModTime.Format "02 Jan 2006, 15:04"}}</td>
</tr>
{{end}}
{{end}}
<!-- Menampilkan Daftar File -->
{{range .Items}}
{{if not .IsDir}}
<tr>
<td>
<span class="icon">📄</span>
<a href="{{.URL}}">{{.Name}}</a>
</td>
<td class="size">{{.HumanSize}}</td>
<td class="time">{{.ModTime.Format "02 Jan 2006, 15:04"}}</td>
</tr>
{{end}}
{{end}}
</tbody>
</table>
</div>
</div>
</body>
</html>
Objek Data dan Variabel Template #
Saat menulis kode Go templates, kita memiliki akses ke objek data terstruktur yang dikirimkan oleh Caddy. Berikut adalah daftar variabel utama yang dapat kita gunakan:
| Variabel | Tipe Data | Deskripsi |
|---|---|---|
.Name | string | Nama direktori saat ini yang sedang dibuka (hanya nama folder akhir, bukan path lengkap). |
.Path | string | Jalur absolut direktori dari tingkat root web (misal: /downloads/pdf/). |
.URL | string | Jalur URL relatif yang aman untuk mengakses direktori saat ini. |
.Items | []FileInfo | Array dari objek yang mewakili seluruh berkas dan folder di dalam direktori saat ini. |
.Breadcrumbs | []Crumb | Array remah roti (breadcrumbs) untuk membuat link navigasi hirarki folder. |
Atribut Objek di Dalam .Items:
#
.Name(string): Nama file atau folder..IsDir(bool): Bernilaitruejika item adalah direktori..Size(int64): Ukuran file dalam satuan byte..HumanSize(string): Ukuran file yang sudah diformat (misal:"12.4 MB")..ModTime(time.Time): Waktu modifikasi terakhir berkas..URL(string): URL yang telah di-escape untuk mengunduh berkas.
Pengamanan Directory Browse (Pertahanan Berlapis) #
Mengaktifkan directory listing tanpa proteksi di server produksi adalah risiko keamanan besar. Penyerang dapat memetakan seluruh aset kita, mencari file cadangan (.bak), atau mencuri data sensitif. Kita wajib menerapkan metode pengamanan berikut:
1. Basic Authentication (Proteksi Kata Sandi) #
Membatasi akses directory listing hanya untuk pengguna yang memiliki akun masuk. Kita harus menyamarkan sandi menggunakan algoritma bcrypt:
files.example.com {
root * /var/www/html
# Menetapkan otentikasi basicauth untuk seluruh situs
basicauth {
# Username: admin, Password: sandi_rahasia_admin (Bcrypt Hash)
admin $2a$14$V0Z7iE8xYp6b1N2w3h4m5uKjLqMrNsOtPuQvRwSxTyUzVwWxXyYz.
}
file_server {
browse
}
}
[!TIP] Kita dapat membuat hash password bcrypt menggunakan perintah CLI bawaan Caddy:
caddy hash-password --plaintext "kata_sandi_kita"
2. Membatasi Akses Hanya dari IP Internal (CIDR Whitelisting) #
Jika directory browse digunakan untuk kebutuhan internal kantor, kita harus memblokir seluruh koneksi dari IP publik luar:
share.company.internal {
root * /var/www/internal-files
# 1. Mendefinisikan Matcher untuk IP luar
# (Hanya izinkan IP lokal RFC 1918)
@external {
not remote_ip 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
}
# 2. Blokir dengan HTTP 403 jika diakses dari luar kantor
respond @external "Akses ditolak: Anda harus terhubung ke VPN kantor!" 403
file_server {
browse
}
}
Menyembunyikan Berkas Secara Selektif #
Caddy menyediakan opsi hide untuk mencegah berkas tertentu muncul dalam daftar browse, sekaligus memblokir akses unduhan langsung ke berkas tersebut secara absolut:
files.example.com {
root * /var/www/html
file_server {
browse
# Menyembunyikan file dari listing dan memblokir unduhan
hide {
.git # Repositori Git
.env # Kredensial rahasia
.htaccess # Konfigurasi apache lama
*.secret # File ber-ekstensi khusus
.DS_Store # File metadata macOS
Thumbs.db # Cache thumbnail Windows
}
}
}
Menambahkan Fitur Pencarian Sisi Klien (Client-Side Search) #
Pada direktori yang berisi ratusan berkas, mencari satu file secara manual sangat menyulitkan. Kita dapat menambahkan fitur pencarian dinamis berbasis JavaScript pada template HTML kustom kita:
<!-- Tambahkan input pencarian di atas tabel pada template HTML kustom -->
<input type="search" id="fileSearch" onkeyup="searchFiles()" placeholder="Cari berkas di sini...">
<script>
function searchFiles() {
// 1. Ambil nilai input pencarian
let input = document.getElementById('fileSearch');
let filter = input.value.toLowerCase();
// 2. Dapatkan seluruh baris data file (tambahkan class "file-row" pada tr di template)
let rows = document.querySelectorAll('tbody tr.file-row');
// 3. Filter baris berdasarkan nama berkas
rows.forEach(row => {
let fileName = row.getAttribute('data-name').toLowerCase();
if (fileName.includes(filter)) {
row.style.display = ""; // Tampilkan
} else {
row.style.display = "none"; // Sembunyikan
}
});
}
</script>
Membatasi Fitur Browse di Subdirektori Tertentu Saja #
Sering kali kita ingin menyajikan situs pemasaran biasa di halaman depan, namun ingin mengaktifkan directory browse khusus pada folder unduhan /downloads/*. Kita dapat membatasi cakupan ini menggunakan blok handle di Caddyfile:
example.com {
root * /var/www/html
# 1. Mengaktifkan directory browse KHUSUS untuk folder /downloads/
handle /downloads/* {
file_server {
browse
}
}
# 2. Situs utama berjalan tanpa directory browse (kembalikan 404 jika index kosong)
handle {
file_server
}
}
Ringkasan #
- Status Bawaan — Fitur penelusuran direktori (Directory Browse) dinonaktifkan secara default oleh Caddy demi alasan keamanan.
- Aktivasi Eksplisit — Kita mengaktifkannya secara sadar menggunakan parameter
browsedi dalam konfigurasi direktiffile_server.- Desain Kustom — Caddy mendukung berkas HTML eksternal menggunakan sistem Go Templates untuk mengubah total antarmuka direktori menjadi premium.
- Pengamanan Berlapis — Lindungi direktori publik di produksi menggunakan kombinasi
basicauth(password terenkripsi) dan pembatasan IP jaringan (remote_ip).- Opsi Penyembunyian — Opsi
hidedi Caddyfile mencegah kemunculan berkas rahasia (seperti.envatau.git) di daftar halaman serta memblokir akses unduhan langsung.- Isolasi Subdirektori — Manfaatkan blok perutean
handleuntuk membatasi fitur browse hanya pada folder tertentu (seperti/downloads/) agar domain utama tetap aman.