Nginx reverse proxy pathname clash - node.js

I am currently running two webservers on the same machine, one using Django through Gunicorn, which is my original site, the other which acts as the online shop using the same domain, this one using Nestjs (Nodejs).
Both servers have an /admin path with the original being at example.com/admin and the other I am wanting to be at example.com/store/admin. However whenever I enter the second URL into my browser (i.e example.com/store/admin) it returns the other admin page, example.com/admin (without the /store prefix).
Here is the config snippet I believe needs reworking:
server {
server_name example.com www.example.com;
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
location /store {
proxy_pass http://127.0.0.1:3000/;
}
location = /store/admin {
proxy_pass http://127.0.0.1:3000/admin/;
}
...
}
I have tried a fair few combinations of the /store and /store/admin location blocks but just can't seem to get it to direct me to the store's server admin site. It works on my local development machine when testing using the nodejs server. Going to http://example.com/store returns what I expect to see from the Nestjs server.

The docs states that:
... To find location matching a given request, nginx first checks locations defined using the prefix strings (prefix locations). Among them, the location with the longest matching prefix is selected and remembered. ...
So you need only two location blocks:
The first :
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
And the other with the rewrite:
location /store {
rewrite ^/store(.*) $1 break;
proxy_pass http://127.0.0.1:3000;
}
This means it will rewrite every URL starts with /store and remove it from the URL before passing to the upstream. and this also includes the /admin, since it is the same.
Also note there is no suffix / at the end of the proxy_pass - which instruct NGINX to take the user supplied URI.

Related

NGINX: Proxy only upstream calls

