skip to Main Content

My team’s server is set up in such as way: We have one domain name, which seems like already a subdomain of the company’s domain. We want to host multiple applications under this one domain. We’d even like to have a production version and staging version for each of the apps on the server.

Document root is an empty folder. Applications sit outside of the document root.
we are trying to use the first token in the URL path to find out which app we try to access, then somehow redirect to it (internally or externally).

Here is a structure equivalent to how the directories are organized.

/usr/local/var/www  <- Document Root
/usr/local/var/app1 <- application 1
------------------/public/index.php
------------------/public/css
/usr/local/var/app2 <- application 2
/usr/local/var/app1.stg <- application 1 staging version, code is exactly the same as application1
/usr/local/var/app2.stg <- application 2 staging version, code is exactly the same as application2

Here are the relevant settings in httpd.conf

DocumentRoot /usr/local/var/www
<Directory "/usr/local/var/www">
    AllowOverride None
    Require all granted
</Directory>
Alias "/app1" "/usr/local/var/app1"
Alias "/app2" "/usr/local/var/app2"

<VirtualHost *:80>
   # rewriting rules to make the routing work
   # There is only one vhost so it can actually be removed
</VirtualHost>

When we access https://sub.domain.com/app1, we expect to go to app1
When we access https://sub.domain.com/app1.stg, we expect to go to app1.stg

The applications are written in PHP. This server configuration means we have to include the "path to the application" in the routes and rewrite rules, and use the "full absolute path" in all the resource references.
For example, a route will look like

$router->map("GET", "/app1/action", SomeController);

A css reference will be: (even though relative path is given, it behaves just like a relative path to the DocRoot (with "/" in front). You can see it in this detailed post)

<link href="app1/public/css/style.css" type="text/css" rel="stylesheet"/>

These will be sufficient to make both apps work, but the staging version is not going to work, because it contains EXACTLY THE SAME copy of code (which is how it’s intended to be, to test out in staging environment, then push to production environment).
If I want both versions to work, I have to code the paths dynamically, namely using CONTEXT_DOCUMENT_ROOT or some other server variable to figure out which app version it’s in, and have two copies of routes, one starting with app1, the other app1.stg. I also have to have separate rewriting rule for each version.

QUESTION

With the server setup restriction applied (one domain name, distinguish apps with the way I described, etc..), is it possible to use only relative paths, write routes with respective to only the app itself? Some like:

<link href="css/style.css" type="text/css" rel="stylesheet"/>

$router->map("GET", "/action", SomeController);

In other words, I have to change the server setup within the constraints, so that the app can be written in a way without caring how the server is set up.

I know one way is to use different ports for each app/version, but apparently the server admin doesn’t like the idea.

I’ve broken down the problem into steps in this question. It’s quite long but if you are willing to follow through, it should provide much more details.


If the question is not clear enough, the per-user directory looks quite like what I want to achieve. But instead of user directory, I want the app directory in place of it. Of course I never used per-user directory so I dont know if it actually behaves the way I think it does.


So I understand that we can have multiple hostnames in /etc/hosts mapped to one IP address. Can I just use that hostname as the ServerName in apache config, and access in the browser by typing that hostname? The website is for internal usage so should only be accessed within company’s network.

In /etc/hosts:

123.45.67.89 app1.team-server-name app2.team-server-name

In httpd.conf:

<VirtualHost>
    ServerName app1.team-server-name
    DocumentRoot /usr/local/var/app1/public
</VirtualHost>
<VirtualHost>
    ServerName app2.team-server-name
    DocumentRoot /usr/local/var/app2/public
</VirtualHost>

2

Answers


  1. Chosen as BEST ANSWER

    I end up finding a solution with one compromise.

    DocumentRoot "/usr/local/var/www"
    Alias /app1 /usr/local/var/app1/public
    
    <Directory "/usr/local/var/www">
        RewriteEngine On
        RewriteRule ^/?$ /app1/ [R,L]
        RewriteRule (.*) /app1/$1 [R,L]
    </Directory>
    
    <VirtualHost *:80>
        <Directory "/usr/local/var/app1">
            Require all granted
            RewriteEngine On
            RewriteCond %{REQUEST_FILENAME} !-f
            RewriteCond %{REQUEST_FILENAME} !-d
            # calls index.php (REQUEST_URI is still the same as before)
            RewriteRule ^ index.php [L]
        </Directory>
    </VirtualHost>
    

    All relative paths can be rewritten normally. Routes and requests will need to start with app name.

    $router->map("GET", "/app1/hello2", SomeController);
    // navigation bar. URI for another tab:
    <li><a href="/app1/hello2">Hello 2</a></li>
    

    If we want to have multiple versions of the app, the main thing to do is to know which version we are in so the app can send the correct request uri. This can be done by checking the REQUEST_URI and remember which version is being called when index.php is called up. Then in the request (e.g. navigation src), append it in front of the action.

    There are different ways to do this, such as writing your html templates in php, so you can access php variables. I used twig so I can pass the value to the twig templates from php. But I still don't like to have all these stuff in my static code, so I decided to just get rid of the staging version.

    If the project is serious enough to require a staging version, then a better suitable environment should be provided.


  2. This is quite the lengthy question, thank you for providing so much detail.

    I would opt for a different approach than you are currently attempting. Instead of trying to serve each of these applications out of a folder, set up each of them as a domain based vhost. Use something like app1.local or whatever for the hostname and be sure to add the entries to your /etc/hosts file under 127.0.0.1. Make sure the listen directive for these vhosts is on the loopback (127.0.0.1:80). Each of these apps should function as if they were installed at the document root of their own server. All the CSS should assume its at ‘css/style.css’ relative to /.

    Now that you have all of the apps setup on the loopback, you can setup a reverse proxy from the vhost listening on the public interface to proxy all of the application locations to their appropriate loopback vhost after you remove the /app1 prefix from the request.

    I haven’t used Apache 2.x for a very long time, but the concepts are the same as nginx.

    location  /foo {
      rewrite /foo/(.*) /$1  break;
      proxy_pass         http://app1.local;
      proxy_redirect     off;
      proxy_set_header   Host $host;
    }
    

    The biggest issue with this approach is that the applications that are being proxied either need to use relative paths everywhere, or they need to have some kind of configurable prefix that is prepended to the urls. Most frameworks will support the prefix option. For Example: https://laravel.com/docs/5.6/urls This prefix can be used for asset (css/js/jpg) loading as well, but only from files that execute PHP.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search