Belajar Flask - Static Files & Serving
Episode 7 of 27

Belajar Flask - Static Files & Serving

Memahami cara Flask menyajikan CSS, JavaScript, dan gambar dari folder /static, mengubah lokasi static folder untuk blueprint dan kebutuhan custom, mengelola cache headers, serta praktik serve aset yang efisien dan aman.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Web app tidak hanya berisi HTML — ada CSS, JavaScript, gambar, favicon, dan font. Semua aset ini disebut static files: file yang tidak berubah mengikuti request dan disajikan apa adanya. Di episode 6 kita merender HTML dari template; sekarang kita lengkapi dengan aset-asetnya.

Episode 7 membahas folder /static bawaan Flask, custom static folder, cache headers — yang berdampak langsung pada kecepatan halaman — dan praktik mengelola aset dengan benar.

Folder /static Bawaan

Secara default Flask melayani semua file di dalam folder static/ pada path /static. Struktur proyeknya:

Struktur proyek dengan static files
flask-lab/
  app.py
  templates/
    index.html
  static/
    css/style.css
    js/app.js
    images/logo.png

Dengan struktur ini, static/css/style.css otomatis tersedia di URL http://localhost:5000/static/css/style.css — tanpa kode apa pun. Link dari template memakai url_for('static', filename='...'):

HTMLMemuat CSS dan JS di template
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<script src="{{ url_for('static', filename='js/app.js') }}"></script>

url_for('static', filename='...') menghasilkan URL yang benar — termasuk nanti saat aplikasi dipasang di bawah sub-path atau di belakang CDN. Jangan pernah menulis path /static/... hardcoded di template.

Tip

Selalu gunakan url_for('static', ...) daripada hardcode /static/.... Jika suatu saat aplikasi kalian dipasang di sub-path (misal /app/) atau aset dipindah ke CDN, satu-satunya yang perlu diubah adalah konfigurasi — bukan puluhan template.

Custom Static Folder

Terkadang aset kalian tidak berada di static/ — misalnya file yang dihasilkan secara dinamis, atau monorepo dengan folder aset terpisah. Ubah lokasi lewat argumen static_folder dan path-nya via static_url_path:

PythonCustom static folder
app = Flask(
    __name__,
    static_folder="assets",        # folder fisik
    static_url_path="/assets",     # URL yang mengeksposnya
)

Sekarang assets/logo.png tersedia di /assets/logo.png. Contoh nyata: aplikasi yang menyimpan hasil generate dokumen atau thumbnail di folder non-default, atau proyek yang memakai bundler (Vite, esbuild) yang mengeluarkan output ke dist/:

PythonMemakai output bundler sebagai static
app = Flask(__name__, static_folder="../frontend/dist", static_url_path="/")

Dengan static_url_path="/", aset bundler dilayani di root URL — pola umum untuk aplikasi yang frontend-nya dibuild dengan tool modern. Perhatikan bahwa saat memakai blueprint, masing-masing bisa punya static_folder sendiri (episode 9).

Cache Headers: Membuat Aset Cepat

Setiap kali browser memuat ulang halaman, ia meminta ulang semua CSS/JS kecuali diberi tahu "file ini tidak berubah". Di situlah cache headers berperan. Flask memberikan kontrol via send_from_directory dan make_response:

PythonServe file dengan cache headers
from flask import send_from_directory, current_app
 
 
@app.get("/download/<path:filename>")
def download(filename: str) -> Response:
    resp = send_from_directory(
        current_app.config["UPLOAD_FOLDER"], filename
    )
    resp.headers["Cache-Control"] = "public, max-age=3600"
    return resp

max-age=3600 memberi tahu browser: aset ini valid 1 jam — browser tidak perlu meminta ulang, menghemat bandwidth dan mempercepat navigasi.

Cache Busting untuk Versi Baru

Masalah max-age besar: saat kalian merilis CSS/JS baru, browser yang menyimpan versi lama tidak akan mengambilnya. Solusinya cache busting — menambah fingerprint pada filename:

PythonCache busting via query
<link rel="stylesheet"
      href="{{ url_for('static', filename='css/style.css', v='20260816') }}">

Setiap rilis, ganti v=... dengan versi baru. URL berubah → browser dianggap file baru → cache lama terlewati. Ini pola sederhana yang efektif; alternatif produksi yang lebih rapi adalah file hashing di build pipeline.

Konten Statis yang Aman

Beberapa aturan keamanan saat menyajikan static files:

  • send_from_directory, bukan send_file manual: selalu batasi ke folder tertentu agar path traversal (../../etc/passwd) tidak lolos. send_from_directory melakukan normalisasi dan pengecekan otomatis.
  • Batasi tipe file: jangan melayani file yang bisa dieksekusi di browser (misal .svg dengan script inline) tanpa kontrol; upload user dipisah folder dan tidak disajikan sebagai HTML.
  • Set Content-Type benar: jika send_file tidak mengenali tipe, response bisa salah render di client.

Warning

Jangan pernah menggabungkan folder static/ dengan folder upload pengguna. File upload harus disimpan terpisah (episode 19), divalidasi tipe & namanya, dan disajikan lewat endpoint khusus dengan header Content-Disposition — bukan diekspos mentah sebagai static file yang bisa berisi script berbahaya.

Mengukur dan Mengoptimalkan

Static files sering jadi penyumbang terbesar bobot halaman. Kebiasaan yang baik:

  • Kompresi: aktifkan gzip/brotli di reverse proxy (episode 22) untuk CSS/JS/HTML.
  • Minifikasi: build CSS/JS lewat bundler; Flask hanya melayani hasilnya.
  • Cache-aware naming: fingerprint filename untuk cache permanen.
  • CDN: untuk aset publik (logo, JS library), pertimbangkan CDN atau HTTP caching di proxy.

Sebagai catatan, send_from_directory dan static serving Flask cukup untuk development dan skala kecil-menengah. Di produksi, Nginx (episode 22) akan mengambil alih tugas serve static — jauh lebih cepat dan membebaskan Python process.

Penutup

Pada episode 7 ini, kalian telah memahami cara Flask menangani static files.

Inti yang harus dibawa pulang:

  • Folder static/ otomatis dilayani di /static; link selalu lewat url_for('static', filename=...).
  • static_folder dan static_url_path menyesuaikan lokasi & URL aset.
  • Cache headers (Cache-Control, max-age) mempercepat halaman; cache busting untuk versi baru.
  • send_from_directory mencegah path traversal; jangan campur folder upload dengan static.
  • Di produksi, serahkan serve static ke Nginx/CDN.

Di episode 8 selanjutnya, kita merapikan fondasi konfigurasi: configuration & environments — config classes, environment variables, instance folder, secrets, dan pola 12-factor untuk memisahkan dev/prod dengan aman. Sampai jumpa di episode 8!