Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running Symfony 5 with reverse proxy in subdirectory

Tags:

php

symfony

I like to run a Symfony 5 application behind a reverse proxy which supplies the following endpoint:

https://my.domain/service1/

The proxy config basically is this:

ProxyPass /marketsy/ http://internal.service1/

On the server the reverse proxy is connecting to, I use the following apache rule for serving my Symfony application:

<VirtualHost *:80>
  ServerName internal.service1
  DocumentRoot /webroot/service1/public

 <FilesMatch \.php$>
     SetHandler proxy:unix:/run/php/php7.2-fpm-ui.sock|fcgi://localhost
     SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
     SetEnv HTTP_X_FORWARDED_PROTO "https"
 </FilesMatch>

 <Directory  /webroot/service1/public>
     AllowOverride None
     Require all granted
     FallbackResource /index.php
 </Directory>

 <Directory  /webroot/service1/public/bundles>
     FallbackResource disabled
 </Directory>
</VirtualHost>

The application itself is reqachable but Symfony can't hanle the "service1" path prefix.

For example it tries to access the profiler under https://my.domain/_wdt/8e3926 instead of https://my.domain/service1/_wdt/8e3926 and beside the root route all the routing isn't working:

For example: When I try to access https://my.domain/service1/my/page i will get redirected to https://my.domain/my/page

Now my question is, how can I configure Symfony to know about the "service1" path prefix when generation urls ets.

like image 443
Johni Avatar asked Aug 21 '20 09:08

Johni


2 Answers

Proper way of doing it (example):

Create src/Controller/BarController.php

<?php

namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;

class BarController
{
    public function index()
    {
        return new Response('<p>Bar controler response</p>');
    }
}

and src/Controller/FooController.php

<?php

namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;

class FooController
{
    public function index()
    {
        return new Response('<p>Foo controler response</p>');
    }
}

Create config/routes/prefix-routes.yaml

index:
    path: /
    controller: App\Controller\DefaultController::index

bar:
    path: /bar
    controller: App\Controller\BarController::index
 
foo:
    path: /foo
    controller: App\Controller\FooController::index
 

and edit routing config/routes.yaml - delete its contents and just put:

prefixed:
   resource: "routes/prefix-routes.yaml"
   prefix: service1

all of controllers are now available at urls:

http://localhost/service1/ for DefaultController.php
http://localhost/service1/bar for BarController.php
http://localhost/service1/foo for FooController.php

If you want your profiler to work with service1 prefix as well then edit config/routes/dev/web_profiler.yaml this way:

web_profiler_wdt:
    resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
    prefix: service1/_wdt

web_profiler_profiler:
    resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
    prefix: service1/_profiler

now they should be available at:

http://localhost/service1/_wdt... for wdt
http://localhost/service1/_profiler for profiler

Adding prefix for annotations:

Create controller src/Controller/AnnoController.php:

<?php

namespace App\Controller;

use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class AnnoController extends AbstractController
{
    /**
     * @Route("/anno", name="anno")
     */
    public function index()
    {
        return new Response('<p>Anno controler response</p>');
    }
}

edit config/routes/annotations.yaml and add prefix: service1:

controllers:
    resource: ../../src/Controller/
    type: annotation
    prefix: service1

kernel:
    resource: ../../src/Kernel.php
    type: annotation

Now prefix is added to routes done via annotation:

http://localhost/service1/anno for AnnoController.php

Some references:

Symfony Routing Prefix
Symfony Routing Configuration Keys

Adding prefix quick and dirty workaround to add prefix service1 to all of routing (not recommended).

Instead of changing routing as above just Edit src/Kernel.php protected function configureRoutes

and change every $routes->import line by adding ->prefix('service1') at the end so it looks this way:

protected function configureRoutes(RoutingConfigurator $routes): void
{
    $routes->import('../config/{routes}/'.$this->environment.'/*.yaml')->prefix('service1');
    $routes->import('../config/{routes}/*.yaml')->prefix('service1');

    if (is_file(\dirname(__DIR__).'/config/routes.yaml')) {

        $routes->import('../config/{routes}.yaml')->prefix('service1');

    } elseif (is_file($path = \dirname(__DIR__).'/config/routes.php')) {
        (require $path)($routes->withPath($path), $this);
    }
}

all of controllers are now available at urls:

http://localhost/service1/ for DefaultController.php
http://localhost/service1/bar for BarController.php
http://localhost/service1/foo for FooController.php

as well as the profiler:

http://localhost/service1/_wdt... for wdt
http://localhost/service1/_profiler for profiler
like image 127
Jimmix Avatar answered Sep 24 '22 00:09

Jimmix


In addition to the solution @Jimmix provided, I had to write a subscriber to add a prefix to my request path:

<?php namespace My\Bundle\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class AppPrefixSubscriber implements EventSubscriberInterface {

    /** @var string */
    private $appPrefix;

    public function __construct(?string $appPrefix) {
        $this->appPrefix = $appPrefix;
    }

    /**
     * Returns events to subscribe to
     *
     * @return array
     */
    public static function getSubscribedEvents() {
        return [
            KernelEvents::REQUEST => [
                ['onKernelRequest', 3000]
            ]
        ];
    }

    /**
     * Adds base url to request based on environment var
     *
     * @param RequestEvent $event
     */
    public function onKernelRequest(RequestEvent $event) {
        if (!$event->isMasterRequest()) {
            return;
        }

        if ($this->appPrefix) {
            $request = $event->getRequest();

            $newUri =
                $this->appPrefix .
                $request->server->get('REQUEST_URI');

            $event->getRequest()->server->set('REQUEST_URI', $newUri);
            $request->initialize(
                $request->query->all(),
                $request->request->all(),
                $request->attributes->all(),
                $request->cookies->all(),
                $request->files->all(),
                $request->server->all(),
                $request->getContent()
            );
        }
    }

}
like image 28
Johni Avatar answered Sep 23 '22 00:09

Johni