Name the types the runtime image cannot look up

The static plane sets nosniff, which turns an unknown Content-Type from a
guess into a refusal. Go's built-in table stops at the web's first dozen
extensions and takes the rest from the system mime.types, which the
distroless runtime does not ship — so .md and .woff2 resolve on a
developer's box and answer empty in production. hanzoskills.com publishes
a markdown twin of every document, and a browser opening one was offered a
download instead of a page.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-09-10 22:00:59 -07:00
co-authored by Hanzo Dev
parent fd10589ad9
commit 0daf7c53e0
2 changed files with 59 additions and 0 deletions
@@ -22,6 +22,26 @@ import (
const typeName = "StaticFiles"
// Extensions Go's built-in table does not carry. It falls back to the system
// mime.types, which a distroless image does not ship, so on a developer's box
// these resolve and in production they come back empty — and this plane sets
// `nosniff`, so an empty answer means the browser is told the type is unknown
// and forbidden to guess. Markdown served that way is offered as a download
// instead of a page; hanzoskills.com publishes a .md twin of every document.
func init() {
for ext, typ := range map[string]string{
".md": "text/markdown; charset=utf-8",
".markdown": "text/markdown; charset=utf-8",
".woff": "font/woff",
".woff2": "font/woff2",
".ttf": "font/ttf",
".otf": "font/otf",
} {
// AddExtensionType only returns an error on a malformed extension.
_ = mime.AddExtensionType(ext, typ)
}
}
type dirEntry struct {
Name string
Size int64
@@ -107,3 +107,42 @@ func TestLocalErrorPage(t *testing.T) {
t.Fatalf("local error page: status=%d body=%q", resp.StatusCode, body)
}
}
// TestNosniffTypesAreRegistered proves every extension this plane serves gets a
// Content-Type. The pairing is what makes it matter: the handler sets
// `X-Content-Type-Options: nosniff`, so a missing type is not a browser falling
// back to a guess, it is a browser refusing to render. Go's built-in table stops
// at the web's first dozen extensions and the rest come from the system
// mime.types, which the runtime image does not ship — so this passed on a
// developer's box and failed in production until the types were registered here.
func TestNosniffTypesAreRegistered(t *testing.T) {
dir := t.TempDir()
for name, body := range map[string]string{
"readme.md": "# hello\n",
"font.woff2": "\x77OF2",
} {
if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o644); err != nil {
t.Fatal(err)
}
}
srv := httptest.NewServer(newLocal(t, dynamic.StaticFiles{Root: dir}))
defer srv.Close()
for path, want := range map[string]string{
"/readme.md": "text/markdown",
"/font.woff2": "font/woff2",
} {
resp, err := srv.Client().Get(srv.URL + path)
if err != nil {
t.Fatal(err)
}
resp.Body.Close()
if got := resp.Header.Get("Content-Type"); !strings.HasPrefix(got, want) {
t.Errorf("%s: Content-Type %q, want %s — with nosniff the browser will not render this", path, got, want)
}
if resp.Header.Get("X-Content-Type-Options") != "nosniff" {
t.Errorf("%s: nosniff missing; this test is only meaningful beside it", path)
}
}
}