Users report incorrect MIME types in Discourse, particularly for MP4 and JS files. Initially, iprok noted MP4 uploads from S3 were served as application/mp4 (incorrect) instead of video/mp4, later resolving it via nginx. The JS issue persisted: /brotli_asset/ files returned application/javascript while /theme-javascripts/ used text/javascript, causing confusion for proxies and analytics tools like GoAccess iprok. Canapin highlighted that MP4s force download instead of playing in-browser on production installs (due to application/mp4 + Content-Disposition: attachment), unlike dev installs which use video/mp4. nat traced the MP4 type to MiniMime, which follows IANA standards (application/mp4 is technically correct). The real issue was the unintended attachment header added for non-image uploads, including videos. A PR nat was created to remove this header for S3 uploads, which should fix forced downloads for MP4s and PDFs. Canapin questioned if the fix applies beyond S3; nat clarified that non-S3 uploads don’t set headers explicitly, so the fix targets S3. Note: Chrome may still force MP4 downloads due to historical behavior, even with correct headers.
I’ve just noticed that discourse is putting incorrect Content-type) headers to several file types: mp4 files from /uploads/ goes with content-type: application/mp4 instead of video/mp4
my uploads went from S3 so it is up to the S3 hosting provider.
Some javascript (.js) e.g. from /brotli_asset/ goes with application/javascript instead of text/javascript .
I use discourse_docker without any modifications, I’ve checked included nginx, it contains correct mime-types configuration file. Seems that it’s discourse backend guilty of sending this incorrect mime-types.
Hello! As far as I have got the problem with uploads was on my side and caused by S3-provider (I have successfully resolved it by overriding the setting in my nginx). And the problem with JS from /brotli_asset/ is somewhere in “internal” nginx of the discourse, so seems to be bug in discourse_docker.
Shame on me, but I failed to find any place to report bug better than this part of the forum. Can you point me please?
In short words discourse sends javascript from different location with different mime-types. E.g. /theme-javascripts/ff3c633d0d4192c83a194066eaa9d823b5c2d8f6.js goes with text/javascript (correct) and previously mentioned /brotli_asset/… with application/javascript. It can confuse proxy servers between discourse and client so as analytic systems, where I have noticed the issue.
Seems you just need to correctly setup internal nginx in discourse docker image for brotli assets.
In my goaccess reports mime-types dashboard of discourse is splitted values for js: one for text/javascript and another for application/javascript.
The second one: both gzip_types and brotli_types directives in nginx are operated by mime-types. So for discourse people have to setup both correct text/javascript and incorrect application/javascript.
The same for any proxy recompressing content that bases on mime-type.
Though, looking into our S3 uploader implementation further, I do see that we are adding Content-Disposition header "attachment" for pretty much every upload that is not an image, but it feels like it was only intended to be added for svgs. Using "attachment" would result in the content to get downloaded rather than open in a new tab. For videos, it is a mixture of application/video and attachment that is causing this.
When uploading to S3, we have to specify headers like the Content-Disposition and Content-Type of the file. These are the values that will be returned when loading the file - PutObject - Amazon Simple Storage Service
Can you explain when this is happening?
The PR will remove the Content-Disposition: attachment header which forces the download regardless of any browser you use.
Once you’ve upgraded your site, the Content-Type is used instead, and the browser will decide what to do with it. One thing to note, there is no problem with Content-Type: application/mp4 on Safari and Firefox, but on Chrome, it still forces a download (probably due to some bad history Chrome has with the mp4 file type).