From 36c35c9bd509fc18495c3c9d39bb6244bdb8bcef Mon Sep 17 00:00:00 2001 From: Amruth Pillai Date: Mon, 17 Aug 2026 22:19:52 +0200 Subject: [PATCH] fix(seo): serve the root request through the web app handler The static middleware was mounted ahead of the web app fallback, and Hono's serveStatic resolves "/" to the directory and returns dist/index.html verbatim. handleWebApp never ran for the root route, so the OpenGraph, Twitter, canonical and JSON-LD markup it injects was missing in production - fetching https://rxresu.me/ as Twitterbot returned zero og: tags. Route "/" explicitly before the static middleware so the injection runs. --- apps/server/src/http/app.test.ts | 12 ++++++++++++ apps/server/src/http/app.ts | 3 +++ 2 files changed, 15 insertions(+) diff --git a/apps/server/src/http/app.test.ts b/apps/server/src/http/app.test.ts index c41690342..902644e24 100644 --- a/apps/server/src/http/app.test.ts +++ b/apps/server/src/http/app.test.ts @@ -192,4 +192,16 @@ describe("createApp", () => { expect(mocks.serveWebDistStatic).not.toHaveBeenCalled(); expect(mocks.handleWebApp).not.toHaveBeenCalled(); }); + + it.each(["GET", "HEAD"])("routes %s / to the web app handler so SEO markup is injected", async (method) => { + const { createApp } = await import("./app"); + const app = createApp(); + const request = new Request("http://localhost:3001/", { method }); + + const response = await app.fetch(request); + + expect(response.status).toBe(200); + expect(mocks.handleWebApp).toHaveBeenCalledWith(request); + expect(mocks.serveWebDistStatic).not.toHaveBeenCalled(); + }); }); diff --git a/apps/server/src/http/app.ts b/apps/server/src/http/app.ts index bf8cba197..677f92d5a 100644 --- a/apps/server/src/http/app.ts +++ b/apps/server/src/http/app.ts @@ -65,6 +65,9 @@ export function createApp() { app.on(["GET", "HEAD"], "/sitemap.xml", (c) => handleSitemap({ head: c.req.method === "HEAD" })); app.on(["GET", "HEAD"], "/llms.txt", (c) => handleLlms({ head: c.req.method === "HEAD" })); + // Must precede the static middleware: serveStatic resolves "/" to dist/index.html and would + // return it verbatim, skipping the OpenGraph/Twitter/canonical/JSON-LD injection in handleWebApp. + app.on(["GET", "HEAD"], "/", (c) => handleWebApp(c.req.raw)); app.use("/*", serveWebDistStatic); app.on(["GET", "HEAD"], "/*", (c) => handleWebApp(c.req.raw));