Статические файлы в Django: пошаговая настройка от нуля
CSS и JavaScript не грузятся, картинки отдают 404, а на проде вместо стилей белый экран. Разбираем, как в Django 5.x правильно настроить статику для разработки и боевого сервера.

После python manage.py runserver сайт открывается, но выглядит голым: стили не подхватились, скрипты молчат, в консоли браузера — вереница 404 на /static/…. А после деплоя на боевой сервер вместо оформленной страницы вообще белый лист. Причина почти всегда одна: статика в Django устроена не так, как ждёт новичок, и настраивается по-разному для разработки и продакшена. К концу этой инструкции у вас будет рабочая раздача статики в обоих режимах. Займёт минут двадцать.
Статья написана для Django 5.x (проверяемо и на 4.2 LTS — отличия оговорены отдельно). Предполагается, что вы уже создали проект командой django-admin startproject и хотя бы одно приложение.
Что нужно иметь до начала
- Установленный Python 3.10+ и Django 5.x (
python -m django --versionпокажет версию). - Созданный проект с файлом
settings.pyи рабочийmanage.py. - Хотя бы одно приложение в списке
INSTALLED_APPS. По умолчанию туда уже входитdjango.contrib.staticfiles— именно он отвечает за поиск и сбор статики. Проверьте, что вы его случайно не удалили. - Для боевого режима — установленный
gunicornили другой WSGI-сервер и (по желанию) пакетwhitenoise.
Два понятия, которые путают все новички
В Django есть статика (statics) и медиа (media). Это разные вещи, и настраиваются они разными параметрами.
- Статика — то, что вы пишете сами и кладёте в репозиторий: CSS, JS, иконки, шрифты. Управляется
STATIC_URL,STATICFILES_DIRS,STATIC_ROOT. - Медиа — то, что загружают пользователи: аватарки, вложения. Управляется
MEDIA_URLиMEDIA_ROOT. В этой инструкции разбираем только статику; медиа — отдельная тема.
Шаг 1. Проверяем базовые настройки
Откройте settings.py. Django при создании проекта уже прописывает одну строку:
# settings.py (Django 5.x)
STATIC_URL = 'static/'
Это URL-префикс, по которому браузер запрашивает статику. С такой настройкой файл style.css будет доступен по адресу /static/style.css. Убедитесь, что строка на месте.
Проверка: в файле есть STATIC_URL, а в INSTALLED_APPS присутствует 'django.contrib.staticfiles'.
Шаг 2. Раскладываем файлы правильно
Django ищет статику в двух местах: внутри каждого приложения в папке static/ и в дополнительных каталогах из STATICFILES_DIRS. Есть подвох: если положить файл в myapp/static/style.css, то при наличии такого же файла в другом приложении они конфликтуют по имени. Поэтому файлы принято класть с подпапкой-неймспейсом по имени приложения:
myapp/
static/
myapp/
css/
style.css
js/
main.js
Тогда в шаблоне вы обращаетесь к myapp/css/style.css, и коллизий не будет. Если хотите общий каталог статики на весь проект (не привязанный к приложению) — создайте папку static/ в корне и добавьте её в настройки:
# settings.py
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
STATICFILES_DIRS = [
BASE_DIR / 'static',
]
Проверка: файл лежит по пути вида myapp/static/myapp/css/style.css либо в корневой папке из STATICFILES_DIRS.
Шаг 3. Подключаем статику в шаблоне
Прямые пути вроде /static/myapp/css/style.css в шаблоне писать не стоит: при смене STATIC_URL или включении хешированных имён всё сломается. Используйте тег {% static %}.
<!-- В самом верху шаблона -->
{% load static %}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="{% static 'myapp/css/style.css' %}">
</head>
<body>
<script src="{% static 'myapp/js/main.js' %}"></script>
</body>
</html>
Строку {% load static %} нужно ставить в каждом шаблоне, где используется тег — она не наследуется из base-шаблона автоматически, если блок с тегом определён в дочернем.
Проверка: запустите python manage.py runserver, откройте страницу. Стили должны примениться. В инструменте разработчика (F12) на вкладке Network запрос к CSS вернёт статус 200, а не 404.
Шаг 4. Понимаем, почему на runserver всё работает, а на проде нет
Встроенный сервер разработки раздаёт статику сам, только когдаDEBUG = True. На боевом сервере сDEBUG = FalseDjango статику не отдаёт вообще — это не его работа.
Именно поэтому первое, что видит новичок после деплоя, — сайт без стилей. В продакшене статику должен отдавать либо веб-сервер (Nginx), либо специальная прослойка (WhiteNoise). Django лишь собирает файлы в одно место командой collectstatic.
Шаг 5. Готовим статику к продакшену
Добавьте в settings.py параметр STATIC_ROOT — папку, куда команда соберёт всю статику из приложений и STATICFILES_DIRS в один каталог:
# settings.py
STATIC_ROOT = BASE_DIR / 'staticfiles'
Важно: STATIC_ROOT и папки из STATICFILES_DIRS — это разные каталоги, они не должны совпадать. STATICFILES_DIRS — откуда собираем, STATIC_ROOT — куда. Если укажете одинаковый путь, collectstatic выдаст ошибку.
Теперь запустите сбор:
python manage.py collectstatic
Команда спросит подтверждение и скопирует все найденные файлы в staticfiles/. Папку STATIC_ROOT добавьте в .gitignore — её содержимое генерируется, в репозитории ему не место.
Проверка: после команды в каталоге staticfiles/ появятся ваши CSS и JS, а также статика админки Django.
Шаг 6. Раздача на проде: два рабочих варианта
Вариант A — WhiteNoise (проще всего)
Если не хотите настраивать Nginx отдельно, статику может отдавать сам Python-процесс через WhiteNoise. Установите пакет и добавьте middleware сразу после SecurityMiddleware:
pip install whitenoise
# settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
# ... остальные middleware
]
# Хешированные имена + сжатие (Django 5.x)
STORAGES = {
'default': {
'BACKEND': 'django.core.files.storage.FileSystemStorage',
},
'staticfiles': {
'BACKEND': 'whitenoise.storage.CompressedManifestStaticFilesStorage',
},
}
Обратите внимание: в Django 4.2+ настройка хранилищ переехала в словарь STORAGES. Старый параметр STATICFILES_STORAGE в 5.x помечен устаревшим — используйте STORAGES.
Вариант B — Nginx (для нагруженного проекта)
Отдавать статику напрямую с Nginx быстрее: Python-процесс не тратится на файлы. В конфиге сайта добавьте location, указывающий на STATIC_ROOT:
# /etc/nginx/sites-available/mysite
location /static/ {
alias /var/www/mysite/staticfiles/;
}
Путь после alias должен совпадать с вашим реальным STATIC_ROOT на сервере. После правки — sudo nginx -t и перезагрузка.
Чем варианты отличаются
| Критерий | WhiteNoise | Nginx |
|---|---|---|
| Настройка | только Python, пара строк | отдельный конфиг веб-сервера |
| Скорость | достаточная для среднего трафика | выше, файлы мимо Python |
| Кому подходит | небольшие сайты, контейнеры, PaaS | высоконагруженные проекты, свой VPS |
| Кеш-хеши в именах | из коробки через storage-бэкенд | настраивается отдельно |
Если не получилось
- В консоли браузера 404 на
/static/…при runserver. Файл лежит не там, где Django его ищет, либо неверный путь в теге. Проверьте раскладку из шага 2 и точное имя в{% static %}. - Ошибка
Invalid template libraryили тег не работает. Забыли{% load static %}в начале шаблона. - После деплоя стилей нет, но локально всё было. На сервере
DEBUG = False, аcollectstaticне запущен или Nginx/WhiteNoise не настроены. Пройдите шаги 5 и 6. ValueError: Missing staticfiles manifest entry for …. Вы включилиManifestStaticFilesStorage, но не пересобрали статику после изменения файла. Запуститеcollectstaticзаново.collectstaticругается, чтоSTATICFILES_DIRSсовпадает сSTATIC_ROOT. Разведите каталоги: собирать и складывать нужно в разные папки.
Что делать дальше
Базовая раздача готова. Дальше стоит настроить кеширование на стороне браузера: хешированные имена файлов (через Manifest-хранилище) позволяют выставить длинный Cache-Control без риска отдать пользователю устаревший CSS. Затем — медиафайлы, если у вас есть пользовательские загрузки: это отдельная связка MEDIA_URL и MEDIA_ROOT, которую нельзя раздавать теми же правилами, что и статику. Для контейнеров и облачных хранилищ смотрите в сторону django-storages с бэкендом под ваш S3-совместимый сервис.
В проде мониторьте долю 404 в логах Nginx на путях /static/: их всплеск после релиза обычно означает, что забыли прогнать collectstatic.
Логотип и фирменный стиль: Три концепции разного характера, палитра с обоснованием, проверка на читаемость в малом размере и в один цвет.
Спросить за 60 ₽Источники: Django documentation — Managing static files, WhiteNoise — Using WhiteNoise with Django
Частые вопросы
У меня Django 4.2, а не 5.x — инструкция подойдёт?
Обязательно ли использовать WhiteNoise?
Что будет, если я не запущу collectstatic на проде?
Можно ли держать DEBUG=True на проде, чтобы Django сам раздавал статику?
Почему после включения хешей файлы называются style.a3f5c2.css и как на них ссылаться?
Куда девать медиафайлы, которые загружают пользователи?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.