squirrelscan
GitHub

Wrong Content-Type: header and extension disagree

A stylesheet served as text/html is refused by the browser under nosniff. squirrel compares the response type against the URL extension.

What a MIME type mismatch is

The Content-Type response header tells the client what a file is. The extension in the URL is a convention, not a declaration: the header is what the browser acts on.

When they disagree, the header wins and things break in ways that look unrelated. A stylesheet served as text/html is refused outright when X-Content-Type-Options: nosniff is set, and a script served as text/plain will not execute. Without nosniff the browser may sniff its way to the right answer, which hides the misconfiguration until the day you add the header.

What squirrel checks

The rule runs on every crawled page. It takes the lowercased extension from the end of the URL path and compares the response Content-Type, with any charset parameter removed, against the types expected for that extension. It emits a single check named mime-type:

  • Skipped when the URL has no extension. Message: No file extension detected.
  • Info when the extension is not in the map. Message: Unknown extension .xyz, skipping validation.
  • Fail when there is no Content-Type header. Message: Missing Content-Type header for .css file, with the expected type.
  • Fail when the type does not match. Message: Incorrect MIME type for .css file, with the value found and the accepted types.
  • Pass otherwise. Message: Correct MIME type for .css, with the value.

The map covers scripts, stylesheets, images, fonts, and the document types .pdf, .json, .xml, .html and .htm, each with the one or two types that are acceptable for it. Severity warning, weight 5.

How to fix it

include mime.types;
default_type application/octet-stream;

Fix it in the server configuration rather than per file. Both Apache and nginx ship a mime types map; the usual cause is a custom handler or a framework route that sets a default type for everything it serves.

Serving a static file through an application route is where this most often goes wrong. Set the type explicitly there.

Rule IDcontent/mime-type
CategoryContent
ScopePer-page
Severitywarning
Weight5/10

Enable / disable

Disable this rule

[rules]
disable = ["content/mime-type"]

Disable all Content rules

[rules]
disable = ["content/*"]

Enable only this rule

[rules]
enable = ["content/mime-type"]

enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.

Content findings ship in every audit next to the SEO, performance and agent experience rules. See Fix SEO issues with AI for how an agent works through a report.

References

Check your site

Run squirrel audit https://example.com and open the Content section of the report. Each mismatch is reported with the type sent and the type expected. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

Type to search…

↑↓ navigate↵ openesc close