Django whitenoise is fine for production but gives 404 for debug mode
Question:
I use django white noise
and it works when DEBUG = False
; however when setting DEBUG = True
, I cannot load the image anymore and get 404 response.
Note: I am aware of hard-refresh. I already used static collection and django migrations.
Here is my code:
django-admin startproject mysite
cd mysite/
python3 manage.py migrate
(after setup settings.py):
python3 manage.py collectstatic
pip show whitenoise|grep Version
Version: 6.2.0
mysite/mysite/wsgi.py
import os
from whitenoise import WhiteNoise
from django.core.wsgi import get_wsgi_application
from mysite.settings import BASE_DIR
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
application = get_wsgi_application()
application = WhiteNoise(application, root=os.path.join(BASE_DIR, "staticfiles") )
mysite/mysite/asgi.py
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
application = get_asgi_application()
mysite/mysite/urls.py
from django.contrib import admin
from django.urls import include, path
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
]
mysite/mysite/settings.py
from pathlib import Path
import os
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = 'django-insecure-u5pg-ef=-o0zzi16fk)xf1p8dsz7t$$vayc$3x1y(r01kb0rkg'
DEBUG = True
ALLOWED_HOSTS = ['*']
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'mysite.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'mysite.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
STATICFILES_STORAGE = "whitenoise.storage.CompressedStaticFilesStorage"
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
WHITENOISE_MANIFEST_STRICT = False
Copy the image and make sure the image does exist:
ls staticfiles/images/hi.jpg
staticfiles/images/hi.jpg
The url to load:
http://127.0.0.1:8000/static/images/hi.jpg
When DEBUG=True
in the settings:
But when DEBUG=False
,
Last failed try:
DEBUG = True
STATICFILES_STORAGE = "whitenoise.storage.CompressedStaticFilesStorage"
STATIC_URL = '/static/'
STATIC_ROOT = None if DEBUG else os.path.join(BASE_DIR, 'staticfiles')
WHITENOISE_MANIFEST_STRICT = False
.
Answers:
I always configure the STATIC_ROOT
like this:
STATIC_ROOT = None if DEBUG else BASE_DIR / "staticfiles"
for more info on why and how does this work you could read this
In debug mode, Django takes over the handling of the static files, which means that by default django looks for static files in this folder my_app/static/my_app/example.jpg
, if you had placed static files somewhere else you probably need to add it to STATICFILES_DIRS=[]
.
If you don’t have django.contrib.staticfiles
in your installed apps, you may also want to try adding the below in your urls.py
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
If you want whitenoise in development, you will need to runsever with -nostatic
option turned on. If you don’t want to write -nostatic
command everytime then just add whitenoise.runserver_nostatic
to your INSTALLED_APPS
.
refer the documentation for more information: http://whitenoise.evans.io/en/stable/django.html#using-whitenoise-in-development
The solution for me is as follows:
settings.py
if DEBUG:
STATICFILES_DIRS=['staticfiles']
STATIC_ROOT = None
else:
STATICFILES_DIRS=[]
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
I use django white noise
and it works when DEBUG = False
; however when setting DEBUG = True
, I cannot load the image anymore and get 404 response.
Note: I am aware of hard-refresh. I already used static collection and django migrations.
Here is my code:
django-admin startproject mysite
cd mysite/
python3 manage.py migrate
(after setup settings.py):
python3 manage.py collectstatic
pip show whitenoise|grep Version
Version: 6.2.0
mysite/mysite/wsgi.py
import os
from whitenoise import WhiteNoise
from django.core.wsgi import get_wsgi_application
from mysite.settings import BASE_DIR
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
application = get_wsgi_application()
application = WhiteNoise(application, root=os.path.join(BASE_DIR, "staticfiles") )
mysite/mysite/asgi.py
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
application = get_asgi_application()
mysite/mysite/urls.py
from django.contrib import admin
from django.urls import include, path
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
]
mysite/mysite/settings.py
from pathlib import Path
import os
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = 'django-insecure-u5pg-ef=-o0zzi16fk)xf1p8dsz7t$$vayc$3x1y(r01kb0rkg'
DEBUG = True
ALLOWED_HOSTS = ['*']
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'mysite.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'mysite.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
STATICFILES_STORAGE = "whitenoise.storage.CompressedStaticFilesStorage"
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
WHITENOISE_MANIFEST_STRICT = False
Copy the image and make sure the image does exist:
ls staticfiles/images/hi.jpg
staticfiles/images/hi.jpg
The url to load:
http://127.0.0.1:8000/static/images/hi.jpg
When DEBUG=True
in the settings:
But when DEBUG=False
,
Last failed try:
DEBUG = True
STATICFILES_STORAGE = "whitenoise.storage.CompressedStaticFilesStorage"
STATIC_URL = '/static/'
STATIC_ROOT = None if DEBUG else os.path.join(BASE_DIR, 'staticfiles')
WHITENOISE_MANIFEST_STRICT = False
.
I always configure the STATIC_ROOT
like this:
STATIC_ROOT = None if DEBUG else BASE_DIR / "staticfiles"
for more info on why and how does this work you could read this
In debug mode, Django takes over the handling of the static files, which means that by default django looks for static files in this folder my_app/static/my_app/example.jpg
, if you had placed static files somewhere else you probably need to add it to STATICFILES_DIRS=[]
.
If you don’t have django.contrib.staticfiles
in your installed apps, you may also want to try adding the below in your urls.py
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
If you want whitenoise in development, you will need to runsever with -nostatic
option turned on. If you don’t want to write -nostatic
command everytime then just add whitenoise.runserver_nostatic
to your INSTALLED_APPS
.
refer the documentation for more information: http://whitenoise.evans.io/en/stable/django.html#using-whitenoise-in-development
The solution for me is as follows:
settings.py
if DEBUG:
STATICFILES_DIRS=['staticfiles']
STATIC_ROOT = None
else:
STATICFILES_DIRS=[]
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')