Skip to content

fix(routing): normalize trailing slashes on mounted routes#3377

Closed
pctablet505 wants to merge 2 commits into
Kludex:mainfrom
pctablet505:fix-869-trailing-slashes-mounted-routes
Closed

fix(routing): normalize trailing slashes on mounted routes#3377
pctablet505 wants to merge 2 commits into
Kludex:mainfrom
pctablet505:fix-869-trailing-slashes-mounted-routes

Conversation

@pctablet505

@pctablet505 pctablet505 commented Jul 15, 2026

Copy link
Copy Markdown

Fixes #869

Mount('/users', routes=[Route('/', users), Route('/{username}', user)]) currently only matches when the request path ends with a slash. GET /users returns a 307 redirect to /users/, and GET /users/bob/ returns a 307 redirect to /users/bob, while a top-level Route('/', ...) responds to both / and an empty path. This asymmetry between Route and Mount has been reported repeatedly: external webhook callers that don't follow redirects fail, Ariadne's GraphQL mount is unreachable at /graphql, and people have resorted to setting app.router.redirect_slashes = False or adding middleware to rewrite the path.

In #823 the maintainer explained that requiring the slash was intentional, because a mount point is meant to be /path-to-mount/{...} and /some-path should not match /some-path{...}. That constraint is still respected here: /usersa still 404s.

This change rewrites the catch-all path regex that Mount builds from compile_path(self.path + "/{path:path}") so the trailing slash is optional. When the mount point is hit without a slash, the router appends the slash to scope["path"] internally before dispatching to the mounted app, so Route('/', ...) resolves directly and Route('/{username}', ...) works with or without a trailing slash. The existing test_mount_urls assertion is updated to expect /users to stay /users instead of redirecting to /users/, and a new test_mount_trailing_slash_with_child_routes covers the child-route cases.

The main trade-off is that /users no longer redirects to /users/; it now serves the mounted content directly. That removes the 307 pain for mounts but changes behavior for anyone relying on the old redirect. Other Route redirects and redirect_slashes behavior are unaffected.

Verified by running pytest tests/test_routing.py.

Make Mount routes match both the trailing-slash and no-trailing-slash
forms of the mount point, so /users and /users/ behave consistently.

Fixes Kludex#869
@pctablet505

Copy link
Copy Markdown
Author

Closing this for now — I want to rethink the approach before taking up review time here. Sorry for the noise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Handling trailing slashes properly with mounted routes

1 participant