showlett.com / Posts

Django vận hành production #5: Static và media — WhiteNoise, S3/MinIO và cache headers

2026-11-01 · Original

Tập này xử lý bài toán hay gặp trên production: file tĩnh và file media không tới được trình duyệt, hoặc tới nhưng lỗi thời. Nối tiếp tập 4 về PostgreSQL, chúng ta hoàn thiện nốt tầng dữ liệu còn lại: static phục vụ nhanh, media lưu bền. Kết quả cuối tập: collectstatic chạy một lần là xong, trình duyệt cache đúng, ảnh đính kèm không nằm lẫn trong bản deploy.

1. Static phục vụ bằng WhiteNoise

Ở dev, runserver tự phục vụ file tĩnh, nhưng Gunicorn trên production không làm việc đó. WhiteNoise là middleware phục vụ static trực tiếp từ ứng dụng, kèm nén gzip và cache header, đủ dùng cho ứng dụng vừa và nhỏ. Thêm nó ngay sau SecurityMiddleware và bật static có hash tên file để trình duyệt không giữ bản cũ.

# settings.py

MIDDLEWARE = [

"django.middleware.security.SecurityMiddleware",

"whitenoise.middleware.WhiteNoiseMiddleware",

# ... cac middleware khac

]

STORAGES = {

"staticfiles": {

"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",

},

}

2. collectstatic và STATIC_ROOT

STATIC_URL là đường dẫn trình duyệt gọi, còn STATIC_ROOT là thư mục thật trên đĩa nơi collectstatic gom mọi file tĩnh về một chỗ. Chạy collectstatic trong bước deploy, trước khi khởi động lại Gunicorn. Nếu sau này dùng Nginx như tập 3 đã mô tả, Nginx cũng trỏ vào chính thư mục này nên chuyển đổi không tốn công sửa code.

python manage.py collectstatic --noinput

ls staticfiles/admin/css/ | head

3. Cache header: hash tên file, đừng cache mọi thứ

Khi tên file chứa hash nội dung, nội dung đổi thì tên đổi, nên cache dài một năm vẫn an toàn: trình duyệt giữ bản cũ cho tên cũ và tự tải tên mới khi có bản mới. File không hash như favicon chỉ cache ngắn, và không áp cache dài cho HTML hay API. Kiểm tra bằng curl -I để thấy header Cache-Control thực tế.

# WhiteNoise cache 1 nam cho file static da hash ten

WHITENOISE_MAX_AGE = 60 * 60 * 24 * 365

4. Media lên S3 hoặc MinIO bằng django-storages

Media do người dùng tạo ra trong lúc chạy, nên không commit vào git và không để trên đĩa container, vì container khởi động lại là mất. Giải pháp là đưa media lên object storage: Amazon S3 nếu dùng cloud, hoặc MinIO tự host. Gói django-storages cung cấp backend S3Storage, chỉ cần khai báo, đưa key vào biến môi trường như tập 2 đã dặn, rồi upload thử một ảnh qua trang admin.

# settings.py

STORAGES = {

"default": {"BACKEND": "storages.backends.s3.S3Storage"},

"staticfiles": {

"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",

},

}

AWS_STORAGE_BUCKET_NAME = env("AWS_STORAGE_BUCKET_NAME")

# None khi dung S3 that, URL MinIO khi tu host

AWS_S3_ENDPOINT_URL = env("AWS_S3_ENDPOINT_URL", default=None)

5. Kiểm tra trước khi coi là xong

Chạy collectstatic trên staging và xác nhận không báo thiếu file.

Dùng curl -I kiểm tra header Cache-Control của một file CSS và một ảnh media.

Upload một ảnh test, deploy lại ứng dụng, rồi mở lại ảnh đó để chắc chắn media không biến mất cùng container cũ.

Xóa ảnh test và ghi chú bucket vào runbook.

Áp dụng vào dự án của bạn

Với ứng dụng quản lý chi tiêu, hãy bật WhiteNoise ngay để hết lỗi mất CSS sau mỗi lần deploy, và chuyển media sang S3 hoặc MinIO khi có người dùng thật tải ảnh hóa đơn lên. Đừng đợi đĩa server đầy mới chuyển, vì lúc đó di dời dữ liệu cũ vừa vội vừa dễ sót.

Bài viết liên quan

Django vận hành production #4: PostgreSQL trên production

Django vận hành production #3: Gunicorn + Nginx

Django vận hành production #2: Tách settings dev/staging/prod

Câu hỏi thường gặp

Tại sao production bị mất CSS dù dev chạy bình thường?

Vì runserver tự phục vụ static còn Gunicorn thì không. Bật WhiteNoise hoặc để Nginx phục vụ thư mục STATIC_ROOT là hết lỗi.

WhiteNoise có thay được Nginx không?

Với ứng dụng vừa và nhỏ thì được; khi lưu lượng lớn, chuyển sang Nginx hoặc CDN.

Media có nên để chung thư mục với static không?

Không. Static là file của code; media phải nằm trên object storage hoặc ổ đĩa bền riêng, tách khỏi vòng đời deploy.

Đổi CSS mà trình duyệt vẫn hiện bản cũ thì sửa sao?

Dùng static có hash tên file và kiểm tra header Cache-Control; nếu vẫn cũ, xóa cache trình duyệt rồi mới nghi tới CDN.

Nguồn tham khảo

Tài liệu Django: Managing static files, File uploads

Tài liệu WhiteNoise và django-storages

Submitted links

Pages submitted to IndexVero for crawler discovery. They belong to the people who submitted them.

More posts