diff --git a/.changeset/bumpy-cats-sneeze.md b/.changeset/bumpy-cats-sneeze.md new file mode 100644 index 000000000000..0aa6b4d30d2c --- /dev/null +++ b/.changeset/bumpy-cats-sneeze.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes an issue where requests with query parameters to the `base` path would return a 404 if trailingSlash was not `'ignore'` in development diff --git a/packages/astro/src/vite-plugin-astro-server/base.ts b/packages/astro/src/vite-plugin-astro-server/base.ts index 11d9c66078af..23e04690fc2b 100644 --- a/packages/astro/src/vite-plugin-astro-server/base.ts +++ b/packages/astro/src/vite-plugin-astro-server/base.ts @@ -13,8 +13,10 @@ export function baseMiddleware( logger: Logger, ): vite.Connect.NextHandleFunction { const { config } = settings; - const site = config.site ? new URL(config.base, config.site) : undefined; - const devRootURL = new URL(config.base, 'http://localhost'); + // The base may be an empty string by now, causing the URL creation to fail. We provide a default instead + const base = config.base || '/'; + const site = config.site ? new URL(base, config.site) : undefined; + const devRootURL = new URL(base, 'http://localhost'); const devRoot = site ? site.pathname : devRootURL.pathname; const devRootReplacement = devRoot.endsWith('/') ? '/' : ''; diff --git a/packages/astro/src/vite-plugin-astro-server/request.ts b/packages/astro/src/vite-plugin-astro-server/request.ts index 4b6c683d11c7..c859e3d4c5fe 100644 --- a/packages/astro/src/vite-plugin-astro-server/request.ts +++ b/packages/astro/src/vite-plugin-astro-server/request.ts @@ -47,6 +47,12 @@ export async function handleRequest({ } } + // Normalize root path to empty string when trailingSlash is 'never' and there's a non-root base + // This ensures consistent route matching for the index route (e.g., /base?query -> '') + if (config.trailingSlash === 'never' && pathname === '/' && config.base !== '/') { + pathname = ''; + } + // Add config.base back to url before passing it to SSR url.pathname = removeTrailingForwardSlash(config.base) + pathname; diff --git a/packages/astro/test/units/routing/trailing-slash.test.js b/packages/astro/test/units/routing/trailing-slash.test.js index 3a1c99df0ec7..ebb39d0a080b 100644 --- a/packages/astro/test/units/routing/trailing-slash.test.js +++ b/packages/astro/test/units/routing/trailing-slash.test.js @@ -213,6 +213,29 @@ describe('trailingSlash', () => { assert.equal(res.statusCode, 404); }); + // Test for issue #15095: Query params should not cause 404 when base is set and trailingSlash is never + it('should match root path with query params when base is set and trailingSlash is never', async () => { + const { req, res, text } = createRequestAndResponse({ + method: 'GET', + url: '/base?foo=bar', + }); + baseContainer.handle(req, res); + const json = await text(); + assert.equal(json, '{"success":true}'); + assert.equal(res.statusCode, 200); + }); + + it('should match sub path with query params when base is set and trailingSlash is never', async () => { + const { req, res, text } = createRequestAndResponse({ + method: 'GET', + url: '/base/injected?foo=bar', + }); + baseContainer.handle(req, res); + const json = await text(); + assert.equal(json, '{"success":true}'); + assert.equal(res.statusCode, 200); + }); + // Test for issue #13736: Astro.url.pathname should respect trailingSlash config with base it('Astro.url.pathname should not have trailing slash on root path when base is set and trailingSlash is never', async () => { const { req, res, text } = createRequestAndResponse({