branded_error_page
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 viaErrorDocument(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 thebranded_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 reportspermission, and
system.logging:error_levelofverboseor
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.404you have
configured, so existing custom pages always win. -
Drush integration.
drush branded-error-page:regenerate
(aliasbep: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
/recipesonce 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_pagelog channel.
Documentation and support
- Full configuration walkthrough, Twig variable reference, and troubleshooting
guide are in the module'sREADME.md. - Issues and feature requests: use the issue queue on this project page.
- Security issues: please follow the standard Drupal security reporting
process.