Aggiunta della pagina di errore 500 in Drupal utilizzando un Event Subscriber
Spesso ci troviamo di fronte alla pagina di errore 500 quando Drupal, i servizi o altri siti non sono disponibili. Quando vediamo una pagina di errore 500 (o 501-504). In Drupal usiamo le Exception per controllare che un certo codice critico sia stato eseguito. Se riceviamo un errore, ad esempio in una richiesta HTTP a un altro sito, allora Drupal mostrerà questo errore: "Il sito ha riscontrato un errore imprevisto. Riprova più tardi":

Non è una buona pratica avere la WSOD (white screen of death) sul tuo sito, quindi miglioriamola e mostriamo invece una pagina HTML stilizzata.
Ho una pagina 500.html
stilizzata nella root del mio sito, per motivi di performance. Possiamo usare una pagina Drupal stilizzata per l'errore 500, ma io userò la stessa pagina anche per gli errori 503/504 in Apache/Nginx ed è più semplice mantenere questa pagina in un unico file HTML.

Ora dobbiamo aggiungere codice nel nostro modulo personalizzato DrupalBook Custom (drupalbook_custom
). In drupalbook_custom.services.yml
dobbiamo aggiungere un Event Subscriber:
services:
drupalbook_custom.exception_subscriber:
class: Drupal\drupalbook_custom\EventSubscriber\SeoExceptionSubscriber
arguments: ['@config.factory']
tags:
- { name: event_subscriber, priority: -250 }
Ecco il codice per 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;
/**
* Sostituisce il 500 error di Drupal\Core\EventSubscriber\FinalExceptionSubscriber.
*/
class SeoExceptionSubscriber implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* Configurazioni delle impostazioni.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected ConfigFactoryInterface $configFactory;
public function __construct(ConfigFactoryInterface $config_factory) {
$this->configFactory = $config_factory;
}
/**
* Gestisce qualsiasi eccezione non intercettata e restituisce una risposta HTML personalizzata.
*/
public function onException(ExceptionEvent $event): void {
// Mostra il normale stack trace di Drupal quando il sito è in modalità VERBOSE.
if ($this->isErrorLevelVerbose()) {
return;
}
$exception = $event->getThrowable();
// Messaggio di base (estendibile in modalità verbose se necessario).
$error = Error::decodeException($exception);
$message = new FormattableMarkup('@message', [
'@message' => $error['!message'] ?? $this->t('Il sito ha riscontrato un errore imprevisto.'),
]);
$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']);
// Mantieni header extra come Retry-After se presenti.
if ($exception instanceof HttpExceptionInterface) {
$response->headers->add($exception->getHeaders());
}
// Invia la risposta e interrompi altri subscriber (incluso quello core).
$event->setResponse($response);
$event->stopPropagation();
}
/**
* Legge web/500.html e sostituisce il token {{ message }} se presente.
*/
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);
}
// Fallback sicuro se il template è mancante.
return '<html><head><title>500</title></head><body>'
. Markup::create($message)
. '</body></html>';
}
/**
* TRUE quando il livello di errore è impostato su “Verbose”.
*
* Replica \Drupal\Core\EventSubscriber\FinalExceptionSubscriber::isErrorLevelVerbose().
*/
protected function isErrorLevelVerbose(): bool {
return $this->configFactory
->get('system.logging')
->get('error_level') === ERROR_REPORTING_DISPLAY_VERBOSE;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
// Priorità -250 → viene eseguito appena prima di FinalExceptionSubscriber del core (-256).
$events[KernelEvents::EXCEPTION][] = ['onException', -250];
return $events;
}
}
Questa classe subscriber, SeoExceptionSubscriber
, intercetta tutte le eccezioni non gestite in Drupal. Controlla se il tuo sito Drupal è impostato sulla modalità di report degli errori "verbose" e, in tal caso, permette a Drupal di mostrare i suoi normali messaggi di errore dettagliati. Tuttavia, se il sito non è in modalità verbose (tipico in ambiente di produzione), cattura l'eccezione e prepara invece un messaggio di errore più user-friendly.
Nello specifico, legge il tuo file personalizzato 500.html
posizionato nella root dell'installazione Drupal. Inserisce dinamicamente il messaggio di errore nel contenuto HTML sostituendo il token {{ message }}
, garantendo che la pagina mostrata sia sia informativa che visivamente coerente.
Inoltre, lo subscriber interrompe esplicitamente il gestore di errori predefinito di Drupal. Questo assicura che la pagina di errore interna di Drupal non sovrascriva la tua pagina HTML personalizzata. Definendo lo subscriber con una priorità di -250
, viene eseguito appena prima dello subscriber integrato di Drupal core, sovrascrivendo efficacemente il comportamento predefinito di Drupal.
Per il tuo ambiente locale puoi inserire impostazioni per mostrare gli errori invece della pagina di errore 500.
settings.php
:
$config['system.logging']['error_level'] = 'verbose';
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
A volte Drupal non è raggiungibile, quindi sarà necessario impostare configurazioni aggiuntive per il tuo web server o Cloud.
Aggiungere la pagina di errore 500 in Apache
Per servire la tua pagina di errore 500.html
esistente dalla root del tuo sito ogni volta che si verificano errori HTTP 500–504, devi configurare Apache di conseguenza. Ecco due modi semplici per farlo:
1. Utilizzando la configurazione Virtual Host di Apache (raccomandato)
Modifica il file di configurazione del virtual host del tuo sito (tipicamente situato in /etc/apache2/sites-available/your-site.conf
) e aggiungi le seguenti direttive all’interno del blocco <VirtualHost>
:
ErrorDocument 500 /500.html
ErrorDocument 501 /500.html
ErrorDocument 502 /500.html
ErrorDocument 503 /500.html
ErrorDocument 504 /500.html
Poi ricarica Apache per applicare i cambiamenti:
sudo systemctl reload apache2
2. Utilizzando il file .htaccess
Se preferisci usare il file .htaccess
(posizionato nella root del tuo sito), inserisci semplicemente queste righe:
ErrorDocument 500 /500.html
ErrorDocument 501 /500.html
ErrorDocument 502 /500.html
ErrorDocument 503 /500.html
ErrorDocument 504 /500.html
Assicurati che il file 500.html
sia collocato nella directory root del tuo sito, accessibile e leggibile da Apache. Dopo aver applicato queste impostazioni, Apache mostrerà in modo coerente la tua pagina HTML stilizzata per gli errori dal 500 al 504.
Aggiungere la pagina di errore 500 in Nginx
Per configurare Nginx in modo che serva la tua pagina di errore personalizzata 500.html
posizionata nella root del sito per errori HTTP (500–504), aggiorna la configurazione del tuo server Nginx come segue:
Modifica il file di configurazione del tuo sito Nginx (tipicamente in /etc/nginx/sites-available/your-site.conf
) e inserisci le seguenti direttive all’interno del blocco server {}
:
error_page 500 501 502 503 504 /500.html;
location = /500.html {
root /var/www/html;
internal;
}
Assicurati che il percorso (/var/www/html
) punti correttamente alla root del tuo sito che contiene il file 500.html
. Dopo la modifica, ricarica la configurazione di Nginx per applicare le modifiche:
sudo nginx -s reload
Ora Nginx mostrerà la tua pagina di errore HTML personalizzata in modo coerente per gli stati di errore HTTP dal 500 al 504.