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:
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
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=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.
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",
]
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",
]