1

I have gone through different posts related to my problem statement but non of them seem to answer my problem and maybe this helps others too.

Problem Statement:

I am trying to setup subdomain based user spaces where foo.myportal.com will point to user foo space and bar.myportal.com will point to user bar space.

I don't want to use `.htaccess' as my application technology stack consists on Nginx, Angular, NodeJS.

Where Nginx points to Angular Application and NodeJS API services (consumed by the Angular App).

Approach "A":

enter image description here

In the example, we are relying on a single domain and would need to manage Nginx rules to address both of the requirements which is a tricky part. And in this regard will the following rules will work?

server{
    listen 80 default_server; 
    server_name _;
    return 301 https://$host$request_uri;
}

server{
    listen 443 default_server; 
    server_name myportal.com;
    ...
    # SSL Certificate configurtion here ...
    ...
}

##############################################
# Handling account specific subdomain mapping

server{
    listen 80;
    server_name *.myportal.com;
    return 301 https://$host$request_uri;
    
}

server{
    listen 443 default_server; 
    
    server_name ~^(?<acc_id>.+)\.myportal\.com$
    
    #...
    # SSL Certificate conf.
    #...
    
    location / {                                # <=== Is this approach fine?
      if($cc_id = "identity"){
           proxy_pass http://127.0.0.1:8000;
      }
      if($acc_id = "api_services"{
           proxy_pass http://127.0.0.1:9000;
      }
      rewrite ^/$ https://myportal.com/$acc_id;  # <=== Will this fallback work at the end?
    }
    
}

Approach "B":

enter image description here

In this scenario we are using multiple domains just in case the approach:B fails to work. Really looking forward to hear experts feedback.

Or would you recommend to use NodeJS to handle subdomains? as in https://www.npmjs.com/package/express-subdomain

I'm setting up whole of this on AWS.

Nah
  • 1,690
  • 2
  • 26
  • 46

1 Answers1

1

EDIT

After a comment, Nah explained better his needs. So you want to proxy the request from nginx to a service also to handle the Angular App. In this case what you want to do is to receive a request like: user1.myportal.com and to proxy it to a service in the form http://<IP>:<PORT>/user1. To achieve this, I'm going to modify my "Solution B" with the right configuration:

 http {
   # Handle angular Single page Application.                                                                                                                                                                                                                                                                                                                          
   server {                                                                                                                                                                                                                                                                                                                         
     listen 80;                                                                                                                                                                                                                                                                                                                   
     server_name ~^(?<user_id>.+)\.myportal\.com$;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
     location / {                                                                                                                                                                                                                                                                                                                     
       proxy_pass http://12.12.12.12:3000/$user_id$request_uri                                                                                                                                                                                                                                                             
     }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
   }
   
   
   # handle api.myportal.com and identity.myportal.com                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
   server {                                                                                                                                                                                                                                                                                                                         
     listen 80;                                                                                                                                                                                                                                                                                                                   
     server_name  api.myportal.com;
     location / {
       proxy_pass http://127.0.0.1:9000;
     }
   }

   server {                                                                                                                                                                                                                                                                                                                         
     listen 80;                                                                                                                                                                                                                                                                                                                   
     server_name  identity.myportal.com;
     location / {
       proxy_pass http://127.0.0.1:8000;
     }
   }
 } 

As you can see you should use the user_id variable from the server_name regex to build the path for the proxy_pass directive.
The $request_uri variable is used to handle all paths with params. So that URLs like user1.myportal.com/path/to/something?key=value are proxied to 12.12.12.12:3000/user1/path/to/something?key=value.

The above example is working because the proxy_pass directive is used with an IP address and NGINX can resolve it. If you want to use an hostname you have to declare a resolver.


Original answer

You are almost there. What I don't see it's a proper configuration to serve (or proxy) the angular application (that is just made by static assets).

Also, you don't need to use odd if statements. It's better to define all subdomains in their own server section. Nginx will give precedence to server_names with the "exact name", and then wildcard and regex. (here you can find more info about this).

Finally, I did not understand if you want that users just do the first access to their space with a custom subdomain like user1.myportal.com and then they are redirected to myportal.com/user1 or if you want that they continue their navigation through the original url they used (user1.myportal.com). I would go for the second approach because it's more versatile and maybe less odd from a user point of view.
I imagine that with both approach, the angular application logic must be ready to know that the username is after the host at the beggining of the path (myportal.com/user1) OR in the subdomain (user1.myportal.com).

My solutions do not require a secondary domain for your services. You can use them if you want but it's not needed, it's only about choices and there is not a better way to go for this.

Solution A ->

Angular app is matched with user1.myportal.com and is then redirected to myportal.com/user1.

 http {
   # Handle angular Single page Application.                                                                                                                                                                                                                                                                                                                          
   server {                                                                                                                                                                                                                                                                                                                         
     listen 80;                                                                                                                                                                                                                                                                                                                   
     server_name myportal.com;                                                                                                                                                                                                                                                                                                    
     root /var/www/html;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
     index index.html;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
     location / {                                                                                                                                                                                                                                                                                                                     
       try_files $uri $uri/ $uri.html /index.html;
       # if you want here you can proxy pass to any service that is serving your angular static assets instead of using the try files directive.                                                                                                                                                                                                                                                                              
     }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
   }
   
   # Redirect user1.myportal.com like domains to myportal.com/user1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
   server {                                                                                                                                                                                                                                                                                                                         
     listen 80;                                                                                                                                                                                                                                                                                                                   
     server_name  ~^(?<user_id>.+)\.myportal\.com$;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
     location / {                                                                                                                                                                                                                                                                                                                     
       return 301 http://myportal.com/$user_id;                                                                                                                                                                                                                                                                                 
     }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
   }
   # handle api.myportal.com and identity.myportal.com                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
   server {                                                                                                                                                                                                                                                                                                                         
     listen 80;                                                                                                                                                                                                                                                                                                                   
     server_name  api.myportal.com;
     location / {
       proxy_pass http://127.0.0.1:9000;
     }
   }

   server {                                                                                                                                                                                                                                                                                                                         
     listen 80;                                                                                                                                                                                                                                                                                                                   
     server_name  identity.myportal.com;
     location / {
       proxy_pass http://127.0.0.1:8000;
     }
   }
 }              

This approach could work but like I told you I find it a bit odd, users will be redirected to a new page. Also, you can have problem handling paths. I imagine you want an URL like user1.myportal.com/pictures should be redirected to user1.myportal.com/user1/pictures. To handle this, you should change the return 301 http://myportal.com/$user_id; to something like return 301 http://myportal.com/$user_id$request_uri;.

Solution B ->

Angular app is matched with user1.myportal.com. No redirect are involved and user continue their navigation with the original url

 http {
   # Handle angular Single page Application.                                                                                                                                                                                                                                                                                                                          
   server {                                                                                                                                                                                                                                                                                                                         
     listen 80;                                                                                                                                                                                                                                                                                                                   
     server_name ~^(?<user_id>.+)\.myportal\.com$;                                                                                                                                                                                                                                                                                                    
     root /var/www/html;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
     index index.html;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
     location / {                                                                                                                                                                                                                                                                                                                     
       try_files $uri $uri/ $uri.html /index.html;
       # if you want here you can proxy pass to any service that is serving your angular static assets instead of using the try files directive.                                                                                                                                                                                                                                                                              
     }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
   }
   
   
   # handle api.myportal.com and identity.myportal.com                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
   server {                                                                                                                                                                                                                                                                                                                         
     listen 80;                                                                                                                                                                                                                                                                                                                   
     server_name  api.myportal.com;
     location / {
       proxy_pass http://127.0.0.1:9000;
     }
   }

   server {                                                                                                                                                                                                                                                                                                                         
     listen 80;                                                                                                                                                                                                                                                                                                                   
     server_name  identity.myportal.com;
     location / {
       proxy_pass http://127.0.0.1:8000;
     }
   }
 }   

As you can see the second solution is not using any redirect. I would go with solution B, it's cleaner and convenient for users. Also you don't have to handle the "path problem". Urls like user1.myportal.com/pictures will just work. Again: I assume you can control the logic (and the router) of the Angular Single Page Application and you know that for Single Page Apps you have to serve always the index.html file, for each path. Then, the client side logic is responsable of routing the user to the right view according to the URL path, and if needed to the url subdomains too.

Both my solutions just shows the basic configuration needed. SSL, https to http redirect and fine tuning params are not included to give you a concise but usefull prototype.

radar155
  • 1,796
  • 2
  • 11
  • 28
  • Thanks for sharing detailed feedback. Just to clarify confusion, I am not redirecting users from their own subdomain space i.e. `user1.myportal.com` to `myportal.com/user1` I was just thinking of URL `rewrite` approach through which I can pass arguments in the URL to the end service. But now I feel I was thinking it like PHP/.htaccess way which would not work in Static Angular App case. So, you are probably right. – Nah Nov 14 '22 at 18:13
  • Ok, I understand your needs. I edited my answer, check it. – radar155 Nov 14 '22 at 21:04