Deploy a Mojolicious::Lite app under relative path with Apache ReverseProxy

279 views
Skip to first unread message

Martin Cernicka

unread,
Dec 18, 2016, 6:00:59 AM12/18/16
to Mojolicious
Hello,

I am struggling with the deployment of a simple Mojolicious::Lite application under Apache ReverseProxy with a relative path. The application base URL looks like this: https://localhost/test. It works fine without /test at the end.

The generated URLs do look fine, but the routes never match. They do match, however, if the path is entered twice, like https://localhost/test/test/show_form.

This is my setup on a Debian 8 Jessie:

morbo running on http://localhost:3001, works fine when contacted directly.
Application URL: https://localhost/test

Apache configuration:
ProxyPreserveHost On
ProxyPass /test http://localhost:3001/
ProxyPassReverse /test http://localhost:3001/
RequestHeader set X-Forwarded-HTTPS "1"

The application:
#!/usr/bin/env perl
use Mojolicious::Lite;
use Data::Dumper;

hook before_dispatch
=> sub {
   
my $c = shift;
   
if ( $c->req->headers->header('X-Forwarded-Host') ) {
        $c
->req->url->base->scheme('https');
        $c
->req->url->base->path('test');
   
}
};

get '/' => sub {
   
my $c = shift;
    $c
->stash( debug => 'url:' . Dumper( $c->req->url ) );
} => 'index';

get 'show_form' => 'menu';

get 'save_form' => sub {
   
my $c = shift;
    $c
->stash( input => $c->param('input') );
} => 'show_saved';

app
->start;
__DATA__

@@ index.html.ep
% layout 'default';
<p><%= link_to('Form' => 'show_form') %>: leads nowhere                                                     <br><a href="/test/test/show_form">Form manual link</a>: this works under apache ReverseProxy, but obviously not without
<p><%= link_to('Home' => '/') %>
<pre><%= stash '
debug' %></pre>

@@ layouts/default.html.ep
<!DOCTYPE html>
<html>
  <head><title><%= title %></title></head>
  <body><%= content %></body>
</html>

@@ menu.html.ep
% layout '
default';
This is a test form
%= form_for save_form => (method => '
GET') => begin
        <br>Desc <input type="text" name="input">
        <br><input type="submit" value="Save">
% end

@@ show_saved.html.ep
% layout '
default';
You entered: desc="<%= stash '
input' %>".
<br>Return <%= link_to '
home' => '/' %>.


And the contents of $c->req->url, as displayed on the first page:

url:$VAR1 = bless( {
                 'path' => bless( {
                                    'charset' => 'UTF-8',
                                    'path' => '/'
                                  }, 'Mojo::Path' ),
                 'query' => bless( {
                                     'pairs' => []
                                   }, 'Mojo::Parameters' ),
                 'base' => bless( {
                                    'scheme' => 'https',
                                    'path' => bless( {
                                                       'charset' => 'UTF-8',
                                                       'leading_slash' => '',
                                                       'trailing_slash' => '',
                                                       'parts' => [
                                                                    'test'
                                                                  ]
                                                     }, 'Mojo::Path' ),
                                    'host' => '127.0.0.1'
                                  }, 'Mojo::URL' )
               }, 'Mojo::URL' );

As far as I understood the Cookbook#Rewriting (not very far, it seems), moving the req->url->path->leading_slash(0) to req->url->base->path->trailing_slash(1) is the way to go. But it is empty in my case and doesn't help. Adding a trailing slash to the URL makes the path appear twice in the links (https://localhost/test/test/show_form) and it matches nicely, e.g. the form appears after clicking on the link. But the form_for URL is correct/wrong again (test/save_form, won't match the route 'save_form').

Yet elsewhere Sebastian suggests to move a chunk from path->parts to base->path->parts. Empty here as well, no matter if the URL contains a trailing slash or not.

I tried hypnotoad instead of morbo, production instead of development, adding slashes here and there, but still can't seem to figure it out.

Paul Williams

unread,
Dec 19, 2016, 3:21:23 AM12/19/16
to Mojolicious

Martin Cernicka

unread,
Dec 23, 2016, 2:27:31 PM12/23/16
to Mojolicious

On Monday, December 19, 2016 at 9:21:23 AM UTC+1, Paul Williams wrote:
 Thanks, Paul, that did it. This solution also lurks in the t/ directory in the Mojolicious source.

For reference, here is the code I used and the corresponding Apache configuration. Yes, the slashes matter:

# change the base path, if deployed under a reverse proxy, e.g. with Apache:
#   ProxyRequests Off
#   ProxyPreserveHost Off
#   ProxyPass /app/ http://localhost:8081/
#   ProxyPassReverse /app/ http://localhost:8081/
#   RequestHeader set X-Request-Base https://host.domain/app
#
app
->hook(

    before_dispatch
=> sub {
       
my $c = shift;

       
if ( my $base = $c->req->headers->header('X-Request-Base') ) {
            $c
->req->url->base( Mojo::URL->new($base) );
       
}
   
}
);

The application url - note the slash at the end: https://host.domain/app/

Strangely enough the url_for('/') won't add the slash at the end. So in the route '/' I just redirect to '/home', which works.

Cheers

Reply all
Reply to author
Forward
0 new messages