Introduction to Django Static Files
This article introduces Django static files and explains how to organize, reference, collect, and serve CSS, JavaScript, images, fonts, and other frontend assets. It covers app-level and project-level static directories, STATIC_URL, STATICFILES_DIRS, STATIC_ROOT, template usage, collectstatic, production serving, WhiteNoise, debugging, and common mistakes.
Introduction to Django Static Files
Most web applications need files that are not generated dynamically by Python.
Examples include:
- CSS stylesheets
- JavaScript files
- logos
- icons
- background images
- fonts
- frontend libraries
Django refers to these files as static files.
Static files are different from uploaded media.
Static files are part of the application’s source code and are usually created by developers. Uploaded media is created or submitted by users while the application is running.
A simple distinction is:
⧉
1 2 3 4 5 | |
Django provides the django.contrib.staticfiles application to locate, organize, collect, and serve static assets.
What Are Static Files?
A static file is a file that the server can return without generating its contents for each request.
For example, a stylesheet may contain:
⧉
1 2 3 4 | |
The file does not need to be rebuilt every time a user opens a page.
A browser requests it directly:
⧉
1 | |
Django templates can generate the correct URL for that file.
⧉
1 2 3 4 5 6 | |
Enabling Static Files
A standard Django project usually includes the static-files application by default.
Check INSTALLED_APPS in settings.py:
⧉
1 2 3 4 5 6 7 8 | |
The important entry is:
⧉
1 | |
The project should also define STATIC_URL:
⧉
1 | |
This is the URL prefix used for static assets.
For example:
⧉
1 2 3 | |
App-Level Static Files
Each Django app can contain its own static files.
A common app structure is:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
The extra blog/ directory inside static/ is important.
The full path is:
⧉
1 | |
The file is referenced as:
⧉
1 | |
This namespacing prevents filename collisions.
Without namespacing, two apps might both contain:
⧉
1 | |
Django would find one of them first, and the result could depend on app order.
Using app names creates unique paths:
⧉
1 2 3 | |
Loading Static Files in Templates
Before using the static template tag, load it:
⧉
1 | |
Then reference a file:
⧉
1 2 3 4 | |
JavaScript:
⧉
1 2 3 4 | |
Image:
⧉
1 2 3 4 | |
The static tag builds the URL using the project’s static-file configuration.
Avoid hard-coding URLs like this:
⧉
1 | |
The hard-coded path may work locally, but the final URL may differ in production.
Prefer:
⧉
1 2 3 4 5 6 | |
Project-Level Static Files
Some static assets belong to the entire project rather than one app.
Examples include:
- the main site stylesheet
- shared JavaScript
- a company logo
- global icons
- a design system
A project-level static directory might look like:
⧉
1 2 3 4 5 6 7 8 9 10 11 | |
Tell Django to search this directory with STATICFILES_DIRS:
⧉
1 2 3 | |
The files can then be referenced as:
⧉
1 2 3 4 5 6 | |
App-Level and Project-Level Static Files
A project may use both approaches.
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Use project-level static files for assets shared across the entire site.
Use app-level static files for assets owned by one reusable or isolated app.
STATIC_URL
STATIC_URL defines the public URL prefix for static files.
⧉
1 | |
This usually produces URLs such as:
⧉
1 | |
It can also be written with a leading slash:
⧉
1 | |
In production, it may point to a separate static-file host:
⧉
1 | |
Templates should continue using the static tag, so they do not need to know the final deployment URL.
STATICFILES_DIRS
STATICFILES_DIRS lists additional directories Django should search for static assets.
Example:
⧉
1 2 3 | |
Multiple directories may be included:
⧉
1 2 3 4 | |
These directories are separate from the app-level static directories that Django discovers automatically.
STATIC_ROOT
STATIC_ROOT is the directory where Django collects static files for production.
Example:
⧉
1 | |
This directory is normally populated by:
⧉
1 | |
A typical configuration is:
⧉
1 2 3 4 5 6 7 | |
These settings have different purposes:
| Setting | Purpose |
|---|---|
STATIC_URL |
Public URL prefix |
STATICFILES_DIRS |
Additional source directories |
STATIC_ROOT |
Final collected production directory |
Do not normally use the same directory for both STATICFILES_DIRS and STATIC_ROOT.
Incorrect:
⧉
1 2 3 4 5 | |
This mixes source assets with collected output.
Prefer:
⧉
1 2 3 4 5 | |
Static Files During Development
When DEBUG=True, Django’s development server can serve static files automatically if django.contrib.staticfiles is installed.
Run:
⧉
1 | |
A file referenced as:
⧉
1 | |
may be available at:
⧉
1 | |
This behavior is intended for development.
Django’s development server is not designed to be a high-performance production static-file server.
The collectstatic Command
Production deployments usually gather all static assets into one directory.
Run:
⧉
1 | |
Django searches:
- app-level
staticdirectories - directories listed in
STATICFILES_DIRS - other configured static-file sources
It copies the discovered files into STATIC_ROOT.
For example:
⧉
1 2 3 4 5 | |
After collectstatic:
⧉
1 2 3 4 5 6 7 8 9 | |
A production web server or storage service can then serve the staticfiles directory.
Why collectstatic Exists
Django projects may contain static assets in many locations.
Without collection, a production server would need to understand the internal directory structure of every installed app.
collectstatic creates one deployment directory containing all required assets.
The deployment flow becomes:
⧉
1 2 3 4 5 6 7 8 9 | |
Running collectstatic
A common production command is:
⧉
1 | |
--noinput prevents interactive confirmation prompts.
This is useful in automated deployment pipelines.
When the command runs again, Django may overwrite changed files and leave unchanged files in place.
Finding Static Files
Django provides the findstatic command to locate a file.
⧉
1 | |
For an app-namespaced file:
⧉
1 | |
Verbose output can show all searched locations:
⧉
1 | |
This command is useful when:
- Django cannot find an asset
- the wrong file is being used
- two files have the same path
- a project directory is misconfigured
Static File Finders
Django uses static-file finders to locate assets.
The default configuration typically includes:
⧉
1 2 3 4 5 6 7 8 9 10 | |
FileSystemFinder searches directories in STATICFILES_DIRS.
AppDirectoriesFinder searches static directories inside installed apps.
Most projects do not need to change this setting.
File Name Collisions
Django uses the first static file it finds for a particular path.
Suppose two apps contain:
⧉
1 2 | |
Both use the same static path:
⧉
1 | |
One file will hide the other.
Avoid this by adding an app namespace:
⧉
1 2 | |
Then reference them separately:
⧉
1 | |
⧉
1 | |
Static Images in CSS
A CSS file may reference another static asset.
Suppose the structure is:
⧉
1 2 3 4 5 | |
Inside site.css:
⧉
1 2 3 | |
The path is relative to the CSS file.
Django template tags do not run inside ordinary CSS files.
This will not work inside site.css:
⧉
1 2 3 4 | |
Template tags are processed only in Django templates.
Use relative paths in static CSS files, or generate inline CSS in a Django template when necessary.
Adding CSS to a Base Template
A common base template is:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | |
A child template can add app-specific assets:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Static JavaScript Files
A JavaScript file can be added in the same way as a stylesheet.
Project structure:
⧉
1 2 3 | |
Template:
⧉
1 2 3 4 5 6 | |
Using defer tells the browser to download the script without blocking HTML parsing and execute it after the document has been parsed.
A JavaScript file might contain:
⧉
1 2 3 | |
Static Files and the Django Admin
The Django admin uses static assets for its CSS, JavaScript, and images.
These assets come from django.contrib.admin.
During collectstatic, Django also collects the admin’s static files.
A production deployment where the admin appears unstyled often has one of these problems:
collectstaticwas not runSTATIC_ROOTis not served- the static URL is misconfigured
- the web server cannot access the collected files
Static Files and Uploaded Media
Static files and media files should use separate settings.
Example:
⧉
1 2 3 4 5 | |
Static example:
⧉
1 | |
Media example:
⧉
1 | |
A model upload uses media storage:
⧉
1 2 3 4 | |
The uploaded image does not belong in the static directory.
Serving Media During Development
Static files are normally handled automatically by django.contrib.staticfiles during development.
Uploaded media often needs an additional development URL pattern:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
This helper is for development only.
It should not be treated as a production media-serving solution.
Production Static-File Serving
In production, static files are commonly served by:
- Nginx
- Apache
- a cloud object-storage service
- a content delivery network
- a platform-specific static-file service
- a package such as WhiteNoise
Django usually handles dynamic requests, while a specialized server handles static files.
⧉
1 2 3 4 | |
This is more efficient than sending every CSS, JavaScript, and image request through Django.
Serving Static Files with Nginx
A conceptual Nginx configuration might look like:
⧉
1 2 3 | |
The path should point to STATIC_ROOT.
Django settings:
⧉
1 2 | |
Deployment command:
⧉
1 | |
Nginx then serves files directly from the collected directory.
WhiteNoise
WhiteNoise allows a Django application to serve its own static assets in production.
Install it:
⧉
1 | |
Add its middleware near the top of the middleware list:
⧉
1 2 3 4 5 | |
A simple static configuration might include:
⧉
1 2 | |
WhiteNoise is useful for deployments where using a separate Nginx server or object-storage service would add unnecessary complexity.
It is especially common on application-hosting platforms.
Hashed Static File Names
Browsers cache static files.
This improves performance, but it creates a problem when a file changes.
Suppose the browser caches:
⧉
1 | |
You deploy a new version with the same URL. The browser may continue using the old cached file.
Hashed filenames solve this problem:
⧉
1 | |
When the file changes, the hash changes:
⧉
1 | |
The browser sees a new URL and downloads the new file.
A production storage backend can provide this behavior through Django’s static-file storage configuration.
For example, WhiteNoise commonly uses compressed manifest storage.
The exact setting depends on the Django version and storage configuration, but the goal is the same:
- add content hashes to filenames
- compress assets
- support long browser-cache lifetimes
Static Files and Frontend Build Tools
Modern projects may use tools such as:
- Vite
- Webpack
- Sass
- Tailwind CSS
- TypeScript
- React
- Vue
These tools produce compiled assets.
For example:
⧉
1 2 3 4 5 6 7 | |
A Vite build might produce:
⧉
1 2 3 4 5 | |
Django can be configured to collect these compiled assets by adding the generated directory to STATICFILES_DIRS:
⧉
1 2 3 | |
However, build tools often generate hashed filenames and manifests. A reliable integration may require:
- reading the frontend build manifest
- using a Django integration package
- copying generated assets into a known structure
- serving the frontend separately
Do not hard-code generated filenames that may change on every build.
Organizing Static Files
A simple project-wide layout is:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
A larger project may group assets by feature:
⧉
1 2 3 4 5 6 7 8 9 10 11 | |
The best structure depends on the application.
The important points are:
- keep paths predictable
- avoid filename collisions
- separate source assets from collected output
- use app namespaces for reusable apps
Common Beginner Mistakes
Forgetting {% load static %}
Incorrect:
⧉
1 2 3 4 | |
Correct:
⧉
1 2 3 4 5 6 | |
Hard-Coding Static URLs
Avoid:
⧉
1 | |
Prefer:
⧉
1 2 3 4 5 6 | |
Using the Wrong Static Path
Given:
⧉
1 | |
Use:
⧉
1 | |
Do not use:
⧉
1 | |
The static/ directory itself is not part of the referenced path.
Forgetting App Namespacing
Avoid:
⧉
1 2 | |
Prefer:
⧉
1 2 | |
Putting Uploaded Files in static/
User uploads belong in MEDIA_ROOT, not in static source directories.
Using STATIC_ROOT as a Source Directory
STATIC_ROOT is collected output.
Do not manually develop files there because collectstatic may overwrite them.
Forgetting collectstatic in Production
If the application works locally but has no CSS in production, verify that this command ran:
⧉
1 | |
Expecting Django to Serve Static Files in Production Automatically
The development server’s static-file behavior does not represent a complete production setup.
Configure a production static-file server, storage service, or suitable middleware.
Confusing STATICFILES_DIRS and STATIC_ROOT
Remember:
⧉
1 2 3 4 5 | |
Writing Template Tags Inside CSS
This does not work in an ordinary static CSS file:
⧉
1 2 | |
Use a relative path or inline template-generated CSS.
Debugging Static Files
When an asset does not load, first inspect the browser’s network panel.
Check:
- the requested URL
- the response status
- whether the response is CSS, JavaScript, HTML, or an error page
- whether the file is cached
Then check Django’s file discovery:
⧉
1 | |
Review the settings:
⧉
1 2 3 4 5 6 7 | |
Check the actual source file:
⧉
1 | |
Check the template reference:
⧉
1 2 3 4 5 6 | |
For production, confirm that the collected file exists:
⧉
1 | |
A Complete Basic Setup
Project structure:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Settings:
⧉
1 2 3 4 5 6 7 | |
Template:
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | |
Development:
⧉
1 | |
Production collection:
⧉
1 | |
Recommended Workflow
During development:
- Add app-specific assets to the app’s namespaced
staticdirectory. - Add shared assets to a project-level static directory.
- Reference assets with
{% static %}. - Test with the development server.
- Use
findstaticwhen a file cannot be located.
During deployment:
- Build frontend assets if a build tool is used.
- Run
collectstatic. - Serve
STATIC_ROOTthrough a production static-file system. - Verify CSS, JavaScript, images, and admin assets.
- Configure caching and hashed filenames when appropriate.
Mini Reference
⧉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Basic settings:
⧉
1 2 3 4 5 6 7 | |
Basic template usage:
⧉
1 2 3 4 5 6 | |
App-level asset:
⧉
1 | |
Template path:
⧉
1 | |
Production command:
⧉
1 | |
Django static files are the CSS, JavaScript, images, fonts, and other developer-controlled assets used by an application.
The main ideas are:
django.contrib.staticfilesmanages static assets- app-level files belong inside app
staticdirectories - reusable apps should namespace their static paths
- project-wide assets can be listed through
STATICFILES_DIRS - templates should use the
{% static %}tag STATIC_URLdefines the public asset prefixSTATIC_ROOTstores collected production filescollectstaticprepares assets for deployment- static files and uploaded media are separate concepts
- production assets should be served by an appropriate static-file system
For a small project, begin with one project-level static directory and the static template tag. Add app-level namespacing, asset compilation, hashed filenames, and external storage only when the project requires them.
Join the Newsletter
Practical insights on Django, backend systems, deployment, architecture, and real-world development — delivered without noise.
Get updates when new guides, learning paths, cheat sheets, and field notes are published.
No spam. Unsubscribe anytime.
There is no third-party involved so don't worry - we won't share your details with anyone.