djangoherokuwagtailwhitenoisewagtail-admin

Django/Wagtail static assets return 404 only when logged in but works logged out


After upgrading Wagtail and supporting packages, I'm having a strange problem where all the static assets (CSS, JS, fonts, etc..) return a 404 error when logged into the Wagtail admin, ie. the sessionid is valid and set in the browser. However, the are accessible when not logged in (no sessionid cookie). This is true for the public facing site as well as /admin

The site is running:

Both production and dev (currently locally) are set using STATICFILES_STORAGE = whitenoise.storage.CompressedManifestStaticFilesStorage and STATIC_URL = "static/". Migrations and collectstatic have also been run.

I've tested this a couple of ways:

Using curl

No Cookie: curl -I http://127.0.0.1:8000/static/wagtailadmin/js/vendor.610e8c3f47d9.js

HTTP/1.1 200 OK
Date: Mon, 01 May 2023 06:11:55 GMT
Server: WSGIServer/0.2 CPython/3.11.2
Content-Type: text/javascript; charset="utf-8"
Cache-Control: max-age=315360000, public, immutable
Access-Control-Allow-Origin: *
Vary: Accept-Encoding, Cookie
Last-Modified: Mon, 01 May 2023 03:59:51 GMT
ETag: "644f3937-41103"
Content-Length: 266499
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
X-Frame-Options: DENY

With Cookie: curl -I http://127.0.0.1:8000/static/wagtailadmin/js/vendor.610e8c3f47d9.js -H "Cookie: csrftoken=fPp7Oq2lR90jkilUiPDRHTOPTsu2lMZm;sessionid=his9jbjahqa3j86hjueeop0lno8pb4a9"

HTTP/1.1 404 Not Found
Date: Mon, 01 May 2023 06:15:53 GMT
Server: WSGIServer/0.2 CPython/3.11.2
Content-Type: text/html; charset=utf-8
Content-Length: 2761
Vary: Cookie

Invalid sessionid: à la sessionid=thisisnotvalid returns assets fine

Setting full URL for STATIC_URL

This is where it gets interesting. If I set STATIC_URL = "http://127.0.0.1:8000/static/" and log in via http://localhost:8000 everything works perfectly. If I set STATIC_URL = "http://localhost:8000/static/" no dice! It's the same if I swap them. Apparently STATIC_URL and WAGTAILADMIN_BASE_URL cannot be the same.

In production I don't really have the option (or don't want) to serve the assets from a separate domain. I'd expect it to just work as it did formally.

Debug setting

DEBUG=True: Setting STATIC_URL works with static/ but not when it's the same as the accessing domain and works when set to an alternate.

DEBUG=False: Works as described above. static/ and matching domains don't work. An alternate does.

Relevent bits of configs

base.py:

STATICFILES_FINDERS = [
    "django.contrib.staticfiles.finders.FileSystemFinder",
    "django.contrib.staticfiles.finders.AppDirectoriesFinder",
]

STATICFILES_DIRS = [
    os.path.join(PROJECT_DIR, "static"),
]
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")

dev.py:

STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
STATIC_URL = "http://127.0.0.1:8000/static/"

production.py:

STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
STATIC_URL = "https://www.domainredacted.com/static/" # Formally just "static/"

It's quite late and perhaps I missed something while upgrading but I can't seem to find it. Any help is greatly appreciated.

App & Middleware:

INSTALLED_APPS = [
   ......
    "generic_chooser",
    "django_extensions",
    "storages",  # For S3 Storage
    "wagtail_2fa",
    "django_otp",
    "django_otp.plugins.otp_totp",
    "captcha",
    "wagtailcaptcha",
    "wagtail.contrib.modeladmin",
    "wagtail.contrib.forms",
    "wagtail.contrib.redirects",
    "wagtail.contrib.settings",
    "wagtail.contrib.routable_page",
    "wagtailmetadata",
    "wagtail.embeds",
    "wagtail.sites",
    "wagtail.users",
    "wagtail.snippets",
    "wagtail.documents",
    "wagtail.images",
    "wagtail.search",
    "wagtail.admin",
    "wagtail",
    "wagtail.contrib.search_promotions",
    "wagtail.contrib.table_block",
    "modelcluster",
    "taggit",
    "django.contrib.sitemaps",
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "whitenoise.runserver_nostatic",
    "django.contrib.staticfiles",
]

MIDDLEWARE = [
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "wagtail_2fa.middleware.VerifyUserMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",
    "wagtail.contrib.redirects.middleware.RedirectMiddleware",
]

Solution

  • Turns out it was something very small. I moved security and whitenoise middleware to the top thanks to this information from Rvector. Not sure why it wasn't an issue previously. Live and learn!

    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",
        "wagtail_2fa.middleware.VerifyUserMiddleware",
        "django.contrib.messages.middleware.MessageMiddleware",
        "django.middleware.clickjacking.XFrameOptionsMiddleware",
        "wagtail.contrib.redirects.middleware.RedirectMiddleware",
    ]