Flask Custom Error Pages

By default, Flask shows a plain white page when an error occurs. Custom error pages let you replace these with branded, helpful pages that match your site's design and guide users toward a solution.

Common HTTP Error Codes

CodeNameWhen It Appears
400Bad RequestMalformed request from browser
403ForbiddenUser lacks permission to access the page
404Not FoundURL does not match any route
405Method Not AllowedWrong HTTP method for the route
500Internal Server ErrorUnhandled Python exception in your code

Registering Error Handlers

Use the @app.errorhandler() decorator to register a function for a specific error code. Flask calls this function whenever that error occurs, and the function returns the custom error page.

from flask import Flask, render_template

app = Flask(__name__)

@app.errorhandler(404)
def page_not_found(error):
    return render_template('errors/404.html'), 404

@app.errorhandler(500)
def server_error(error):
    return render_template('errors/500.html'), 500

@app.errorhandler(403)
def forbidden(error):
    return render_template('errors/403.html'), 403

The function receives the error object as an argument. Always return the correct HTTP status code as the second return value — without it, Flask sends 200 OK even for error pages.

Creating the Error Templates

Create templates/errors/404.html:

{% extends 'base.html' %}

{% block title %}Page Not Found{% endblock %}

{% block content %}
  <h1>404 — Page Not Found</h1>
  <p>The page you're looking for doesn't exist or has been moved.</p>
  <a href="{{ url_for('home') }}">Go back to the home page</a>
{% endblock %}

Create templates/errors/500.html:

{% extends 'base.html' %}

{% block title %}Server Error{% endblock %}

{% block content %}
  <h1>500 — Something Went Wrong</h1>
  <p>We're working to fix this. Please try again in a few minutes.</p>
  <a href="{{ url_for('home') }}">Return home</a>
{% endblock %}

How Flask Error Handling Works

Request arrives → Flask tries to match a route
        │
  Route matched? → NO → @app.errorhandler(404) runs
        │
  Route matched → View function runs
        │
  Exception raised? → YES → @app.errorhandler(500) runs
        │
  Everything OK → Normal response sent

Raising Errors Manually

Your own code can trigger error responses using Flask's abort() function. This immediately stops the current request and triggers the appropriate error handler.

from flask import abort

@app.route('/admin')
def admin_panel():
    user_is_admin = False  # check real permission here
    if not user_is_admin:
        abort(403)  # triggers the 403 error handler
    return '<h1>Admin Panel</h1>'
User visits /admin
       │
user_is_admin = False
       │
abort(403) called
       │
Flask triggers: @app.errorhandler(403)
       │
User sees: custom 403 Forbidden page

Using abort() with a Custom Message

Pass a description string to abort() by raising an HTTPException directly:

from werkzeug.exceptions import NotFound

raise NotFound(description='The user ID you requested does not exist.')

The error object passed to the handler contains this description in error.description:

@app.errorhandler(404)
def page_not_found(error):
    return render_template('errors/404.html', message=error.description), 404

Catching All HTTP Errors

Register a handler for all HTTP exceptions at once using HTTPException:

from werkzeug.exceptions import HTTPException

@app.errorhandler(HTTPException)
def handle_http_error(error):
    return render_template(
        'errors/generic.html',
        code=error.code,
        name=error.name,
        description=error.description
    ), error.code

Summary

Custom error pages replace Flask's default white error screens with on-brand, helpful pages. Register handlers with @app.errorhandler(code) and render a template with the correct status code as the second return value. Use abort(code) inside view functions to trigger error handlers for permission or logic errors. Always extend your base template so error pages look consistent with the rest of your site.

Leave a Comment

Your email address will not be published. Required fields are marked *