Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • SensioLabs Professional services to help you with Symfony
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by
  1. Home
  2. Documentation
  3. Security
  4. How to Customize Access Denied Responses

How to Customize Access Denied Responses

Edit this page

In Symfony, you can throw an AccessDeniedException to disallow access to the user. Symfony will handle this exception and generates a response based on the authentication state:

  • If the user is not authenticated (or authenticated anonymously), an authentication entry point is used to generate a response (typically a redirect to the login page or an 401 Unauthorized response);
  • If the user is authenticated, but does not have the required permissions, a 403 Forbidden response is generated.

Customize the Unauthorized Response

You need to create a class that implements AuthenticationEntryPointInterface. This interface has one method (start()) that is called whenever an unauthenticated user tries to access a protected resource:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// src/Security/AuthenticationEntryPoint.php
namespace App\Security;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;

class AuthenticationEntryPoint implements AuthenticationEntryPointInterface
{
    public function __construct(
        private UrlGeneratorInterface $urlGenerator,
    ) {
    }

    public function start(Request $request, ?AuthenticationException $authException = null): RedirectResponse
    {
        // add a custom flash message and redirect to the login page
        $request->getSession()->getFlashBag()->add('note', 'You have to login in order to access this page.');

        return new RedirectResponse($this->urlGenerator->generate('security_login'));
    }
}

That's it if you're using the default services.yaml configuration. Otherwise, you have to register this service in the container.

Now, configure this service ID as the entry point for the firewall:

1
2
3
4
5
6
7
# config/packages/security.yaml
firewalls:
    # ...

    main:
        # ...
        entry_point: App\Security\AuthenticationEntryPoint
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- config/packages/security.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<srv:container xmlns="http://symfony.com/schema/dic/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:srv="http://symfony.com/schema/dic/services"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <config>
        <firewall name="main"
            entry-point="App\Security\AuthenticationEntryPoint"
        >
            <!-- ... -->
        </firewall>
    </config>
</srv:container>
1
2
3
4
5
6
7
8
9
10
// config/packages/security.php
use App\Security\AuthenticationEntryPoint;
use Symfony\Config\SecurityConfig;

return static function (SecurityConfig $security): void {
    $security->firewall('main')
        // ....
        ->entryPoint(AuthenticationEntryPoint::class)
    ;
};

Customize the Forbidden Response

Create a class that implements AccessDeniedHandlerInterface. This interface defines one method called handle() where you can implement whatever logic that should execute when access is denied for the current user (e.g. send a mail, log a message, or generally return a custom response):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/Security/AccessDeniedHandler.php
namespace App\Security;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;

class AccessDeniedHandler implements AccessDeniedHandlerInterface
{
    public function handle(Request $request, AccessDeniedException $accessDeniedException): ?Response
    {
        // ...

        return new Response($content, 403);
    }
}

If you're using the default services.yaml configuration, you're done! Symfony will automatically know about your new service. You can then configure it under your firewall:

1
2
3
4
5
6
7
# config/packages/security.yaml
firewalls:
    # ...

    main:
        # ...
        access_denied_handler: App\Security\AccessDeniedHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- config/packages/security.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<srv:container xmlns="http://symfony.com/schema/dic/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:srv="http://symfony.com/schema/dic/services"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <config>
        <firewall name="main"
            access-denied-handler="App\Security\AccessDeniedHandler"
        >
            <!-- ... -->
        </firewall>
    </config>
</srv:container>
1
2
3
4
5
6
7
8
9
10
// config/packages/security.php
use App\Security\AccessDeniedHandler;
use Symfony\Config\SecurityConfig;

return static function (SecurityConfig $security): void {
    $security->firewall('main')
        // ....
        ->accessDeniedHandler(AccessDeniedHandler::class)
    ;
};

Customizing All Access Denied Responses

In some cases, you might want to customize both responses or do a specific action (e.g. logging) for each AccessDeniedException. In this case, configure a kernel.exception listener:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// src/EventListener/AccessDeniedListener.php
namespace App\EventListener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;

class AccessDeniedListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            // the priority must be greater than the Security HTTP
            // ExceptionListener, to make sure it's called before
            // the default exception listener
            KernelEvents::EXCEPTION => ['onKernelException', 2],
        ];
    }

    public function onKernelException(ExceptionEvent $event): void
    {
        $exception = $event->getThrowable();
        if (!$exception instanceof AccessDeniedException) {
            return;
        }

        // ... perform some action (e.g. logging)

        // optionally set the custom response
        $event->setResponse(new Response(null, 403));

        // or stop propagation (prevents the next exception listeners from being called)
        //$event->stopPropagation();
    }
}
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version

    Symfony 7.1 is backed by

    Check Code Performance in Dev, Test, Staging & Production

    Check Code Performance in Dev, Test, Staging & Production

    Become certified from home

    Become certified from home

    Version:

    Table of Contents

    • Customize the Unauthorized Response
    • Customize the Forbidden Response
    • Customizing All Access Denied Responses

    Symfony footer

    Avatar of Vladimir Tsykun, a Symfony contributor

    Thanks Vladimir Tsykun (@vtsykun) for being a Symfony contributor

    19 commits • 319 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • What is Symfony?
      • Symfony at a Glance
      • Symfony Components
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • Symfony Community
      • SymfonyConnect
      • Events & Meetups
      • Projects using Symfony
      • Contributors
      • Symfony Jobs
      • Backers
      • Code of Conduct
      • Downloads Stats
      • Support
    • Blog

      • All Blog Posts
      • A Week of Symfony
      • Case Studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Living on the edge
      • Releases
      • Security Advisories
      • Symfony Insight
      • Twig
      • SensioLabs Blog
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Powered by

    Follow Symfony