Serving Static Files #
Melayani file statis adalah salah satu hal yang dilakukan Nginx dengan sangat baik. Tapi untuk melakukannya dengan benar — bukan sekadar “bekerja” — ada beberapa hal yang perlu dipahami: bagaimana Nginx menentukan path file yang harus dibaca, directive mana yang mengontrol perilaku ini, dan bagaimana mengoptimalkannya agar efisien.
Bagaimana Nginx Menentukan Path File #
Ketika sebuah request masuk, Nginx menggabungkan nilai root dengan URI untuk menentukan path file yang harus dibaca:
Path file = nilai root + URI request
Contoh:
root /var/www/html;
Request: GET /images/logo.png
Path file: /var/www/html + /images/logo.png
= /var/www/html/images/logo.png
Ini terdengar sederhana, tapi ada satu detail penting: root ditambahkan dengan URI secara penuh, termasuk segmen pertama. Ini berbeda dengan alias yang akan dibahas di artikel berikutnya.
server {
listen 80;
server_name example.com;
root /var/www/html; # Base directory
location / {
# Request: GET /about.html
# File yang dibaca: /var/www/html/about.html
try_files $uri $uri/ =404;
}
location / {
# Request: GET /guide.html
# File yang dibaca: /var/www/html/guide.html
# (root dari server block diwarisi)
try_files $uri $uri/ =404;
}
}
Directive index: File Default untuk Direktori #
Ketika request mengarah ke direktori (URL diakhiri /), Nginx mencari file index:
server {
root /var/www/html;
# Urutan pencarian: index.html dulu, lalu index.htm
index index.html index.htm;
# Untuk PHP, tambahkan index.php di depan:
# index index.php index.html index.htm;
}
Jika file index tidak ditemukan dan autoindex tidak aktif, Nginx mengembalikan 403 Forbidden.
Optimasi: sendfile, tcp_nopush, tcp_nodelay #
Tiga directive ini bekerja bersama untuk mengoptimalkan transfer file:
http {
# Aktifkan zero-copy transfer (lewat kernel, tidak melewati user space)
# Sangat disarankan untuk serving file statis
sendfile on;
# Buffer paket TCP hingga penuh sebelum dikirim
# Mengurangi jumlah paket jaringan — bekerja bersama sendfile
tcp_nopush on;
# Kirim data segera tanpa menunggu buffer penuh
# Berguna untuk koneksi keep-alive setelah sendfile selesai
tcp_nodelay on;
# Batasi ukuran yang dikirim per sendfile() call
# 0 = tidak ada batas (default)
# Untuk mencegah satu file besar memblok worker terlalu lama:
sendfile_max_chunk 1m;
}
Kombinasi sendfile on + tcp_nopush on adalah yang paling umum dan memberikan performa terbaik untuk file statis.
Cache Header untuk Browser #
File statis seperti CSS, JavaScript, gambar, dan font jarang berubah. Dengan memberi tahu browser untuk menyimpan file-file ini dalam cache, kamu mengurangi request ke server dan mempercepat loading bagi pengunjung yang kembali:
server {
root /var/www/html;
# HTML — jangan cache atau cache sangat singkat
# (supaya perubahan konten langsung terlihat)
location ~* \.html$ {
expires -1; # atau: add_header Cache-Control "no-cache";
}
# CSS dan JavaScript — cache 1 tahun jika menggunakan hash di nama file
# (teknik cache busting: style.a1b2c3d4.css)
location ~* \.(css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Gambar dan font — cache 1 tahun
location ~* \.(png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off; # Tidak perlu log akses file gambar
}
}
immutable di Cache-Control memberitahu browser bahwa file ini tidak akan berubah selama masa cache — browser tidak perlu mengirim kondisional request (If-Modified-Since) untuk memverifikasi, langsung gunakan dari cache saja.
Melayani File dengan MIME Type yang Benar #
Nginx menentukan Content-Type header dari ekstensi file menggunakan file mime.types:
http {
include /etc/nginx/mime.types;
default_type application/octet-stream; # Fallback jika ekstensi tidak dikenal
}
Jika kamu melayani file dengan ekstensi yang tidak ada di mime.types, atau ingin override MIME type tertentu:
server {
# Override MIME type untuk ekstensi tertentu
location ~* \.wasm$ {
add_header Content-Type application/wasm;
}
# Atau gunakan types block untuk mendefinisikan mapping
location /api/ {
types {
application/json json;
}
default_type application/json;
}
}
Konfigurasi Lengkap: Static Website #
Berikut konfigurasi yang cukup lengkap untuk sebuah website statis — misalnya hasil build dari React, Vue, atau Hugo:
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
# Optimasi transfer file
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# Kompresi (hemat bandwidth)
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
image/svg+xml;
# SPA routing: semua path yang tidak ada filenya → index.html
location / {
try_files $uri $uri/ /index.html;
}
# Cache agresif untuk asset dengan hash di nama file
location ~* \.(css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~* \.(png|jpg|jpeg|gif|ico|svg|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Keamanan: blokir akses ke file tersembunyi
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Keamanan: blokir file sensitif
location ~* \.(env|git|svn|htaccess|htpasswd)$ {
deny all;
log_not_found off;
}
}
Mengecek Apakah File Berhasil Diservis #
# Cek response headers — pastikan Content-Type dan Cache-Control benar
curl -I http://example.com/style.css
# Output yang diharapkan:
# HTTP/1.1 200 OK
# Content-Type: text/css
# Cache-Control: public, immutable
# Expires: [tanggal 1 tahun ke depan]
# Cek apakah file yang tepat yang diservis
curl http://example.com/index.html | head -20
# Cek apakah gzip aktif
curl -H "Accept-Encoding: gzip" -I http://example.com/style.css
# Harus ada: Content-Encoding: gzip
Ringkasan #
- Path file =
root+ URI — Nginx menggabungkan keduanya secara langsung untuk menentukan file yang dibaca.try_files $uri $uri/ =404adalah pola standar untuk static site; ganti=404dengan/index.htmluntuk SPA.sendfile on+tcp_nopush onadalah kombinasi standar untuk performa file statis terbaik.- Gunakan
expires 1y+Cache-Control: public, immutableuntuk CSS/JS/gambar yang menggunakan cache busting di nama file.- Selalu blokir akses ke file tersembunyi (
/\.) dan file sensitif (.env,.git) di konfigurasi production.