I hope this question is not too simple to include here. Any advice would be appreciated.
I have a situation where I am using a single url to host multiple different angular projects on nginx. I use path extensions to differentiate between the sites. It looks something like this:
server {
listen 443 http2 ssl;
#listen [::]:80;
server_name site.local;
. . .
root /usr/share/nginx/html/. . .;
. . .
location /site1 {
alias /usr/share/nginx/html/. . .;
. . .
location /site2 {
alias /usr/share/nginx/html/ . . .;
. . .
What complicates this is that site1 and site2 share an upstream api on tomcat. When these api calls are made, however, "site1" or "site2" gets inserted into the call.
I'm quite ignorant as to how proxying works on nginx, so attempting to use a proxy_path in the location blocks in the simplest way results in calls to nginx (for html files etc.) are also proxied, making the site inaccessible.
Is there a way to set it up such that only upstream calls get rewrote/proxied?
Any advice would be greatly appreciated.
I'm going to make a few assumptions about your build and correct me if I am wrong. but I see two options to resolve this considering they share the same API you can make the changes in the angular build Or in NGINX. Assuming your nginx server name is example.com it contains a location for /site1 and /site2
example.com/site1
example.com/site2
Options 1 Angular
if the URL is public you can simply route calls by setting the environment.ts inside the environments folder.
export const environment = {
production: false,
apiUrl: 'https://example.com/api',
}
You may need to deploy a production environment.ts and a test environment.ts with their respective apiUrl's for each build. But assuming they share the same API all you would need is a nginx location to said upstream API
location /api/ {
proxy_pass upstream_api;
}
Option 2 NGINX
Let's assume you don't want the full API URL in the environment.ts what is the Nginx option. I don't like this method because it's not as D.R.Y and you find yourself writing two nested locations.
location /site1 {
alias /usr/share/nginx/html/. . .;
#any other settings...
#SUB LOCATION TO API
location /site1/api/ {
rewrite /site1/(.*) /$1 break;
proxy_pass upstream_api;
#any other settings...
}
}
location /site2 {
alias /usr/share/nginx/html/. . .;
#any other settings...
#SUB LOCATION TO API
location /site2/api/ {
rewrite /site2/(.*) /$1 break;
proxy_pass upstream_api;
#any other settings...
}
}
Let's break this down.
the first location /site1 will qualify and url using site1 thus your SPA application can route to any url. But when making a request to an API it will seek the 2nd qualifier in the nested location /site1/api/ we use /site1/api so that Nginx knows that /api is not outside of the location.
Lastly rewrite /site1/(.*) /$1 break; will remove the site1 and send the rest of the url upstream to resolve your problem.
Hope this helps.

Is there any way to return a plaintext response with React, containing no HTML at all?

I need to be able to return a valid JSON response with no HTML wrapping it all. I can accomplish this by specifying a port number pointing directly to my Express backend but need to be able to return pure JSON without having to specify a port number in production. Is there any way to do this if you're using React/React-Router and Express together?
This setup uses a proxy pass to work:
server {
listen 80;
listen [::]:80;
server_name example.com;
location / {
proxy_pass http://localhost:5000;
}
}
Current output: https://socialmaya.com/u/real (1)
Desired output: http://socialmaya.com:5000/actors/real (2)
So I need the output of 1 to be the exact same as 2, ensuring that another server can read it. If you click to View Page Source for both, you can see that the output is not the same.
Per Richard Smith, rewriting the URL before passing it upstream within Nginx solved my problem:
location / {
rewrite ^/u/(.*)$ /actors/$1 last;
proxy_pass http://localhost:5000;
}

Hiding Node app port numbers on one domain with nginx reverse proxy

I'm still a relative beginner in nginx and hope to get some help and clarification on something I'm working on.
So say I have 2 Node apps, app1 and app2.
I have a production server, but I'm trying to test this locally first.
Currently app1 listens on port 8000 and app2 listens on port 8001.
So currently, they are sitting at localhost:8000 and localhost:8001, and would accessed in the production server as production.example.com:8000 and production.server.com:8001.
My question is, how can I hide the port numbers and assign them to a specific URL?
I want the result to be accessible from localhost/app1 and localhost/app2, and production.example.com/app1 and production.server.com/app2 in the production server.
I don't know what I'm getting wrong in the nginx.conf, so I hope someone can help me on this issue. These apps all have HTML forms, so I need them to post to production.example.com/app1 or something like production.example.com/app2/download. Their CSS breaks as well due to the location of the public folder in each app, since they are only in /public/css.css, not in app2/public/css.css.
I can change all of the form actions and router gets/posts, as well as the stylesheet references by adding /app1 and /app2 respectively in the Node apps, but that feels like I'm doing something wrong, as I shouldn't change any of my router info.
Here is my nginx.conf file:
Edit: So this is what I have right now:
server {
# ...
location /app1 {
rewrite ^/app1$ / break;
rewrite ^/app1/(.*) /$1 break;
proxy_pass http://127.0.0.1:8000;
}
location /app2 {
rewrite ^/app2$ / break;
rewrite ^/app2/(.*) /$1 break;
proxy_pass http://127.0.0.1:8001;
}
}
And I still get the same issue where the node apps themselves are not using their contexts.
So, I have this same configuration with like 4-5 microservices. Here is the configuration that I use.
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location /ifttt {
rewrite ^/ifttt$ / break;
rewrite ^/ifttt/(.*) /$1 break;
proxy_pass http://127.0.0.1:8080;
}
location /actions {
rewrite ^/actions$ / break;
rewrite ^/actions/(.*) /$1 break;
proxy_pass http://127.0.0.1:5000;
}
location /events {
rewrite ^/events$ / break;
rewrite ^/events/(.*) /$1 break;
proxy_pass http://127.0.0.1:5050;
}
location / {
proxy_pass http://127.0.0.1:8081;
}
}
Hope it serves as a constructive reference. Let's take one,
location /actions {
rewrite ^/actions$ / break;
rewrite ^/actions/(.*) /$1 break;
proxy_pass http://127.0.0.1:5000;
}
So, the first rewrite matches only localhost/actions and forwards it to http://127.0.0.1:5000
The second one matches localhost/actions/<anything> and forwards it to http://127.0.0.1:5000/<anything>
I think you're missing the slash (/) before the regex match.
Edit:
Using Your comment as reference
The index is at /app1/index
a page is at /app1/index/flashfireblast
and the navbar header needs to reference /app1/stylesheets/css.css
So, To address /app1/stylesheets/css.css from /app1/index/flashfireblast you need to add ../stylesheets/css.css as the stylesheet href.
For reference:
If current directory is /var/www
1) /: means Root. /
2) ./: means Current Directory. /var/www
3) ../: means Previous directory. /var/

