Flask route without trailing slash returns 404 when slash is appended — how should both variants be handled?
0 reputation · 18 Apr 2023, 16:48 UTC
0 reputation · 18 Apr 2023, 16:48 UTC
I am defining URL rules in a Flask application using the standard Werkzeug-based routing system. I noticed that the behavior differs depending on whether the route decorator includes a trailing slash: a route defined as /about/ redirects requests for /about to the slashed version, while a route defined as /about returns a 404 when /about/ is requested.
My goal is to pick a consistent convention for a project that sits behind a reverse proxy, and I am unsure how the redirect and 404 behavior interacts with proxies that may normalize or strip trailing slashes before forwarding the request. I am working with a current Flask 3.x release and default Werkzeug routing, with no custom URL converters.
Is the redirect from the non-slashed to the slashed form a permanent (301) redirect that clients and caches will store long-term? If the proxy strips trailing slashes, could the slashed-route redirect create a redirect loop? Is there a documented, supported way to make both /about and /about/ serve the same view without issuing a redirect?
32775 reputation · 18 Apr 2023, 18:53 UTC
In Flask 3.x the following holds for a rule defined as /about/:
/about triggers a 308 Permanent Redirect to /about/ (older Werkzeug used 301)./about/ with a rule defined as /about returns 404 Not Found.Both forms can be served without a redirect by either:
strict_slashes=False on the slash‑terminated rule:@app.route('/about/', strict_slashes=False)
def about():
return "About page"
@app.route('/about')
@app.route('/about/')
def about():
return "About page"
Both approaches return 200 OK for /about and /about/.
The 308 is a permanent redirect. Browsers and caches may store it indefinitely, so the canonical form should be chosen consciously. If you want /about to remain the canonical URL, keep the rule without a trailing slash and let Flask redirect to /about/.
When a reverse proxy strips trailing slashes before forwarding the request, a /about/ rule can cause a loop: proxy → Flask (404) → Flask redirect → proxy strips again. The usual fix is to configure the proxy to forward the path unchanged or to use strict_slashes=False so Flask accepts both forms.
strict_slashes=False or duplicate decorators.curl -i http://localhost:5000/about and curl -i http://localhost:5000/about/.Does your reverse proxy rewrite the request path (e.g., strip trailing slashes) or simply pass it through unchanged? Knowing this will determine whether you should rely on Flask’s redirect or serve both forms directly.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.