Alex Kurilin

Correct Content-Type of static assets in Ring apps

If you’re thinking of serving assets from a Clojure Ring app, then you should be aware of an interesting quirk of one of the core pieces of Ring middleware: wrap-file-info. This middleware is used to automatically detect the file type based on extension and inject the corresponding Content-Type header into the HTTP response.

Now, when developing with it enabled, you might run into an interesting and somewhat hard situation to debug. In development mode the files will be served with the correct MIME type. In production, when the app is packaged together into a single uberjar, IF your resources were being served from within the resources/ folder, then wrap-file-info will fail to correctly identify the file type.

Why? In development ring will be working with real files on disk, in a .jar situation you simply don’t get file extensions. See the following thread where Weavejester clarifies the situation.

Solution? Use wrap-content-type middleware instead. It only cares about the extension specified in the URL and should work correctly in most of the basic cases. I imagine you could have Ring fetch resources outside of the .jar itself, in which case the problem above would not reproduce. I believe you could get the best of both worlds by moving your assets outside of the uberjar and by using wrap-content-type regardless.

As a sidenote, cURL seems to infer MIME type based off of the URL, a behavior that’s inconsistent with the current versions of Firefox and Chrome. If you try to debug the issue above with cURL, you will be deeply puzzled. cURL will consistently correctly guess the MIME type, while the browser pointing at the file will print out a console message saying it’s getting a resource with text/plain, instead a different Content-Type value. The browser still correctly tries to use the resource, as they’re wrapped in a