How to Customize Error Pages

How to Customize Error Pages

When an exception is thrown, the core HttpKernel class catches it and dispatches a kernel.exception event. This gives you the power to convert the exception into a Response in a few different ways.

The core TwigBundle sets up a listener for this event which will run a configurable (but otherwise arbitrary) controller to generate the response. The default controller used has a sensible way of picking one out of the available set of error templates.

Thus, error pages can be customized in different ways, depending on how much control you need:

  1. Use the default ExceptionController and create a few templates that allow you to customize how your different error pages look (easy);
  2. Replace the default exception controller with your own (intermediate).
  3. Use the kernel.exception event to come up with your own handling (advanced).

Using the Default ExceptionController

By default, the showAction() method of the ExceptionController will be called when an exception occurs.

This controller will either display an exception or error page, depending on the setting of the kernel.debug flag. While exception pages give you a lot of helpful information during development, error pages are meant to be shown to the user in production.

You should not set kernel.debug to false in order to see your error pages during development. This will also stop Symfony from recompiling your twig templates, among other things.

The third-party WebfactoryExceptionsBundle provides a special test controller that allows you to display your custom error pages for arbitrary HTTP status codes even with kernel.debug set to true.

How the Template for the Error and Exception Pages Is Selected

The TwigBundle contains some default templates for error and exception pages in its Resources/views/Exception directory.

Tip

In a standard Symfony installation, the TwigBundle can be found at vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle. In addition to the standard HTML error page, it also provides a default error page for many of the most common response formats, including JSON (error.json.twig), XML (error.xml.twig) and even JavaScript (error.js.twig), to name a few.

Here is how the ExceptionController will pick one of the available templates based on the HTTP status code and request format:

  • For error pages, it first looks for a template for the given format and status code (like error404.json.twig);
  • If that does not exist or apply, it looks for a general template for the given format (like error.json.twig or exception.json.twig);
  • Finally, it ignores the format and falls back to the HTML template (like error.html.twig or exception.html.twig).

Tip

If the exception being handled implements the HttpExceptionInterface, the getStatusCode() method will be called to obtain the HTTP status code to use. Otherwise, the status code will be "500".

Overriding or Adding Templates

To override these templates, simply rely on the standard method for overriding templates that live inside a bundle. For more information, see Overriding Bundle Templates.

For example, to override the default error template, create a new template located at app/Resources/TwigBundle/views/Exception/error.html.twig:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>An Error Occurred: {{ status_text }}</title>
</head>
<body>
    <h1>Oops! An Error Occurred</h1>
    <h2>The server returned a "{{ status_code }} {{ status_text }}".</h2>
</body>
</html>

Caution

You must not use is_granted in your error pages (or layout used by your error pages), because the router runs before the firewall. If the router throws an exception (for instance, when the route does not match), then using is_granted will throw a further exception. You can use is_granted safely by saying {% if app.user and is_granted('...') %}.

Tip

If you're not familiar with Twig, don't worry. Twig is a simple, powerful and optional templating engine that integrates with Symfony. For more information about Twig see Creating and Using Templates.

This works not only to replace the default templates, but also to add new ones.

For instance, create an app/Resources/TwigBundle/views/Exception/error404.html.twig template to display a special page for 404 (page not found) errors. Refer to the previous section for the order in which the ExceptionController tries different template names.

Tip

Often, the easiest way to customize an error page is to copy it from the TwigBundle into app/Resources/TwigBundle/views/Exception and then modify it.

Note

The debug-friendly exception pages shown to the developer can even be customized in the same way by creating templates such as exception.html.twig for the standard HTML exception page or exception.json.twig for the JSON exception page.

Replacing the Default ExceptionController

If you need a little more flexibility beyond just overriding the template, then you can change the controller that renders the error page. For example, you might need to pass some additional variables into your template.

Caution

Make sure you don't lose the exception pages that render the helpful error messages during development.

To do this, simply create a new controller and set the twig.exception_controller option to point to it.

  • YAML
    1
    2
    3
    # app/config/config.yml
    twig:
        exception_controller:  AcmeFooBundle:Exception:showException
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    <!-- app/config/config.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:twig="http://symfony.com/schema/dic/twig"
        xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
            http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd">
    
        <twig:config>
            <twig:exception-controller>AcmeFooBundle:Exception:showException</twig:exception-controller>
        </twig:config>
    </container>
    
  • PHP
    1
    2
    3
    4
    5
    // app/config/config.php
    $container->loadFromExtension('twig', array(
        'exception_controller' => 'AcmeFooBundle:Exception:showException',
        // ...
    ));
    

Tip

You can also set up your controller as a service.

The default value of twig.controller.exception:showAction refers to the showAction method of the ExceptionController described previously, which is registered in the DIC as the twig.controller.exception service.

Your controller will be passed two parameters: exception, which is a FlattenException instance created from the exception being handled, and logger, an instance of DebugLoggerInterface (which may be null).

Tip

The Request that will be dispatched to your controller is created in the ExceptionListener. This event listener is set up by the TwigBundle.

You can, of course, also extend the previously described ExceptionController. In that case, you might want to override one or both of the showAction and findTemplate methods. The latter one locates the template to be used.

Caution

As of writing, the ExceptionController is not part of the Symfony API, so be aware that it might change in following releases.

Working with the kernel.exception Event

As mentioned in the beginning, the kernel.exception event is dispatched whenever the Symfony Kernel needs to handle an exception. For more information on that, see kernel.exception Event.

Working with this event is actually much more powerful than what has been explained before but also requires a thorough understanding of Symfony internals.

To give one example, assume your application throws specialized exceptions with a particular meaning to your domain.

In that case, all the default ExceptionListener and ExceptionController could do for you was trying to figure out the right HTTP status code and display your nice-looking error page.

Writing your own event listener for the kernel.exception event allows you to have a closer look at the exception and take different actions depending on it. Those actions might include logging the exception, redirecting the user to another page or rendering specialized error pages.

Note

If your listener calls setResponse() on the GetResponseForExceptionEvent, event propagation will be stopped and the response will be sent to the client.

This approach allows you to create centralized and layered error handling: Instead of catching (and handling) the same exceptions in various controllers again and again, you can have just one (or several) listeners deal with them.

Tip

To see an example, have a look at the ExceptionListener in the Security Component.

It handles various security-related exceptions that are thrown in your application (like AccessDeniedException) and takes measures like redirecting the user to the login page, logging them out and other things.

Good luck!

This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License .