nginx proxy to remote node.js express app in subdirectory

I am completely stuck with a situation where I want to have several node applications on one server. I get this working fine by having the applications running on different ports. I can access the applications by putting in the ip address with port.
I would like to proxy the applications from my nginx server by using different sub-directories like so:
my.domain
location /app1 {
proxy_pass http://10.131.6.181:3001;
}
location /app2 {
proxy_pass http://10.131.6.181:3002;
}
Doing this I had to move the all the express routes to /app1 for application1. This works but now I am stuck with the static files.
I can now access the application with http://10.131.6.181:3001/app1 which is great, but via http://my.domain/app1 the static files are not loaded.
The static files can be accessed directly http://10.131.6.181:3001/css but not via the proxy http://my.domain/css
Ideally I would like to have the applications on different ports without the sub-directory in the express routes but only sub-directories in the proxy. I tried to put my head through the wall for the last 5 hours but didn't achieve anything.
Now I would happy if can at least get the static files via the nginx proxy.
An updated answer for anyone who needs:
instead of
location /app1 {
proxy_pass http://10.131.6.181:3001/app1;
}
use
location /app1/ {
proxy_pass http://10.131.6.181:3001/;
}
or if on local
location /app1/ {
proxy_pass http://localhost:3000/;
}
This is the correct way and this way you will not need to modify express. Express will receive only the part after /app1/
I finally worked it out after a google surge.
I added the directories to the nginx proxy_pass
my.domain
location /app1 {
proxy_pass http://10.131.6.181:3001/app1;
}
location /app2 {
proxy_pass http://10.131.6.181:3002/app2;
}
And I had to change the express applications to use the subdirectory
app.use('/app1', express.static(path.join(__dirname, 'public')));
app.use('/app1'', require('./routes'));
In the router I had to prefix all the redirects.
router.get('/logout', function (req, res) {
req.logout();
res.redirect('/app1/login');
});
The static files are called like so from html
<link rel="stylesheet" href="/app1/css/style.css"/>
A bit of a pain to change all the redirects and static url. I am sure there is a smarter way by setting a global variable in my node-express app. If anybody knows an easier way please post...

node.js with nginx, how to remove direct ip:port access

I inherited a node.js project and I am very new to the platform/language.
The application I inherited is in development so it is a work in progress. In its current state it runs off port 7576 so you access it this way: server_ip:7576
I've been tasked with putting this "prototype" on a live server so my boss can show it to investors etc. But I have to password protect it.
So what I did is I got it running on the live server. And then I made it use a nginx vhost like this:
server {
listen 80;
auth_basic "Restricted";
auth_basic_user_file /usr/ssl/htpasswd;
access_log /etc/nginx/logs/access/wip.mydomain.com.access.log;
error_log /etc/nginx/logs/error/wip.mydomain.com.error.log;
server_name wip.mydomain.com;
location / {
proxy_pass http://127.0.0.1:7576;
root /var/app;
expires 30d;
#uncomment this is you want to name an index file:
#index index.php index.html;
access_log off;
}
location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf|html|htm)$ {
root /var/app/public;
}
}
`
This got the job done, I can now access my app by going to wip.mydomain.com
And I can easily password protect it via nginx.
My problem is the app is still accessible via the ip:port and I don't know how to prevent that.
Any help is appreciated.
Thanks
In your node javascript code, you need to explicitly bind to the loopback IP:
server.listen(7576, '127.0.0.1');
(You are looking for a call to .listen(<port>) to fix. The variable may be called app or something else though).
Any IP address starting with 127. is a loopback address that can only be accessed within a single machine (doesn't actually use the network).

Resources