使用事件订阅器在 Drupal 中添加 500 错误页面
我们经常会遇到 500 错误页面,尤其当 Drupal、服务或其他站点不可用时。出现 500(或 501-504)错误页面时,Drupal 会通过异常(Exceptions)来处理关键代码错误。例如在向其他网站发起 HTTP 请求时发生错误,Drupal 会显示类似如下的错误信息:“网站遇到意外错误,请稍后再试”:

在你的网站上出现 WSOD(白屏死机)并不好,因此我们来优化这个页面,改为展示一个有样式的 HTML 页面。
我在站点根目录放置了一个已经设计好的 500.html 页面,这样做是为了性能原因。虽然我们也可以使用 Drupal 页面作为 500 错误页,但我打算将该页面同时用于 Apache/Nginx 的 503/504 错误,因此将其保存在一个统一位置的单一 HTML 文件中更为便捷。

现在我们需要在自定义模块 DrupalBook Custom(drupalbook_custom)中添加代码。在 drupalbook_custom.services.yml 中注册事件订阅器:
services:
drupalbook_custom.exception_subscriber:
class: Drupal\drupalbook_custom\EventSubscriber\SeoExceptionSubscriber
arguments: ['@config.factory']
tags:
- { name: event_subscriber, priority: -250 }
以下是 drupalbook_custom/src/EventSubscriber/SeoExceptionSubscriber 的代码:
<?php
namespace Drupal\drupalbook_custom\EventSubscriber;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Utility\Error;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\KernelEvents;
class SeoExceptionSubscriber implements EventSubscriberInterface {
use StringTranslationTrait;
protected ConfigFactoryInterface $configFactory;
public function __construct(ConfigFactoryInterface $config_factory) {
$this->configFactory = $config_factory;
}
public function onException(ExceptionEvent $event): void {
if ($this->isErrorLevelVerbose()) {
return;
}
$exception = $event->getThrowable();
$error = Error::decodeException($exception);
$message = new FormattableMarkup('@message', [
'@message' => $error['!message'] ?? $this->t('The website encountered an unexpected error.'),
]);
$html = $this->buildHtml((string) $message);
$status = $exception instanceof HttpExceptionInterface
? $exception->getStatusCode()
: Response::HTTP_INTERNAL_SERVER_ERROR;
$response = new Response($html, $status, ['Content-Type' => 'text/html']);
if ($exception instanceof HttpExceptionInterface) {
$response->headers->add($exception->getHeaders());
}
$event->setResponse($response);
$event->stopPropagation();
}
protected function buildHtml(string $message): string {
$template = DRUPAL_ROOT . '/500.html';
if (is_readable($template)) {
$html = file_get_contents($template);
return str_replace('{{ message }}', Markup::create($message), $html);
}
return '<html><head><title>500</title></head><body>'
. Markup::create($message)
. '</body></html>';
}
protected function isErrorLevelVerbose(): bool {
return $this->configFactory
->get('system.logging')
->get('error_level') === ERROR_REPORTING_DISPLAY_VERBOSE;
}
public static function getSubscribedEvents(): array {
$events[KernelEvents::EXCEPTION][] = ['onException', -250];
return $events;
}
}
这个订阅器类 SeoExceptionSubscriber
会拦截 Drupal 中所有未捕获的异常。如果站点设置为详细(verbose)错误报告模式,它将允许 Drupal 显示默认的详细错误信息。而在非 verbose 模式下(如生产环境),它会捕获异常并显示用户友好的错误页面。
具体来说,它会读取 Drupal 根目录下的 500.html
页面,将错误信息插入 {{ message }}
占位符,并返回完整的 HTML 页面作为响应。
此外,订阅器还会阻止 Drupal 默认的错误处理器进一步执行,确保不会覆盖你自定义的错误页。通过设置优先级为 -250
,该订阅器的执行时机刚好早于 Drupal 核心的默认处理器(优先级 -256)。
如果你希望在本地环境中显示详细错误信息,而不是 500 页面,可以在 settings.php 中添加如下设置:
$config['system.logging']['error_level'] = 'verbose';
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
如果 Drupal 无法访问,你可能还需要为 Web 服务器或云环境设置额外的错误页面配置。
在 Apache 中添加 500 错误页面
要在 Apache 中为 HTTP 500–504 错误显示自定义 500.html
页面,你可以如下配置:
1. 编辑 Apache 虚拟主机配置(推荐方式)
在 /etc/apache2/sites-available/your-site.conf
中的 <VirtualHost>
区块添加:
ErrorDocument 500 /500.html
ErrorDocument 501 /500.html
ErrorDocument 502 /500.html
ErrorDocument 503 /500.html
ErrorDocument 504 /500.html
然后重载 Apache:
sudo systemctl reload apache2
2. 使用 .htaccess
文件
在站点根目录的 .htaccess
文件中添加:
ErrorDocument 500 /500.html
ErrorDocument 501 /500.html
ErrorDocument 502 /500.html
ErrorDocument 503 /500.html
ErrorDocument 504 /500.html
确保 500.html
存在于站点根目录,并且可被 Apache 读取。配置完成后,Apache 将在出现 500-504 错误时显示该页面。
在 Nginx 中添加 500 错误页面
在 Nginx 中配置以使用自定义 500.html
页面,请编辑 /etc/nginx/sites-available/your-site.conf
,在 server {}
区块中添加:
error_page 500 501 502 503 504 /500.html;
location = /500.html {
root /var/www/html;
internal;
}
确保 /var/www/html
是你的站点根目录,并包含 500.html
文件。然后重载 Nginx 配置:
sudo nginx -s reload
完成后,Nginx 会在 500–504 错误时统一显示该自定义 HTML 页面。