The reason is because the template tag indirectly uses ​urlparse.urljoin or ​urllib.parse.urljoin. From what I can tell, that method is designed to take one URL as an example (in this case, STATIC_URL) and use it to make an absolute URL out of a second argument (in this case, 'js/script.min.js'). Because it interprets the first argument's staticfiles-cache as a file name rather than a directory, it removes that slug and appends the second argument at the root.

I think that behavior makes sense, but I want to do something to make this less confusing. My initial thought is to check the output (either in django.core.files.storage.FileSystemStorage.url or in the template tag itself) to make sure it starts with the base URL (or in the template tag, STATICFILES_URL directly).

Can I get everyone's input on this approach? Am I barking up the wrong tree entirely? If not, should I do it in FileSystemStorage, or only in the static template tag? If to the template tag specifically, should I set up similar things in other areas (i.e. media URLs)?

I'm more than willing to implement this, but it'd be my first contribution, so I don't want to be presumptuous about whether or not this is a valuable fix.

However, I don't think the {% static %} tag or FileSystemStorage are good places to put code that basically validates settings.

There used to be a comment for MEDIA_URL that warned you to use a trailing slash [1] and personally, I'd be favorable to put something like this back for STATIC_URL.

The fact that that staticfiles view seems to serve the file wihtout a problem might itself be a bug I think. Note that there's a django.contrib.staticfiles.utils.check_config function where it might makes sense to check that STATIC_URL ends with the required trailing slash.