[Django] #36562: `StaticFilesStorage` should respect `base_url` and treat `STATIC_URL` as a fallback

10 views
Skip to first unread message

Django

unread,
Aug 19, 2025, 9:49:58 AM8/19/25
to django-...@googlegroups.com
#36562: `StaticFilesStorage` should respect `base_url` and treat `STATIC_URL` as a
fallback
-------------------------------------+-------------------------------------
Reporter: Kamil Paduszyński | Type:
| Uncategorized
Status: new | Component:
| contrib.staticfiles
Version: 5.2 | Severity: Normal
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
As follows from the official
[https://docs.djangoproject.com/en/5.2/ref/files/storage/#django.core.files.storage.FileSystemStorage.base_url
docs] one can specify `base_url` in storage's `"OPTIONS"`.
`StaticFilesStorage` subclasses from `FileSystemStorage`, so one could
expect that when setting:

{{{#!python
# Storages

STORAGES = {
"staticfiles": {
"BACKEND":
"django.contrib.staticfiles.storage.StaticFilesStorage",
"OPTIONS": {
"base_url": "static/",
},
},
}
}}}

everything should work without touching `STATIC_URL`. However, we get:

{{{
django.core.exceptions.ImproperlyConfigured: You're using the staticfiles
app without having set the required STATIC_URL setting.
}}}

Would it be possible to change this behavior? IMHO, such change would make
setting up storages (even in development) more centralized, thus cleaner.
Of course, this would not be a breaking change, since `STATIC_URL` could
be still returned when `base_url` is not explicitly set.

I believe that this would require updating `get_base_url` method of the
static files handler mixin:

https://github.com/django/django/blob/ad4a9e0f3b1de261409bc083aa49dba705531824/django/contrib/staticfiles/handlers.py#L29-L31
--
Ticket URL: <https://code.djangoproject.com/ticket/36562>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Aug 19, 2025, 1:32:17 PM8/19/25
to django-...@googlegroups.com
#36562: `StaticFilesStorage` should respect `base_url` and treat `STATIC_URL` as a
fallback
-------------------------------------+-------------------------------------
Reporter: Kamil Paduszyński | Owner: (none)
Type: Bug | Status: closed
Component: contrib.staticfiles | Version: 5.2
Severity: Normal | Resolution:
| worksforme
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Natalia Bidart):

* resolution: => worksforme
* status: new => closed
* type: Uncategorized => Bug

Comment:

Hello Kamil Paduszyński, thank you for taking the time to create this
ticket.

I invested some time investigating this, and I can't reproduce the error
you shared. I tried various local settings and they all work. So then, I
wrote a test that shows how the `base_url` from `OPTIONS` gets used by the
`StaticFilesStorage`. This test is passing in `main`:

{{{#!diff
diff --git a/tests/staticfiles_tests/test_checks.py
b/tests/staticfiles_tests/test_checks.py
index b9ac486ed1..9243546eef 100644
--- a/tests/staticfiles_tests/test_checks.py
+++ b/tests/staticfiles_tests/test_checks.py
@@ -164,3 +164,22 @@ class StoragesCheckTests(SimpleTestCase):
def test_staticfiles_no_errors(self):
errors = check_storages(None)
self.assertEqual(errors, [])
+
+ @override_settings(
+ STATIC_URL=None,
+ STORAGES={
+ "staticfiles": {
+ "BACKEND":
"django.contrib.staticfiles.storage.StaticFilesStorage",
+ "OPTIONS": {"base_url": "override/"},
+ }
+ },
+ )
+ def test_base_url_in_storages_options_without_static_url(self):
+ """
+ Providing base_url in STORAGES['staticfiles']['OPTIONS'] should
be
+ sufficient to configure StaticFilesStorage without STATIC_URL.
+ Currently it still raises ImproperlyConfigured.
+ """
+ from django.core.files.storage import storages
+ storage = storages["staticfiles"]
+ self.assertEqual(storage.base_url, "override/")
}}}

Because of the above, I'll close as `worksforme`. If you still have this
issue, before re-opening please be sure to provide a failing test case for
the Django test suite or provide a minimal Django test project for us to
reproduce.
--
Ticket URL: <https://code.djangoproject.com/ticket/36562#comment:1>

