Search code examples
url-rewritingapache2reverse-proxysymfony5

Symfony 5.3 behind Apache2 proxy has unexpected behavior (incorrect url in browser with ProxyPass/ProxyPassReverse)


I want to run a Symfony 5.3 application behind an Apache reverse proxy, but it doesn't work as expected

I have many web project Apache servers behind the Apache reverse proxy without any problems, but this one defies me :-/

Scenario:

Setup Browser Proxy-Server App-Server
Setup: http://host/app1/login ProxyPass /app1/ http://192.168.1.1/
ProxyPassReverse /app1/ http://192.168.1.1/
http://192.168.1.1/login

What i expect the setup to do:

Flow Browser Proxy-Server App-Server
Browser > Proxy > App-Server Browser requests url
(e.g. post credentials)
/app1/login
Proxy maps request to app
/login
Server does stuff in /login controller
(and redirects to /success see next figure)
Flow App-Server Proxy-Server Browser
App-Server > Proxy > Browser App-Server sends
/success redirect
Proxy maps response to
/app1/success
Receives redirect pointing to /app1/success

What happens in real life:

Flow Browser Proxy-Server App-Server
Browser > Proxy > App-Server Browser requests url
(e.g. post credentials)
/app1/login
Proxy maps request to app
/login
Server does stuff in /login controller
(and redirects to /success see next figure)
Flow App-Server Proxy-Server Browser
App-Server > Proxy > Browser App-Server sends
/success redirect
### ERROR ###
### ERROR ###
### ERROR ###
Proxy somehow NOT rewriting to
/app1/success but instead goes for /success
Receives redirect pointing to /success
and calls http://host/success
and goes 404 (or worse stuff)

What i WANT

Browser: Hey i post credentials to /app1/login
Proxy: Uhh, one moment.. that has to be /login - otherwise Server does not know
Server: Yo, /login seems good, go to /success, oh and load /this/css.file
Proxy: Err, just a second.. that has to be /app1/success aaand /app1/this/css.file - otherwise Browser get's confused
Browser: Yay, ill make my way to /app1/success
Browser: Whoa, what a nice design in /app1/this/css.file

Everybody happy!

To make things worse

  • I only have power over to the network/server stuff, not the Symfony application itself
  • So, i can't change the App code/configuration itself, i just have to "host" this thing

I've read (literally) hundreds of Stack posts concerning this/similar problem.

I've read "all" the documentation there is with Apache, ModProxy, Symfony, and spent days on Google.

My hope to get this running is quite low now, and my brain hurts.

Questions

  • What additional information do you need to approach my question
  • What - on earth - (probably obvious hiding in plain sight) bit of config stuff am i missing??
  • Even if solution would be inside the Symfony application and a maintainer has to fix it (< this makes it a valid stackoverflow question by the way ;) )

Additional information

Server installed:

  • Ubuntu 20.04
  • Apache 2.4
  • Php 7.4 (FPM)

Server/App configs:

  • vHost Proxy-Server (the relevant parts)
ProxyRequests Off
ProxyVia Off
ProxyPreserveHost On
ProxyTimeout 1200
ProxyReceiveBufferSize 4096

<VirtualHost *:80>

ProxyPass /app1/ http://192.168.1.1/
ProxyPassReverse /app1/ http://192.168.1.1/

</VirtualHost>
  • vHost App-Server (the relevant parts)
<VirtualHost *:80>

        DocumentRoot /var/www/public

        <Directory /var/www/public>
                AllowOverride All
        </Directory>

</VirtualHost>
  • htaccess App-Server (rewrite parts)
    # Determine the RewriteBase automatically and set it as environment variable.
    # If you are using Apache aliases to do mass virtual hosting or installed the
    # project in a subdirectory, the base path will be prepended to allow proper
    # resolution of the index.php file and to redirect to the correct URI. It will
    # work in environments without path prefix as well, providing a safe, one-size
    # fits all solution. But as you do not need it in this case, you can comment
    # the following 2 lines to eliminate the overhead.
    RewriteCond %{REQUEST_URI}::$0 ^(/.+)/(.*)::\2$
    RewriteRule .* - [E=BASE:%1]

    # Sets the HTTP_AUTHORIZATION header removed by Apache
    RewriteCond %{HTTP:Authorization} .+
    RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]

    # Redirect to URI without front controller to prevent duplicate content
    # (with and without `/index.php`). Only do this redirect on the initial
    # rewrite by Apache and not on subsequent cycles. Otherwise we would get an
    # endless redirect loop (request -> rewrite to front controller ->
    # redirect -> request -> ...).
    # So in case you get a "too many redirects" error or you always get redirected
    # to the start page because your Apache does not expose the REDIRECT_STATUS
    # environment variable, you have 2 choices:
    # - disable this feature by commenting the following 2 lines or
    # - use Apache >= 2.3.9 and replace all L flags by END flags and remove the
    #   following RewriteCond (best solution)
    RewriteCond %{ENV:REDIRECT_STATUS} =""
    RewriteRule ^index\.php(?:/(.*)|$) %{ENV:BASE}/$1 [R=301,L]

    # If the requested filename exists, simply serve it.
    # We only want to let Apache serve files and not directories.
    # Rewrite all other queries to the front controller.
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ %{ENV:BASE}/index.php [L]

Solution

  • I Finally got it working.

    Proxy configuration changed from this:

    <VirtualHost *:80>
    
        ProxyPass /app1/ http://192.168.1.1/
        ProxyPassReverse /app1/ http://192.168.1.1/
    
    </VirtualHost>
    

    to this:

    <VirtualHost *:80>
    
        ProxyPass /app1/ http://192.168.1.1/app1/
        ProxyPassReverse /app1/ http://192.168.1.1/app1/
    
    </VirtualHost>
    

    Now it points to /var/www/public/app1/ directory on the app server, which does not exist.

    So.. on the app server i've changed this:

    <VirtualHost *:80>
    
        DocumentRoot /var/www/public
    
        <Directory /var/www/public>
                AllowOverride All
        </Directory>
    
    </VirtualHost>
    

    to this:

    <VirtualHost *:80>
    
        DocumentRoot /var/www/public
    
        Alias /app1 /var/www/public
    
        <Directory /var/www/public>
                AllowOverride All
        </Directory>
    
    </VirtualHost>
    

    .. and DING .. it's working.

    There was some additional app troubleshooting necessary because of the used webpack configuration. There had to be a change to integrate the /app1/ prefix in the manifest. To get correct style/content urls at build. ;)

    e.g. using Symfony Encore

    from this:

    // directory where compiled assets will be stored
    .setOutputPath('public/build/')
    // public path used by the web server to access the output path
    .setPublicPath('/build')
    // only needed for CDN's or sub-directory deploy
    //.setManifestKeyPrefix('build/')
    

    to this:

    // directory where compiled assets will be stored
    .setOutputPath('public/build/')
    // public path used by the web server to access the output path
    .setPublicPath('/app1/build')
    // only needed for CDN's or sub-directory deploy
    .setManifestKeyPrefix('build/')
    

    Thank's for reading :)

    If there is anything i should add for better understanding, please comment ;)