Django distinguishes two kinds of files: static files (assets that are part of your application — CSS, JavaScript, images you ship) and media files (files uploaded by users at runtime — profile pictures, documents). They're handled differently because their sources and lifecycles differ fundamentally.
Static files — your application's assets
# settings.py
STATIC_URL = "/static/"
STATICFILES_DIRS = [BASE_DIR / "static"] # where YOU put source static files
STATIC_ROOT = BASE_DIR / "staticfiles" # where collectstatic GATHERS them for prod
<!-- in templates -->
{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<img src="{% static 'images/logo.png' %}">
Static files are part of your codebase — CSS, JS, fonts, design images. They're version-controlled, the same for all users, and served from your project's static directories.
collectstatic for production
python manage.py collectstatic
# gathers ALL static files (from your apps and STATICFILES_DIRS) into STATIC_ROOT,
# so a web server / CDN can serve them efficiently in production
In production, collectstatic consolidates static files from all apps into one directory (STATIC_ROOT) for the web server or CDN to serve — Django doesn't serve them itself in production.
Media files — user uploads
# settings.py
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media" # where UPLOADED files are stored
# a model with an uploadable file
class Profile(models.Model):
avatar = models.ImageField(upload_to="avatars/") # stored under MEDIA_ROOT/avatars/
document = models.FileField(upload_to="docs/")
profile.avatar.url # the URL to access the uploaded file
Media files are uploaded by users at runtime via FileField/ImageField. They're dynamic, NOT in version control, and grow over time as users upload content.
The key differences
Static files Media files
Source part of your code uploaded by users at runtime
Version control YES (committed) NO (user-generated, .gitignored)
Lifecycle fixed (changes with code) dynamic (grows continuously)
Examples CSS, JS, logos, fonts avatars, uploaded documents, photos
Settings STATIC_URL/ROOT/DIRS MEDIA_URL/ROOT
Production collectstatic + CDN/server stored on disk/cloud (S3), served carefully
Production considerations
✓ Static → serve via the web server (nginx) or a CDN (cached, fast)
✓ Media → store on cloud storage (S3) for scalability/persistence; in multi-server
setups, local disk doesn't work (files must be shared) → use S3/object storage
✓ Security → VALIDATE uploaded files (type, size); be careful serving user uploads
(a user could upload malicious content)
Why it matters
Understanding the static-vs-media distinction is important for both correctly building and properly deploying Django applications, since the two file types have fundamentally different sources, lifecycles, and handling requirements. Static files (your application's own CSS, JavaScript, images, fonts) are part of the codebase, version-controlled, and the same for everyone — referenced via {% static %} and consolidated with collectstatic for production serving by a web server or CDN. Media files (user uploads via FileField/ImageField) are dynamic, user-generated content created at runtime, NOT in version control, and growing continuously.
Confusing them or misconfiguring their settings (STATIC_* vs MEDIA_*) is a common source of "my CSS isn't loading" or "uploads don't work" problems, especially in production where Django doesn't serve static files itself (requiring collectstatic and a proper server/CDN).
The distinction also has important production and security implications: media files in multi-server deployments can't rely on local disk (they must use shared cloud storage like S3), and user uploads require validation (file type, size) and careful serving to avoid security risks from malicious content.
Knowing how to configure and handle both types, the role of collectstatic, and the production considerations (CDN for static, cloud storage for media, upload security) is practical, frequently-needed knowledge for building and deploying real Django applications correctly — a common stumbling block that this understanding resolves.
