I trying to set up a 404.html page template but the issue that I am running into is that dash returns a 200 when a request is made to a page that does not exist. Why is that?
Dash handles 404’s internally and returns a valid response with the 404 dash component layout. If you haven’t set a not_found_404.py file dash is going to render a basic Title.
Just create the mentioned file and ad the styles from the html to a css file in your assets and you are good to go.
Thank you for the response but that option only inserts a layout when there is a “404” into dash.page_container and keeps the default layout. I really just want to return a static html file. I will probably end up going with something like
@app.server.before_request
def override_dash_404():
""" Ensure unknown paths return a proper 404 page. """
# Store the stripped request path once
requested_path = flask.request.path.strip("/")
# Known Dash and Flask routes
dash_routes = {page["path"].strip("/") for page in dash.page_registry.values()}
flask_routes = {rule.rule.strip("/") for rule in app.server.url_map.iter_rules()}
# ✅ Allow valid Dash routes
if requested_path in dash_routes:
return # Let Dash handle it
# ✅ Allow valid Flask routes
if requested_path in flask_routes:
return # Let Flask handle it
# ✅ Allow Dash-internal API calls (updates, assets, favicon, etc.)
if flask.request.path.startswith(("/_dash", "/assets/", "/favicon.ico")):
return # Let Dash handle it
# ❌ Everything else gets a real 404 error
flask.abort(404) # This ensures Flask's real 404 handling takes over
@app.server.errorhandler(404)
def page_not_found(error):
""" Serve a proper 404 page instead of Dash's fake 404 """
return flask.render_template("404.html"), 404
It would have taken me a lot of time to figure it out, and it works with minimal changes to my existing code. Moreover, it seems to play nice with multi-page Dash app.
I believe the solution could be slightly improved:
dash_routes = ... could be taken outside the function, as it does not rely on requested_path, so no need to re-compute it on each call;
for flask_routes, maybe Flask’s pattern/rule matching infrastructure could be used? There are rules like 'assets/<path:filename>', so the third check (.startswith(...)) would not be needed.
Did you improve it eventually? Am I missing anything?
Update. A slightly improved version that worked in my case. Additionally, use PyGuy’s page_not_found() to render a page.
all_page_paths = {page["path"]: page for page in dash.page_registry.values()}
@app.server.before_request
def override_dash_404():
if (flask.request.endpoint == "/<path:path>") and (flask.request.path not in all_page_paths):
flask.abort(404)