Django MEDIA_URL and MEDIA_ROOT
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
MEDIA_ROOT and MEDIA_URL are two of the most frequently confused Django settings. They solve different parts of the same problem: where uploaded files are stored and how those files are reached from a browser. Once you separate filesystem paths from public URLs, the distinction becomes clear.
MEDIA_ROOT Is a Filesystem Path
MEDIA_ROOT tells Django where uploaded files live on disk. It should be an absolute path pointing to a directory on the server.
A common setup in settings.py looks like this:
If a model has an ImageField(upload_to="avatars/"), Django stores the uploaded file somewhere under MEDIA_ROOT, such as:
That is storage, not a browser URL.
MEDIA_URL Is the Public URL Prefix
MEDIA_URL is the URL prefix used when building links to uploaded files. If MEDIA_URL = "/media/", then the file above would typically be reachable at:
That is why the two settings work together:
- '
MEDIA_ROOTsays where the file is stored' - '
MEDIA_URLsays what URL prefix points to that storage'
They should not be the same kind of value. One is a filesystem path, the other is a URL prefix.
A Working Model Example
Here is a simple model using uploaded media:
When a file is uploaded, Django stores it under MEDIA_ROOT / "avatars" and the field can expose a URL through avatar.url, which is based on MEDIA_URL.
For example in a template:
That is the normal flow: store with MEDIA_ROOT, render with MEDIA_URL.
Development Serving Is Separate
In development, Django can serve media files for you if you add the usual URL helper:
This is convenient for local development. It is not how production should usually work.
Production Uses a Real Web Server or Object Storage
In production, uploaded files are usually served by:
- Nginx
- Apache
- a cloud object store such as S3
- a CDN in front of that storage
Django should usually not serve large volumes of user-uploaded files itself. The settings still matter, but the actual serving path may be handled by infrastructure outside the Django process.
That is also why MEDIA_URL can point to a different domain entirely in some deployments, such as a CDN or storage bucket URL prefix.
Do Not Mix Media and Static Files
MEDIA_* is for user-uploaded content. STATIC_* is for application assets such as CSS, JavaScript, and bundled images.
That distinction matters because the lifecycle is different:
- static files are built and deployed with the app
- media files are created by users at runtime
Using the same directory or URL scheme for both is a common mistake and usually causes deployment confusion later.
Common Pitfalls
- Setting
MEDIA_ROOTto a URL instead of a filesystem path. - Setting
MEDIA_URLto a local disk path instead of a browser-facing URL prefix. - Forgetting to configure media serving during development and then assuming uploads failed.
- Using Django to serve media in production when a proper web server or storage backend should handle it.
- Mixing media settings with static-file settings as if they solved the same problem.
Summary
- '
MEDIA_ROOTis the absolute filesystem location for uploaded files.' - '
MEDIA_URLis the public URL prefix used to reach those files.' - Uploaded model fields usually store under
MEDIA_ROOTand expose URLs underMEDIA_URL. - Django can serve media in development, but production usually delegates that to web infrastructure.
- Keep media and static files conceptually and operationally separate.

