Incorrect mime-types (content-type header) for mp4 and js

AI-generated summary

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.

Hello!

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?

This is the correct place, you found it alright.

Can you explain why is this a problem that we should care about? Does it breaks anything?

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.

Can you name the proxy / analytic system that broken because of this? Share an example of the issue happening in the wild?

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.

Bumping this topic because I believe it creates a small issue, or at least an unwanted behavior.

Opening a video file hosted on Discourse forces the download of the video instead of playing it in the browser.

Right-click, copy video address, paste the link in the browser’s address bar.

Weird thing is that on a local dev installation, it properly plays the video in the browser:

When loading the video URL, headers are different between a regular install and a dev install.

On the regular production install, the video content type is set to application/mp4, whereas it’s set to video/mp4 on a dev install.

Crossposting Discourse send PDF inline because the author fixed a similar unwanted behavior with PDFs.

I’m all ears if someone has a solution to prevent mp4s from being forcibly downloaded.

4 Likes

Oh good find - this is weird.

It looks like MiniMime (a library we use and own) might be returning these values

pry(main)> MiniMime.lookup_by_filename("a.mp4").content_type
=> "application/mp4"

I’ll create a PR and we’ll discuss if it’s an issue changing these.

5 Likes

Just a quick update -

The ext_mime_db above that we use follows IANA Media Types defined here - https://www.iana.org/assignments/media-types/media-types.xhtml. Unfortunately, this means that mp4 is correctly application/mp4 :sweat_smile:


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.

We can at least remove this header, I think.

https://github.com/discourse/discourse/pull/30535

3 Likes

Not sure I fully understand, so quick question about this PR :person_raising_hand:

Why do the modifications specifically target s3-related files, when the forced download occurs even when not using s3? :thinking:

Will it fix the issue in general anyway?
Will it also fix forced download for other files that could be opened in the browser such as PDFs?

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).

2 Likes

Closing in favour of Mp4 files are downloading instead of displaying inline