Static files là asset đi kèm code (CSS, JS, ảnh trong app/static/). Media files là file user upload lúc runtime (avatar, attachment).
# settings.py
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles' # collectstatic gom về đây
STATICFILES_DIRS = [BASE_DIR / 'assets'] # source dev
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media' # user upload save về đây- Flow production gồm 3 bước.
- Bước build chạy
python manage.py collectstatic --noinputđể gom tất cả file static vềSTATIC_ROOT. - Bước serve để nginx lo
/static/từSTATIC_ROOTvà/media/từMEDIA_ROOT, không cho Django xử lý. - Hoặc thay nginx bằng CDN: dùng
django-storagespush static lên S3/CloudFront, setSTORAGES['default']cho media vàSTORAGES['staticfiles']cho static (Django 4.2+).
# Hash filename để cache-bust khi deploy
STORAGES = {
'staticfiles': {
'BACKEND': 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage',
},
}Khác biệt quan trọng cần nhớ: static có thể cache vĩnh viễn vì filename có hash, mỗi deploy đổi hash → browser tự fetch lại.
- Media thì cần kiểm soát access — đừng mở public
MEDIA_ROOTlên S3 cho mọi file, vì invoice hay avatar có thể leak ra ngoài. - Tài liệu private phải cấp signed URL có hạn.
- Và đừng để Django serve media ở prod —
serve()của Django docs nói rõ chỉ dành cho dev.
Static files = assets shipped with the code (CSS, JS, images in app/static/). Media files = files users upload at runtime (avatars, attachments).
# settings.py
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles' # collectstatic dumps here
STATICFILES_DIRS = [BASE_DIR / 'assets'] # source in dev
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media' # user uploads land hereProduction flow:
1. Build: python manage.py collectstatic --noinput → gather every static file into STATIC_ROOT.
2. Nginx serves /static/ from STATIC_ROOT, /media/ from MEDIA_ROOT — not through Django.
3. Or CDN: use django-storages to push statics to S3/CloudFront. Set STORAGES['default'] for media, STORAGES['staticfiles'] for static (Django 4.2+).
# Hash filenames to bust cache on deploy
STORAGES = {
'staticfiles': {
'BACKEND': 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage',
},
}Pitfall: Important distinction: statics can be cached forever (filenames are hashed); media needs access control (signed URLs for private docs).
- Do not make
MEDIA_ROOTpublic on S3 for every file — invoices/avatars can leak. - And do not let Django serve media in prod (the Django docs are explicit:
serve()is dev-only).