How can we catch the acces denied exception?
To catch the access error you can use a symfony event listener:


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


http://symfony.com/doc/current/controller/error_pages.html#working-with-the-kernel-exception-event

This is an example how to return a GetResponseForExceptionEvent event:

Drupal\Core\EventSubscriber\CustomPageExceptionHtmlSubscriber

public function on403(GetResponseForExceptionEvent $event) {
    $custom_403_path = $this-&gt;configFactory-&gt;get('system.site')-&gt;get('page.403');
    if (!empty($custom_403_path)) {
      $this-&gt;makeSubrequestToCustomPath($event, $custom_403_path, Response::HTTP_FORBIDDEN);
    }
  }


For your own Event Subscriber extend DefaultExceptionHtmlSubscriber.

class MyCustomPageExceptionHtmlSubscriber extends DefaultExceptionHtmlSubscriber


See the complete example:

http://cgit.drupalcode.org/drupal/tree/core/lib/Drupal/Core/EventSubscriber/CustomPageExceptionHtmlSubscriber.php

which extends

http://cgit.drupalcode.org/drupal/tree/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php

You would have to use a higher priority, so that your code will run first.



In most cases it might be easier to configure a url pointing to a custom controller here:

/admin/config/system/site-information


BTW the example code above is how core implements this configuration.