Django

unread,
Aug 19, 2025, 3:10:40 PM8/19/25
to django-...@googlegroups.com
#36562: `StaticFilesStorage` should respect `base_url` and treat `STATIC_URL` as a
fallback
-------------------------------------+-------------------------------------
Reporter: Kamil Paduszyński | Owner: (none)
Type: Bug | Status: closed
Component: contrib.staticfiles | Version: 5.2
Severity: Normal | Resolution:
| worksforme
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Kamil Paduszyński):

Sorry for misunderstanding. Let me explain it in more detail.

Setting `STATIC_URL=None` and putting the actual URL into `base_url`
options results in passing system checks (the `check` command).

The mentioned error is raised when I execute the `runserver` command (the
one overloaded from the `staticfiles` app). Your tests don't account for
running commands, I guess. I also checked that the problem (I don't want
to call that a bug for now) raises only in debugging mode `DEBUG = True`.

Sample project: https://github.com/paduszyk/ticket_36562.

My tests:

{{{#!python
from django.core.exceptions import ImproperlyConfigured
from django.core.management import call_command
from django.test import TestCase
from django.test.utils import override_settings


@override_settings(
DEBUG=True,
STORAGES={
"staticfiles": {
"BACKEND":
"django.contrib.staticfiles.storage.StaticFilesStorage",
"OPTIONS": {
"base_url": "static/",
},
},
},
STATIC_URL=None,
)
class TestStoragesIfBaseURLandStaticURLNone(TestCase):
def test_check_does_not_raise_error(self):
try:
call_command("check")
except Exception:
self.fail()

def test_runserver_raises_improperly_configured(self):
with self.assertRaises(ImproperlyConfigured):
call_command("runserver", "--noreload")

}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/36562#comment:2>

Django

unread,
Aug 20, 2025, 10:47:55 AM8/20/25
to django-...@googlegroups.com
#36562: `StaticFilesStorage` should respect `base_url` and treat `STATIC_URL` as a
fallback
-------------------------------------+-------------------------------------
Reporter: Kamil Paduszyński | Owner: (none)
Type: Bug | Status: new
Component: contrib.staticfiles | Version: 5.2
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Kamil Paduszyński):

* resolution: worksforme =>
* status: closed => new

--
Ticket URL: <https://code.djangoproject.com/ticket/36562#comment:3>

Django

unread,
Aug 26, 2025, 9:00:20 AM8/26/25
to django-...@googlegroups.com
#36562: `StaticFilesStorage` should respect `base_url` and treat `STATIC_URL` as a
fallback
-------------------------------------+-------------------------------------
Reporter: Kamil Paduszyński | Owner: (none)
Type: Bug | Status: closed
Component: contrib.staticfiles | Version: 5.2
Severity: Normal | Resolution: wontfix
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Natalia Bidart):

* resolution: => wontfix
* status: new => closed

Comment:

Replying to [comment:3 Kamil Paduszyński]:

Thanks for the follow up and the extra details. After reviewing this I
think this should be closed as `wontfix`. A few points:

* The `StaticFilesHandler` is for development only. It is the helper that
`runserver` uses when `DEBUG=True`. In production you should always serve
static files with a proper web server or CDN or similar. The handler
itself is undocumented, which is a sign it is an internal detail and not
part of the public API.

* The handler has no direct relation to the storage backend. The storage
controls where files are stored and what URLs are returned. The handler is
a WSGI wrapper that serves requests. They both care about static files but
they are independent..

* The parallel you mention between `FileSystemStorage` and
`StaticFilesStorage` does not apply here for handlers. There is no
''handler version'' of each storage, they are different layers.

Because of the above, making the handler honor the `base_url` from storage
does not seem right. It would blur two separate concepts and could confuse
the setup. Keeping `STATIC_URL` as the single knob that the handler checks
is simpler and keeps the dev helper separated from storage configuration.
--
Ticket URL: <https://code.djangoproject.com/ticket/36562#comment:4>
Reply all
Reply to author
Forward
0 new messages