How to setup NGINX to deploy different Single Page Apps (SPA's... i.e static files) depending on location (under same server_name) with subroutes

qx3 picture qx3 · Jul 20, 2015 · Viewed 19.5k times · Source

My goal is to setup two different single page apps (SPA) under the same domain, where we show the SPA corresponding to the location / path requested. I also want to default to one of the two SPA's on / location. And.. I want the html5 history location paths appended by the SPA to actually route to the right place if someone enters the url in the browser.

It is easier to explain with an example.

Example:

User navs to mydomain.com/app and the server serves contents under /home/user/app/dist (which has an index.html and all the js/css assets) (Using linux so /home/user is just my home directory path).

User navs to mydomain.com/auth and the server serves contents under /home/user/auth/dist

User navs to / and the server serves contents under /home/user/auth/dist ( / navs to auth by default)

User navs to mydomain.com/auth/login and the server, yet again, serves the contents under /home/user/auth/dist folder but the url stays mydomain.com/auth/login so that the auth SPA can use as route

User navs to mydomain.com/auth/signup and the server, yet again, serves the contents under /home/user/auth/dist folder again, the url stays mydomain.com/auth/login so that the auth SPA can use as route

user navs to mydomain.com/app/home and the server serves contents under /home/user/app/dist

I have tried root/alias/rewrite/regex/=/^~ rules. I am trying to get a more in depth understanding on nginx setup but in the meantime, this is what I currently have:

server {
listen [::]:80 default_server;
listen 80;
server_name mydomain.com 127.0.0.1;    # Make it serve in mydomain.com

# ^~ rules stop matching after the exact match 
location ^~ /app {                     # if location start matches /app
  alias /home/user/app/dist;
  index index.html;
  try_files $uri $uri/ index.html =404;
}                                      # if location start matches app/lobby
location ^~ /app/home {
  alias /home/user/app/dist;
  index index.html;
  try_files $uri $uri/ index.html =404;
}

# you can see that I need to add a new one per each client js app route
location ^~ /app/home/visitor {
  alias /home/user/app/dist;
  index index.html;
  try_files $uri $uri/ index.html =404;
}

location ^~ /auth/login {
  alias /home/user/auth/dist;
  index index.html;
  try_files $uri $uri/ index.html =404;
}
location ^~ /auth {
  alias /home/user/auth/dist;
  index index.html;
  try_files $uri $uri/ index.html =404;
}

# Rewrites / to auth, appending whatever params to path
#   var (for the client to consume)for now
location / {
  rewrite ^/(.*) /auth?path=$1 last;
}

}

}

It should find an index.html under the /dist folder

I would like to use either regular expressions or a location match that lets the rest of the match through for the client js app to catch, so that I don't have to add a new rule per each route(this app actually has nested state routes). I know that the location ^~ modifier stops after matching the specific rule, but I wasnt able to get it working any other way. If I dont use use a modifier, or a regular expresion location match, or the = modifier... I just get 404, 403 and 500 responses from nginx.

Also, if I stop using alias/rewrite and go with the root keyword, it tries to find /app or /auth actual folders under the dist folder, which it really shouldn't (like if /home/user/auth/dist/auth folder existed).

I also have the client know what the base directory for each SPA is, for example basedir ="app" (for the app one) and basedir="auth" for the auth one.

I think Im doing the rewrite wrong, or I need more rewrites, or additional rules to make things more generic.

How do I go about doing this? Some kind of sample configuration would be appreciated. Thanks.

P.s. I am using Ember with Ember-cli if anybody is curious. This generates the dist/ folder with a built app and also can allow routes such as http://www.mydomain/app/home/visitor/comments/new which is why it wouldn't make sense to hardcode each path (and the app is still in development, more routes to come!).

EDIT

I also tried this, and I just get 404 in both /app and /auth paths:

server {
listen [::]:80 default_server;
listen 80;
server_name localhost mydomain.com 127.0.0.1;

index index.html;

location /app {
  root /home/user/app/dist;
  try_files $uri $uri/index.html index.html;
}

location /auth {
  root /home/user/auth/dist;
  try_files $uri $uri/index.html index.html;
}

}

Answer

Hevlastka picture Hevlastka · Jul 20, 2015

Before anything, make sure that you don't have a default configuration in sites-enabled this sometimes may lead to unexpected behaviour.

EDIT: Final configuration below

server {
  listen [::]:80 default_server;
  listen 80;
  server_name localhost mydomain 127.0.0.1;

  location /app {
    alias /home/user/app/dist/;
    index index.html;
    try_files $uri $uri/ index.html =404;
  }

  location /auth {
    alias /home/user/auth/dist/;
    index index.html;
    try_files $uri $uri/ index.html =404;
  }

  location / {
    rewrite ^/(.*) /auth?$1 last;
  }
}

It's also worth checking out this question and the nginx docs which explain a bit more the differences between root and alias.