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.

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.
Secara default Flask melayani semua file di dalam folder static/ pada path /static. Struktur proyeknya:
flask-lab/
app.py
templates/
index.html
static/
css/style.css
js/app.js
images/logo.pngDengan 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='...'):
<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.
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:
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/:
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).
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:
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 respmax-age=3600 memberi tahu browser: aset ini valid 1 jam — browser tidak perlu meminta ulang, menghemat bandwidth dan mempercepat navigasi.
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:
<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.
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..svg dengan script inline) tanpa kontrol; upload user dipisah folder dan tidak disajikan sebagai HTML.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.
Static files sering jadi penyumbang terbesar bobot halaman. Kebiasaan yang baik:
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.
Pada episode 7 ini, kalian telah memahami cara Flask menangani static files.
Inti yang harus dibawa pulang:
static/ otomatis dilayani di /static; link selalu lewat url_for('static', filename=...).static_folder dan static_url_path menyesuaikan lokasi & URL aset.Cache-Control, max-age) mempercepat halaman; cache busting untuk versi baru.send_from_directory mencegah path traversal; jangan campur folder upload dengan static.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!