Search code examples
phpapachenginxreverse-proxynette

Nette behind a reverse-proxied alias on nginx


I have a Nette application running on an Apache2/Debian 11 server that works fine. However, we need to hide it behind an nginx proxy with an alias.

Let's say we have a perfectly fine Apache2 environment running on http://127.0.0.1:89/, and we want to access it through a nginx set up as a reverse proxy with address https://example.com/app/

nginx is set up as follows:

  location /app/ {
    proxy_pass http://127.0.0.1:89;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Proto "https";
    proxy_set_header X-Forwarded-Port "443";
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }

While Apache2 configuration remains intact from before the proxy attempt:

<VirtualHost *:89>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/app/www/
        <Directory /var/www/app/www/>
                Options Indexes FollowSymLinks
                AllowOverride All
                Require all granted
        </Directory>
</VirtualHost>

The problem is, that the Nette application still thinks that it runs on a path without the "/app" URI part, and all links generated through the redirect and link calls, including the $basePath template variable are therefore invalid.

I also appended the proxy info to the Nette config, and since the nginx instance runs on the same server, it looks as follows:

http:
    proxy: 127.0.0.1

I tried to set the nginx config to forward the /admin path in the forwarded URI:

proxy_pass http://127.0.0.1:89/admin;

And also tried to fiddle with the Nette router to filter out the "admin/" part (to not look for AdminPresenter, which is obviously missing):

$router->addRoute('[admin/]<presenter>/<action>[/<id>]', 'Homepage:default');

The Nette application yields this error when attempting to access the page:

TypeError: unpack() expects parameter 2 to be string, bool given in /var/www/app/vendor/nette/http/src/Http/Helpers.php:49  @  http://example.com/app/

Could somebody please point me in the right direction?


Solution

  • Well, to answer my own question, in case anybody has the same problem. I feel like my solution is a bit of a hack, so feel free to post the non-hacky answer.

    First, I modified the nginx config:

    location /app/ {
        rewrite /app/(.*) /app/$1 break;
        proxy_pass http://127.0.0.1:89/;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Proto "https";
        proxy_set_header X-Forwarded-Port "443";
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    

    Then I modified the Nette config to cover all the proxies (might not have been actually needed):

    http:
        proxy: 0.0.0.0/0
    

    I also added "app" route to the router code (not as an optional prefix, but as a regular route):

    $router = new RouteList;
    $router->addRoute('app/<presenter>/<action>[/<id>]', 'Homepage:default');
    $router->addRoute('<presenter>/<action>[/<id>]', 'Homepage:default');
    return $router;
    

    Also modified BasePresenter code startup method with:

    $this->template->basePath = '/app'.$this->template->basePath;
    

    Finally, I modified the .htaccess file to rewrite the URLs of all static resources to a path without the "app" part:

    # Default Nette htaccess contents
    RewriteEngine On
    RewriteRule /\.|^\. - [F]
    
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule !\.(pdf|js|ico|gif|jpg|png|css|rar|zip|tar\.gz)$ index.php [L]
    
    # Added this line under the default Nette htaccess file
    RewriteRule ^app(/.*|$) $1 [NC,L]
    

    The Apache vhost remains unchanged.

    This way, the nginx passes the complete request URL (including the "app") to Apache, which invokes the Nette router with the "app" prefix. Routing now works fine, as the "app" is part of the URL and is even perceived by Nette (as it actually is fully present in the request headers). This causes the $basePath and links/redirect to work.

    The static resources are, however, not served through the Nette router, thus the app/ prefix causes the Apache to not find the file and report 404. This is the reason for adding the rewrite rule to erase the app/ prefix from static resource's URL.

    It is hacky, but it works both for the proxied and non-proxied access.