whitenoise
whitenoise copied to clipboard
WHITENOISE_STATIC_PREFIX still required if django app deployed on subpath
The docs for WHITENOISE_STATIC_PREFIX currently say
Default: Path component of settings.STATIC_URL (with settings.FORCE_SCRIPT_NAME removed if set) .... If your application is not running at the root of the domain and FORCE_SCRIPT_NAME is set then this value will be removed from the STATIC_URL path first to give the correct prefix.
I understand this to mean if in my settings.py I have
FORCE_SCRIPT_NAME = "/dev"
STATIC_URL = "{}/static/".format(FORCE_SCRIPT_NAME)
WHITENOISE_STATIC_PREFIX
will automatically be /static/
However, this is not the case and i still need to explicitly set in my settings.py
WHITENOISE_STATIC_PREFIX = "/static/"
otherwise static files will not work.
Is this a bug, or am I misunderstanding something in the docs? I did see https://github.com/evansd/whitenoise/issues/164 which seems similar but different enough (no mention of FORCE_SCRIPT_NAME
) where it explicitly mentions needing to do this, but that issue is from 2017 and seems to contradict the above documentation.
am using
- whitenoise 5.2.0
- zappa 0.52.0
- django 3.1.4
Your reading of the docs is correct so this does sound like a bug, but I'm struggling to understand what might be causing it.
Can you post a minimal reproducing example somewhere?
I recently ran into something similar in https://github.com/ome/omero-web-docker. That's definitely not a minimal example, but I'm pretty sure it used to work, so if I get time I might be able to pin down the version where it broke.
https://github.com/evansd/whitenoise/pull/259 is the problematic PR in my case, 5.1.0 works, 5.2.0 doesn't. I'm using Django 1.11 with something like
FORCE_SCRIPT_NAME = "/prefix"
STATIC_URL = "/prefix/static/"
The related issue https://github.com/evansd/whitenoise/issues/258 says
In Django 3.1 static urls are prefixed if there is a url path prefix specified by settings (FORCE_SCRIPT_NAME) or more likely, from the webserver.
I'm not familiar with Django 3, but could it be that get_script_prefix
doesn't take FORCE_SCRIPT_NAME
into account, so both cases need to be handled? E.g.
if settings.FORCE_SCRIPT_NAME:
script_prefix = settings.FORCE_SCRIPT_NAME.rstrip("/")
else:
script_prefix = get_script_prefix().rstrip("/")
...
I had a similar issue. I've not hosted django on apache before with mod_wsgi but figured I would try it out.
I had django mounted under '/d':
WSGIDaemonProcess testnet.net python-home=/opt/python/cur python-path=/srv/www/testnet/ processes=2 threads=3
WSGIProcessGroup testnet.net
WSGIScriptAlias /d /srv/www/testnet/config/wsgi.py process-group=testnet.net
<Directory /srv/www/testnet/config >
<Files wsgi.py>
Require all granted
</Files>
</Directory>
Which meant my static files weren't being served. I updated my config to STATIC_URL = "/d/static/"
, I could see in the html that the static files were prefixed right but django was giving 404 for each file. Setting FORCE_SCRIPT_NAME
to many combinations of '/d', 'd', 'd/', etc nothing was working. This is what ended up working:
#FORCE_SCRIPT_NAME = '/d'
STATIC_URL = "/d/static/"
WHITENOISE_STATIC_PREFIX = "/static/"
(yes FORCE_SCRIPT_NAME is commented out)
I checked and get_script_prefix()
was returning '/d/'.
I had a similar issue which I solved by setting WHITENOISE_STATIC_PREFIX = "/static/"
although I'm not sure why this worked.
I believe this is still an issue, I've been experiencing incorrect pathing from static assets with whitenoise which I spent a bit of time debugging in https://github.com/TandoorRecipes/recipes/issues/1878 and https://code.djangoproject.com/ticket/34028.
The root cause seems to be that recent Django versions cache the prefix and setting values based on their first access. Normally first access is a HTTP request so SCRIPT_NAME would be set on it and all is well... However, whitenoise attempts to access the configured STATIC_URL during middleware initialization, as you can see here:
recipes-web_recipes-1 | File "/opt/recipes/recipes/wsgi.py", line 15, in <module>
recipes-web_recipes-1 | _application = get_wsgi_application()
recipes-web_recipes-1 | File "/opt/recipes/venv/lib/python3.10/site-packages/django/core/wsgi.py", line 13, in get_wsgi_application
recipes-web_recipes-1 | return WSGIHandler()
recipes-web_recipes-1 | File "/opt/recipes/venv/lib/python3.10/site-packages/django/core/handlers/wsgi.py", line 126, in __init__
recipes-web_recipes-1 | self.load_middleware()
recipes-web_recipes-1 | File "/opt/recipes/venv/lib/python3.10/site-packages/django/core/handlers/base.py", line 61, in load_middleware
recipes-web_recipes-1 | mw_instance = middleware(adapted_handler)
recipes-web_recipes-1 | File "/opt/recipes/venv/lib/python3.10/site-packages/whitenoise/middleware.py", line 47, in __init__
recipes-web_recipes-1 | self.configure_from_settings(settings)
recipes-web_recipes-1 | File "/opt/recipes/venv/lib/python3.10/site-packages/whitenoise/middleware.py", line 86, in configure_from_settings
recipes-web_recipes-1 | self.static_prefix = urlparse(settings.STATIC_URL or "").path
recipes-web_recipes-1 | File "/opt/recipes/venv/lib/python3.10/site-packages/django/conf/__init__.py", line 93, in __getattr__
recipes-web_recipes-1 | val = self._add_script_prefix(val)
recipes-web_recipes-1 | File "/opt/recipes/venv/lib/python3.10/site-packages/django/conf/__init__.py", line 144, in _add_script_prefix
recipes-web_recipes-1 | traceback.print_stack()
Since this is during initialization and outside a HTTP request context, SCRIPT_NAME is absent and therefore the Django prefix gets incorrectly returned as '/' (i.e. STATIC_URL gets set to '/static/' and then remains that way). This causes all the asset URIs to be wrong when a real request comes in with SCRIPT_NAME header set.
In theory setting FORCE_SCRIPT_NAME should address this by short-circuiting SCRIPT_NAME and ensuring the Django prefix is set correct on whitenoise middleware initialization, but in my testing I didn't see that behavior. The prefix only gets set on the first HTTP request, so until then even with FORCE_SCRIPT_NAME set the prefix is still '/' according to Django - and that's what whitenoise sees. Note sure if thats a Django issue or if whitenoise needs to add an explicit check for FORCE_SCRIPT_NAME.
This issue still exists.