Cors Listener in Symfony backend
When working with a frontend and backend on different domains I run into issues with the cors headers. Here's a way to solve them for a Symfony backend.
A setup might look like the following:
- www.awesomeproject.tld (Your Website - Frontend)
- app.awesomeproject.tld (Your API - Backend)
Frontend requests will be blocked by CORS. You can add them for every request as described here. But if you wan't to enable this for your whole API there is a better way with the use of a listener:
<?php
declare(strict_types=1);
namespace App\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
final class CorsListener implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => ['onKernelRequest', 9999],
KernelEvents::RESPONSE => ['onKernelResponse', 9999],
KernelEvents::EXCEPTION => ['onKernelException', 9999],
];
}
public function onKernelException(ExceptionEvent $event): void
{
$response = $event->getResponse();
if ($response) {
$response->headers->set('Access-Control-Allow-Origin', '*');
$response->headers->set('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH');
$response->headers->set('Access-Control-Allow-Headers', 'content-type');
}
}
public function onKernelRequest(RequestEvent $event): void
{
// Don't do anything if it's not the master request.
if (!$event->isMasterRequest()) {
return;
}
$request = $event->getRequest();
$method = $request->getRealMethod();
if (Request::METHOD_OPTIONS === $method) {
$response = new Response();
$event->setResponse($response);
}
}
public function onKernelResponse(ResponseEvent $event): void
{
// Don't do anything if it's not the master request.
if (!$event->isMasterRequest()) {
return;
}
$response = $event->getResponse();
if ($response) {
$response->headers->set('Access-Control-Allow-Origin', '*');
$response->headers->set('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH');
$response->headers->set('Access-Control-Allow-Headers', 'content-type');
}
}
}
Then you just need to tag it in your services.yml
App\EventListener\CorsListener:
tags:
- { name: kernel.event_subscriber }
This way you get an empty response for all OPTIONS
calls and a CORS header that allows the external usage. Instead of *
provided on Access-Control-Allow-Origin
you can (and should) provide your domain of the frontend.