Skip to main content

Custom error pages in Flask

You can use app.error_handler to add custom responses for HTTP status codes, so the errors match the look and feel of the rest of the site.

You can customise the error pages by adding a few instances of app.error_handler, for example:

@app.errorhandler(404)
def not_found(e: Exception) -> str:
    return "Not found!", 404


@app.errorhandler(500)
def internal_server_error(e: Exception) -> str:
    return "Internal server error!", 500

If it can’t render a custom error page, it will throw a 500 error. If it can’t render the custom 500 Internal Server Error page, it will return Flask’s generic 500 Internal Server Error page.

A custom route for 500 errors

I find it helpful to add a route that I can use to test the behaviour of 500 pages in the prod environment, which looks like this:

@app.route("/500/")
def deliberate_error() -> str:
    raise ValueError("BOOM!")

Including tests

Here’s a complete program to illustrate this pattern, including tests for the two custom error pages:

from flask import Flask

app = Flask(__name__)


@app.errorhandler(404)
def not_found(e: Exception) -> str:
    return "Not found!", 404


@app.errorhandler(500)
def internal_server_error(e: Exception) -> str:
    return "Internal server error!", 500


@app.route("/500/")
def deliberate_error() -> str:
    raise ValueError("BOOM!")


def test_custom_404_page():
    # This creates a test instance of the Flask app, as described
    # in the Flask docs on testing.
    # See https://flask.palletsprojects.com/en/3.0.x/testing/#fixtures
    app.config["TESTING"] = True

    with app.test_client() as client:
        resp = client.get("/404/")

        assert resp.status_code == 404
        assert b"Not found!" in resp.data


def test_custom_500_page():
    # This is similar to the test instance above, but we disable
    # TESTING -- this means that Flask will render the error page
    # as it would be shown to a real user, not the Werkzeug error
    # page shown for local development/debugging.
    app.config["TESTING"] = False

    with app.test_client() as client:
        resp = client.get("/500/")

        assert resp.status_code == 500
        assert b"Internal server error!" in resp.data