resources.GetRemote: failed to resolve media type (application/javascript)

This works fine for me:

{{ $url := "https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js" }}
{{ with try (resources.GetRemote $url) }}
  {{ with .Err }}
    {{ errorf "%s" . }}
  {{ end }}
{{ end }}

This does not:

{{ $url := "https://cdn.jsdelivr.net/npm/@docsearch/js@3" }}
{{ with try (resources.GetRemote $url) }}
  {{ with .Err }}
    {{ errorf "%s" . }}
  {{ end }}
{{ end }}

Here an error is thrown:

ERROR template: shortcodes/test.html:4:22: executing "shortcodes/test.html" at <resources.GetRemote>:
error calling GetRemote: failed to resolve media type for remote resource "https://cdn.jsdelivr.net/npm/@docsearch/js@3"

The media type for both files is exactly the same:

curl -s -I https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js https://cdn.jsdelivr.net/npm/@docsearch/js@3 | grep content-type:
content-type: application/javascript; charset=utf-8
content-type: application/javascript; charset=utf-8

I worked around this by adding application/javascript to security.http.mediaTypes in my hugo.yml. Out of interest: what’s the difference that makes the one script load without problems
and the other fail?

What Hugo does

Hugo’s resources.GetRemote function safeguards against malicious payloads by carefully handling remote resource requests. Here’s how it works:

  1. Filename determination: The filename is extracted from the Content-Disposition header (if present), or from the URL path.

  2. Content type retrieval: The Content-Type header from the response is used.

  3. Extension hints: Hugo attempts to determine the file extension to help identify the media type:

    • If the Content-Type suggests known extensions, these are used as extensionHints.

    • Otherwise, if the filename has an extension, that extension is used as the sole extensionHint.

    • If neither of the above yields an extension, extensionHints remains empty (nil).

  4. Media type resolution: Hugo uses a combination of built-in media types, the extensionHints, and the resource’s content to determine the correct media type. If the media type cannot be resolved, an error is thrown: failed to resolve media type for remote resource.

  5. Success: If a media type is successfully determined, the request is considered successful, and the resource content is available via the Content method.

The problem

Your first request succeeds because, despite the Content-Type (application/javascript) not being a built-in Hugo type, the filename has a .js extension. This provides an extensionHint, allowing Hugo to associate it with a known media type.

Your second request fails because the filename lacks an extension. Since the Content-Type is not a built-in type, and there are no extensionHints, Hugo cannot determine the media type, resulting in the error.

The solution

To resolve this, you need to tell Hugo how to handle application/javascript by adding it to your site configuration:

[mediaTypes."application/javascript"]
suffixes = ["js"]

This configuration tells Hugo that files with the application/javascript content type should be treated as JavaScript files (using the .js suffix). This provides the necessary information for Hugo to successfully process the second request.

Notes

The explanation above omits some details. See source code.

Hugo excludes application/javascript from its built-in media types because that media type is obsolete per RFC 9239. The correct media type is text/javascript, which is one of Hugo’s built-in media types. Also see the IANA registry.

2 Likes

[quote=“jmooring, post:2, topic:53607”]

The solution

To resolve this, you need to tell Hugo how to handle application/javascript by adding it to your site configuration:

[mediaTypes."application/javascript"]
suffixes = ["js"]

This works great, thanks @jmooring for pointing that out!

Remark (mostly for the records!?):

After reading the section Security policy from the official docs, I tried to resolve my problem by adding this to my hugo.yaml:

security:
  http:
    mediaTypes:
    - ^application/javascript$

This didn’t work however.

Only after adding the lines below, the media type could be resolved:

security:
  http:
    mediaTypes:
    - ^application/javascript; charset=utf-8$

This is a bit counter-intuitive IMHO.

This is a bit counter-intuitive IMHO.

Nah, I don’t think so. It’s a regular expression. Every character has a meaning.

security:
  http:
    mediaTypes:
    - ^application/javascript$

The ^ means “the start of the string” and $ means “the end of the string”, so ^something$ means “something (the string), and only that, exactly that, nothing more”.

^application/javascript would have probably worked.

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.