diff --git a/packages/next/src/server/lib/router-server.ts b/packages/next/src/server/lib/router-server.ts index 44e73f698613f..6ba6beea2a3a0 100644 --- a/packages/next/src/server/lib/router-server.ts +++ b/packages/next/src/server/lib/router-server.ts @@ -676,6 +676,13 @@ export async function initialize(opts: { 'no-cache, no-store, max-age=0, must-revalidate' ) + // Short-circuit favicon.ico serving so that the 404 page doesn't get built as favicon is requested by the browser when loading any route. + if (opts.dev && !matchedOutput && parsedUrl.pathname === '/favicon.ico') { + res.statusCode = 404 + res.end('') + return null + } + const appNotFound = opts.dev ? devInstance?.serverFields.hasAppNotFound : await fsChecker.getItem('/_not-found') diff --git a/test/e2e/favicon-short-circuit/app/route.js b/test/e2e/favicon-short-circuit/app/route.js new file mode 100644 index 0000000000000..07b9b214893bd --- /dev/null +++ b/test/e2e/favicon-short-circuit/app/route.js @@ -0,0 +1,3 @@ +export async function GET() { + return new Response('Hello world') +} diff --git a/test/e2e/favicon-short-circuit/favicon-short-circuit.test.ts b/test/e2e/favicon-short-circuit/favicon-short-circuit.test.ts new file mode 100644 index 0000000000000..6cd91e5c01636 --- /dev/null +++ b/test/e2e/favicon-short-circuit/favicon-short-circuit.test.ts @@ -0,0 +1,38 @@ +import { createNextDescribe } from 'e2e-utils' + +createNextDescribe( + 'favicon-short-circuit', + { + files: __dirname, + }, + ({ next, isNextDev, isNextStart }) => { + if (isNextDev) { + it('should short circuit the favicon in development', async () => { + const res = await next.fetch('/favicon.ico') + + // Expect we got the right status and headers. + expect(res.status).toBe(404) + expect(res.headers.get('content-type')).toBeNull() + + // Expect we got no body. + const text = await res.text() + expect(text).toBeEmpty() + + // Expect we didn't compile the not found route. + expect(next.cliOutput).not.toContain('compiling /not-found') + }) + } else if (isNextStart) { + it('should not short circuit the favicon in production', async () => { + const res = await next.fetch('/favicon.ico') + + // Expect we got the right status and headers. + expect(res.status).toBe(404) + expect(res.headers.get('content-type')).toBe('text/html; charset=utf-8') + + // Expect we got the right body. + const html = await res.text() + expect(html).toContain('') + }) + } + } +)