diff --git a/packages/next/server/web/sandbox/context.ts b/packages/next/server/web/sandbox/context.ts index 6685b4f6255e244..7159bea301386a3 100644 --- a/packages/next/server/web/sandbox/context.ts +++ b/packages/next/server/web/sandbox/context.ts @@ -154,7 +154,10 @@ async function createModuleContext(options: { const prevs = init.headers.get(`x-middleware-subrequest`)?.split(':') || [] const value = prevs.concat(options.module).join(':') init.headers.set('x-middleware-subrequest', value) - init.headers.set(`user-agent`, `Next.js Middleware`) + + if (!init.headers.has('user-agent')) { + init.headers.set(`user-agent`, `Next.js Middleware`) + } if (typeof input === 'object' && 'url' in input) { return fetch(input.url, { diff --git a/test/integration/middleware/core/pages/api/headers.js b/test/integration/middleware/core/pages/api/headers.js new file mode 100644 index 000000000000000..0f65c82e9ffa77b --- /dev/null +++ b/test/integration/middleware/core/pages/api/headers.js @@ -0,0 +1,3 @@ +export default function handler(req, res) { + res.json({ url: req.url, headers: req.headers }) +} diff --git a/test/integration/middleware/core/pages/interface/_middleware.js b/test/integration/middleware/core/pages/interface/_middleware.js index 1aee8917ed5c988..5f70ff396e22dd2 100644 --- a/test/integration/middleware/core/pages/interface/_middleware.js +++ b/test/integration/middleware/core/pages/interface/_middleware.js @@ -31,6 +31,52 @@ export async function middleware(request) { } } + if (url.pathname.includes('/fetchUserAgentDefault')) { + try { + const apiRoute = new URL(url) + apiRoute.pathname = '/api/headers' + const res = await fetch(apiRoute) + return new Response(await res.text(), { + status: 200, + headers: { + 'content-type': 'application/json', + }, + }) + } catch (err) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + headers: { + 'content-type': 'application/json', + }, + }) + } + } + + if (url.pathname.includes('/fetchUserAgentCustom')) { + try { + const apiRoute = new URL(url) + apiRoute.pathname = '/api/headers' + const res = await fetch(apiRoute, { + headers: { + 'user-agent': 'custom-agent', + }, + }) + return new Response(await res.text(), { + status: 200, + headers: { + 'content-type': 'application/json', + }, + }) + } catch (err) { + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + headers: { + 'content-type': 'application/json', + }, + }) + } + } + if (url.pathname.endsWith('/webcrypto')) { const response = {} try { diff --git a/test/integration/middleware/core/test/index.test.js b/test/integration/middleware/core/test/index.test.js index 0d53fca1c9ff2eb..d329d45e2d2a292 100644 --- a/test/integration/middleware/core/test/index.test.js +++ b/test/integration/middleware/core/test/index.test.js @@ -139,6 +139,20 @@ describe('Middleware base tests', () => { }) function urlTests(_log, locale = '') { + it('should set fetch user agent correctly', async () => { + const res = await fetchViaHTTP( + context.appPort, + `${locale}/interface/fetchUserAgentDefault` + ) + expect((await res.json()).headers['user-agent']).toBe('Next.js Middleware') + + const res2 = await fetchViaHTTP( + context.appPort, + `${locale}/interface/fetchUserAgentCustom` + ) + expect((await res2.json()).headers['user-agent']).toBe('custom-agent') + }) + it('rewrites by default to a target location', async () => { const res = await fetchViaHTTP(context.appPort, `${locale}/urls`) const html = await res.text()