Static Files & Media

Django separates two kinds of files that aren't part of your Python code: static files — CSS, JavaScript, images that ship with your app — and media files — content your users upload, like a profile picture.

Serving static files

Static files live in a static/ folder inside an app, and get referenced in templates with the {% static %} tag rather than a hardcoded path:

HTML blog/templates/home.html
{% load static %}
<link rel="stylesheet" href="{% static 'blog/style.css' %}">
<img src="{% static 'blog/logo.png' %}" alt="Logo">
Python mysite/settings.py
STATIC_URL = 'static/'

{% load static %} must appear before any {% static %} tag is used. Using the tag instead of a plain href="/static/blog/style.css" matters because it respects STATIC_URL automatically — if that setting ever changes (say, when deploying behind a CDN with a different prefix), every template using {% static %} updates with it, with zero find-and-replace.

Media files: user uploads

Media files need two settings — where they're stored on disk, and what URL prefix serves them:

Python mysite/settings.py
MEDIA_URL = 'media/'
MEDIA_ROOT = BASE_DIR / 'media'

A model field for an upload uses ImageField or the more general FileField, and Django handles saving the uploaded file to MEDIA_ROOT automatically:

Python blog/models.py
class Post(models.Model):
    title = models.CharField(max_length=200)
    cover_image = models.ImageField(upload_to='covers/', blank=True)

upload_to='covers/' means an uploaded file for this field is saved to MEDIA_ROOT/covers/, and post.cover_image.url in a template gives you the full URL to display it.

During development

In development, Django's own dev server can serve media files directly, with one line added to the project's urls.py:

Python mysite/urls.py
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    # ... your other patterns
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Note: that static() helper for serving media is explicitly a development-only convenience — Django's own documentation says so directly. In production, a real web server (nginx, or a cloud storage service like S3) serves these files instead, since the Django dev server isn't built for that kind of traffic or file handling at scale.