Skip to content

Commit 88e8747

Browse files
committed
fix(realtime): noindex every response, not just the 404
Setting the header only on the 404 fallback covered the one response that crawlers already drop on status code alone, while /health — the sole route returning 200 with a body, and so the only indexable surface on the socket hostnames — stayed uncovered, with a test pinning it that way. Set it once on the handler instead. Node merges setHeader values into writeHead and no branch sets X-Robots-Tag, so it reaches every response.
1 parent 381ae3c commit 88e8747

2 files changed

Lines changed: 27 additions & 11 deletions

File tree

apps/realtime/src/routes/http.test.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { IRoomManager } from '@/rooms'
44
import { createHttpHandler } from '@/routes/http'
55

66
function createMocks(req: Partial<IncomingMessage>) {
7+
const setHeader = vi.fn()
78
const writeHead = vi.fn()
89
const end = vi.fn()
910
const logger = { info: vi.fn(), error: vi.fn(), debug: vi.fn(), warn: vi.fn() }
@@ -15,26 +16,42 @@ function createMocks(req: Partial<IncomingMessage>) {
1516
return {
1617
handler: createHttpHandler(roomManager, logger),
1718
req: { headers: {}, ...req } as IncomingMessage,
18-
res: { writeHead, end } as unknown as ServerResponse,
19+
res: { setHeader, writeHead, end } as unknown as ServerResponse,
20+
setHeader,
1921
writeHead,
2022
end,
2123
}
2224
}
2325

2426
describe('createHttpHandler', () => {
25-
it('marks unmatched routes noindex so crawlers drop the socket hostnames', async () => {
27+
/**
28+
* `/health` is the only route on this server that returns 200 with a body, so
29+
* it is the only genuinely indexable surface on the `sockets.*` hostnames.
30+
* Node merges `setHeader` values into `writeHead`, and no branch here sets
31+
* `X-Robots-Tag`, so the handler-level call reaches every response.
32+
*/
33+
it.each([
34+
['health check', { method: 'GET', url: '/health' }],
35+
['unmatched route', { method: 'GET', url: '/' }],
36+
['unauthenticated internal API call', { method: 'POST', url: '/api/workflow-deleted' }],
37+
])('marks the %s noindex', async (_label, req) => {
38+
const { handler, req: request, res, setHeader } = createMocks(req)
39+
40+
await handler(request, res)
41+
42+
expect(setHeader).toHaveBeenCalledWith('X-Robots-Tag', 'noindex, nofollow')
43+
})
44+
45+
it('still serves the unmatched-route 404 unchanged', async () => {
2646
const { handler, req, res, writeHead, end } = createMocks({ method: 'GET', url: '/' })
2747

2848
await handler(req, res)
2949

30-
expect(writeHead).toHaveBeenCalledWith(404, {
31-
'Content-Type': 'application/json',
32-
'X-Robots-Tag': 'noindex, nofollow',
33-
})
50+
expect(writeHead).toHaveBeenCalledWith(404, { 'Content-Type': 'application/json' })
3451
expect(end).toHaveBeenCalledWith(JSON.stringify({ error: 'Not found' }))
3552
})
3653

37-
it('does not mark the health check noindex', async () => {
54+
it('still serves the health check as 200', async () => {
3855
const { handler, req, res, writeHead } = createMocks({ method: 'GET', url: '/health' })
3956

4057
await handler(req, res)

apps/realtime/src/routes/http.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ function sendError(res: ServerResponse, message: string, status = 500): void {
5959
*/
6060
export function createHttpHandler(roomManager: IRoomManager, logger: Logger) {
6161
return async (req: IncomingMessage, res: ServerResponse) => {
62+
res.setHeader('X-Robots-Tag', 'noindex, nofollow')
63+
6264
// Health check doesn't require auth
6365
if (req.method === 'GET' && req.url === '/health') {
6466
try {
@@ -150,10 +152,7 @@ export function createHttpHandler(roomManager: IRoomManager, logger: Logger) {
150152
return
151153
}
152154

153-
res.writeHead(404, {
154-
'Content-Type': 'application/json',
155-
'X-Robots-Tag': 'noindex, nofollow',
156-
})
155+
res.writeHead(404, { 'Content-Type': 'application/json' })
157156
res.end(JSON.stringify({ error: 'Not found' }))
158157
}
159158
}

0 commit comments

Comments
 (0)