@@ -157,36 +157,36 @@ export const createRouter = <
157157 }
158158 }
159159
160+ // Normalize the configured base path once. `basePath` is configuration, not
161+ // per-request input, so trailing-slash normalization belongs here rather than
162+ // in the request hot path. An empty result (unset, "/", or all slashes) means
163+ // "no base path": route on the full pathname.
164+ const basePath =
165+ config ?. basePath && config . basePath !== "/"
166+ ? config . basePath . replace ( / \/ + $ / , "" )
167+ : "" ;
168+
160169 const processRequest = async ( request : Request ) => {
161170 const url = new URL ( request . url ) ;
162171 const pathname = url . pathname ;
163172 // Strip `basePath` only when it is a leading, "/"-boundary prefix of the
164173 // request pathname. A pathname that does not start with the configured
165- // basePath is outside this router and resolves to a 404. The previous
166- // implementation stripped basePath wherever it occurred
167- // (`pathname.split(basePath)`), which let a path like "/x/api/test"
168- // resolve onto the internal route "/test" even though it is not mounted
169- // under basePath.
170- let path : string | null ;
171- if ( config ?. basePath && config . basePath !== "/" ) {
172- // Normalize trailing slashes so "/api/auth/" and "/api/auth" behave the same.
173- const normalizedBasePath = config . basePath . replace ( / \/ + $ / , "" ) ;
174- if ( ! normalizedBasePath ) {
175- path = pathname ;
176- } else if ( pathname . startsWith ( `${ normalizedBasePath } /` ) ) {
177- path = pathname . slice ( normalizedBasePath . length ) ;
178- } else {
179- path = null ;
174+ // basePath is outside this router and resolves to a 404, so a path like
175+ // "/x/api/test" never reaches "/test". The "/" boundary also rejects a
176+ // sibling prefix such as "/api/authevil/...". The previous implementation
177+ // stripped basePath wherever it occurred (`pathname.split(basePath)`).
178+ let path : string ;
179+ if ( basePath ) {
180+ if ( ! pathname . startsWith ( `${ basePath } /` ) ) {
181+ return new Response ( null , { status : 404 , statusText : "Not Found" } ) ;
180182 }
183+ path = pathname . slice ( basePath . length ) ;
181184 } else {
182- path = url . pathname ;
183- }
184- if ( path === null || path . length === 0 ) {
185- return new Response ( null , { status : 404 , statusText : "Not Found" } ) ;
185+ path = pathname ;
186186 }
187187
188- // Reject paths with consecutive slashes
189- if ( / \/ { 2 , } / . test ( path ) ) {
188+ // Reject empty paths and paths with consecutive slashes.
189+ if ( path . length === 0 || / \/ { 2 , } / . test ( path ) ) {
190190 return new Response ( null , { status : 404 , statusText : "Not Found" } ) ;
191191 }
192192
0 commit comments