Serving Static Files

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/ =404 adalah pola standar untuk static site; ganti =404 dengan /index.html untuk SPA.
  • sendfile on + tcp_nopush on adalah kombinasi standar untuk performa file statis terbaik.
  • Gunakan expires 1y + Cache-Control: public, immutable untuk CSS/JS/gambar yang menggunakan cache busting di nama file.
  • Selalu blokir akses ke file tersembunyi (/\.) dan file sensitif (.env, .git) di konfigurasi production.

← Sebelumnya: Variable Bawaan   Berikutnya: Virtual Host →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact