Drupal is a registered trademark of Dries Buytaert
drupal 11.3.7 Update released for Drupal core (11.3.7)! drupal 11.2.11 Update released for Drupal core (11.2.11)! drupal 10.6.7 Update released for Drupal core (10.6.7)! drupal 10.5.9 Update released for Drupal core (10.5.9)! cms 2.1.1 Update released for Drupal core (2.1.1)! drupal 11.3.6 Update released for Drupal core (11.3.6)! drupal 10.6.6 Update released for Drupal core (10.6.6)! cms 2.1.0 Update released for Drupal core (2.1.0)! bootstrap 8.x-3.40 Minor update available for theme bootstrap (8.x-3.40). menu_link_attributes 8.x-1.7 Minor update available for module menu_link_attributes (8.x-1.7). eca 3.1.1 Minor update available for module eca (3.1.1). layout_paragraphs 2.1.3 Minor update available for module layout_paragraphs (2.1.3). ai 1.3.3 Minor update available for module ai (1.3.3). ai 1.2.14 Minor update available for module ai (1.2.14). node_revision_delete 2.0.3 Minor update available for module node_revision_delete (2.0.3). moderated_content_bulk_publish 2.0.52 Minor update available for module moderated_content_bulk_publish (2.0.52). klaro 3.0.10 Minor update available for module klaro (3.0.10). klaro 3.0.9 Minor update available for module klaro (3.0.9). layout_paragraphs 2.1.2 Minor update available for module layout_paragraphs (2.1.2). geofield_map 11.1.8 Minor update available for module geofield_map (11.1.8).

A themable, brandable replacement for Drupal's plain-text
"The website encountered an unexpected error. Please try again later."
message, backed by a static HTML fallback for the moments when Drupal
itself is on fire.

Why this module

Drupal's built-in error page is unbranded, uninformative, and in the
worst case (fatal PHP or database failure) not served at all. This
module replaces it with a fully themed, configurable page AND a
pre-rendered static snapshot the web server can serve when Drupal
cannot run.

Features

  • Themed application-level error page. Intercepts the
    kernel exception flow and renders a branded page through Twig for
    uncaught exceptions and HTTP 500 / 503 responses.
  • Two-tier resilience.
    Tier 1 renders live when Drupal is alive but the request
    failed. Tier 2 is a static HTML snapshot written to
    public://branded_error_page/ with inline CSS and zero
    external assets, mapped via ErrorDocument (Apache) or
    error_page (Nginx).
  • Per-error-type branding. Separate title, message
    body, technical-details toggle, optional logo / support-email /
    support-URL overrides, a "Back to homepage" toggle, and up to three
    extra call-to-action buttons for HTTP 500, 503, and a generic
    catch-all.
  • Reference IDs. Every error gets a short
    eight-character token, logged to the branded_error_page
    channel and surfaced on the page so support staff can correlate
    user reports with the underlying log entry.
  • Safe technical-details reveal. Stack traces are
    gated by three independent checks: per-type config toggle,
    access site reports permission, and
    system.logging:error_level of verbose or
    all. Anonymous users never see them, regardless of
    config.
  • Optional 403 / 404 handling. Off by default. When
    enabled, still defers to any custom
    system.site:page.403 / page.404 you have
    configured, so existing custom pages always win.
  • Drush integration.
    drush branded-error-page:regenerate
    (alias bep:regen) rebuilds snapshots on demand.
  • Live preview. Each error type renders in a new tab
    from the settings form, using sample content with a synthetic
    reference ID.
  • Accessible. Semantic HTML, proper heading
    hierarchy, ARIA live region for the reference token, no
    color-only signaling.
  • Themeable. CSS custom properties, a single Twig
    template, and a preprocess hook for downstream overrides.

How the two tiers work

Tier 1: a KernelEvents::EXCEPTION
subscriber runs at a low priority, after core's logging and
format-negotiation subscribers, and produces a themed response for
requests that accept HTML. JSON / API clients are left to core.

Tier 2: on config save, install, and on demand via
Drush, the module renders each error type to a standalone HTML file
under public://branded_error_page/. The file is fully
self-contained (inline CSS, logo inlined as an absolute URL) so it
works when PHP, the database, or the theme system is unavailable.
Map it via your web server:

# Nginx
  error_page 500 502 504 = @branded_500;
  error_page 503        = @branded_503;

  location @branded_500 {
      internal;
      root /var/www/html/web;
      try_files /sites/default/files/branded_error_page/500.html =500;
  }
  location @branded_503 {
      internal;
      root /var/www/html/web;
      try_files /sites/default/files/branded_error_page/503.html =503;
  }
# Apache
  ErrorDocument 500 /sites/default/files/branded_error_page/500.html
  ErrorDocument 502 /sites/default/files/branded_error_page/500.html
  ErrorDocument 503 /sites/default/files/branded_error_page/503.html
  ErrorDocument 504 /sites/default/files/branded_error_page/500.html

Configuration

Visit Configuration > System > Branded Error Page
(/admin/config/system/branded-error-page). The form is
grouped into global branding, three per-type fieldsets (HTTP 500, 503,
Generic), and a snapshot section. Each error type supports:

  • Title, filtered message body, technical-details toggle.
  • Per-type logo, support email, and support URL overrides. All three
    inherit from the global branding when left blank.
  • A "Back to homepage" link toggle, rendered as a tertiary action.
  • Up to three extra call-to-action buttons, each with a label and a
    URL. Validation rejects non-http(s) URLs and paths
    that do not start with /.

Permissions

The settings form is gated by
administer branded error pages, which deliberately
does NOT require administer site configuration so brand
control can be delegated to a content-ops role without granting full
site-config access. Viewing technical details on the rendered page
additionally requires access site reports.

How it differs from similar modules

Module What it does How this differs Custom Error Lets you set the body of 403 / 404 pages via routes you define. Route-based, so it can only help when Drupal is able to render.
Branded Error Page intercepts the exception flow itself and
adds the Tier 2 static fallback for when Drupal cannot run. Fast 404 Short-circuits 404s for asset paths to skip a full bootstrap. Performance optimization for known-missing assets. Branded
Error Page is a branding and resilience layer for fatal
failures. The two compose well; install both.

Requirements

  • Drupal 10.3 or later, or Drupal 11.
  • PHP 8.1 or later.
  • A writable public:// filesystem for Tier 2 snapshots.
  • Drush 12+ (optional, for the regenerate command).

No contrib dependencies.

Security

  • Message bodies always pass through a filter format. Default install uses
    restricted_html.
  • Technical-details reveal is gated by three independent checks; anonymous users
    never see stack traces.
  • Logo uploads are restricted to image mimetypes.
  • Reference IDs are random, not sequential, and reveal no internal state.
  • The static snapshot contains no secrets, only configured public branding text,
    the logo, and CSS.

Roadmap

Will do:

  • Per-language snapshot files with server-side Accept-Language
    mapping.
  • Webhook on snapshot regeneration so external CDN purges can hook in.
  • Lando / DDEV recipes under /recipes once the API stabilizes.

Will not do:

  • Full WYSIWYG page builder for the error page. Use a theme.
  • Automatic incident reporting to third parties. Wire your own handler to the
    branded_error_page log channel.

Documentation and support

  • Full configuration walkthrough, Twig variable reference, and troubleshooting
    guide are in the module's README.md.
  • Issues and feature requests: use the issue queue on this project page.
  • Security issues: please follow the standard Drupal security reporting
    process.

Activity

Total releases
1
First release
Apr 2026
Latest release
1 day ago
Release cadence
Stability
100% stable

Releases

Version Type Release date
1.0.0 Stable Apr 17, 2026