whitenoise icon indicating copy to clipboard operation
whitenoise copied to clipboard

WHITENOISE_STATIC_PREFIX still required if django app deployed on subpath

Open w- opened this issue 3 years ago • 7 comments

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

w- avatar Jan 02 '21 13:01 w-

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?

evansd avatar Jan 04 '21 12:01 evansd

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.

manics avatar Jan 04 '21 22:01 manics

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("/")
...

manics avatar Jan 06 '21 23:01 manics

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/'.

smith153 avatar Jan 16 '21 06:01 smith153

I had a similar issue which I solved by setting WHITENOISE_STATIC_PREFIX = "/static/" although I'm not sure why this worked.

bluespider42 avatar Jul 22 '21 15:07 bluespider42

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.

stewartadam avatar Sep 26 '22 02:09 stewartadam

This issue still exists.

tangjienan avatar Mar 19 '23 16:03 tangjienan