authorgravatar for zacharyjwebb@gmail.comZach Webb <zacharyjwebb@gmail.com> 2026-08-21 03:10:14+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-20 22:10:14-04:00
log820e5639c30be83a9433f696613760f8ee42075a
tree9d346b86a160cbfe7a9e818d662e749beeda786c
parent6d3bf01b4f021eb3fbd2feaf07fcacaa4f55881d
signature Signed by PGP key B5690EEEBB952194

fix(server): serve html assets with utf-8 charset (#6409)


2 files changed, 16 insertions(+), 1 deletions(-)

apps/server/src/http.test.ts+11
...@@ -44,4 +44,15 @@ describe("assetResponseHeaders", () => {...@@ -44,4 +44,15 @@ describe("assetResponseHeaders", () => {
44 "X-Content-Type-Options": "nosniff",44 "X-Content-Type-Options": "nosniff",
45 });45 });
46 });46 });
47
48 it("declares utf-8 for HTML assets so non-ASCII content renders correctly", () => {
49 expect(assetResponseHeaders("/workspace/page.html")).toHaveProperty(
50 "Content-Type",
51 "text/html; charset=utf-8",
52 );
53 expect(assetResponseHeaders("/workspace/PAGE.HTM")).toHaveProperty(
54 "Content-Type",
55 "text/html; charset=utf-8",
56 );
57 });
47});58});
apps/server/src/http.ts+5-1
...@@ -46,10 +46,14 @@ const DESKTOP_RENDERER_ORIGINS = ["t3code://app", "t3code-dev://app"];...@@ -46,10 +46,14 @@ const DESKTOP_RENDERER_ORIGINS = ["t3code://app", "t3code-dev://app"];
46const SVG_CONTENT_SECURITY_POLICY = "default-src 'none'; style-src 'unsafe-inline'; sandbox";46const SVG_CONTENT_SECURITY_POLICY = "default-src 'none'; style-src 'unsafe-inline'; sandbox";
4747
48export function assetResponseHeaders(filePath: string): Record<string, string> {48export function assetResponseHeaders(filePath: string): Record<string, string> {
49 const lowerPath = filePath.toLowerCase();
49 return {50 return {
50 "Cache-Control": "private, max-age=3600",51 "Cache-Control": "private, max-age=3600",
51 "X-Content-Type-Options": "nosniff",52 "X-Content-Type-Options": "nosniff",
52 ...(filePath.toLowerCase().endsWith(".svg")53 ...(lowerPath.endsWith(".html") || lowerPath.endsWith(".htm")
54 ? { "Content-Type": "text/html; charset=utf-8" }
55 : {}),
56 ...(lowerPath.endsWith(".svg")
53 ? { "Content-Security-Policy": SVG_CONTENT_SECURITY_POLICY }57 ? { "Content-Security-Policy": SVG_CONTENT_SECURITY_POLICY }
54 : {}),58 : {}),
55 };59 };