diff --git a/apps/server/src/projectFaviconRoute.test.ts b/apps/server/src/projectFaviconRoute.test.ts
index e8b826d7be74c5d81d3b2b73d29a5d85db51bb0f..34a430d9742f7d020dc19d2939938aef09523668 100644
--- a/apps/server/src/projectFaviconRoute.test.ts
+++ b/apps/server/src/projectFaviconRoute.test.ts
@@ -152,4 +152,16 @@ describe("tryHandleProjectFaviconRequest", () => {
expect(response.body).toBe("");
});
});
+
+ it("serves a fallback favicon when no icon exists", async () => {
+ const projectDir = makeTempDir("t3code-favicon-route-fallback-");
+
+ await withRouteServer(async (baseUrl) => {
+ const pathname = `/api/project-favicon?cwd=${encodeURIComponent(projectDir)}`;
+ const response = await request(baseUrl, pathname);
+ expect(response.statusCode).toBe(200);
+ expect(response.contentType).toContain("image/svg+xml");
+ expect(response.body).toContain('data-fallback="project-favicon"');
+ });
+ });
});
diff --git a/apps/server/src/projectFaviconRoute.ts b/apps/server/src/projectFaviconRoute.ts
index 4d66f0e86f3156ef6c7f194969acbab0b0ac2ea5..cf234ad894ac94ae702cbdf317707b80d261eef2 100644
--- a/apps/server/src/projectFaviconRoute.ts
+++ b/apps/server/src/projectFaviconRoute.ts
@@ -9,6 +9,8 @@ const FAVICON_MIME_TYPES: Record = {
".ico": "image/x-icon",
};
+const FALLBACK_FAVICON_SVG = ``;
+
// Well-known favicon paths checked in order.
const FAVICON_CANDIDATES = [
"favicon.svg",
@@ -85,6 +87,14 @@ function serveFaviconFile(filePath: string, res: http.ServerResponse): void {
});
}
+function serveFallbackFavicon(res: http.ServerResponse): void {
+ res.writeHead(200, {
+ "Content-Type": "image/svg+xml",
+ "Cache-Control": "public, max-age=3600",
+ });
+ res.end(FALLBACK_FAVICON_SVG);
+}
+
export function tryHandleProjectFaviconRequest(url: URL, res: http.ServerResponse): boolean {
if (url.pathname !== "/api/project-favicon") {
return false;
@@ -118,8 +128,7 @@ export function tryHandleProjectFaviconRequest(url: URL, res: http.ServerRespons
const trySourceFiles = (index: number): void => {
if (index >= ICON_SOURCE_FILES.length) {
- res.writeHead(404, { "Content-Type": "text/plain" });
- res.end("No favicon found");
+ serveFallbackFavicon(res);
return;
}
const sourceFile = path.join(projectCwd, ICON_SOURCE_FILES[index]!);