NGINX + ExpressJS - Reverse proxy for URLs in HTML - node.js

I have a NodeJS + ExpressJS app running on port 3000 which has a route:
router.get('/getHello', function(req, res) {
res.send('Hello, world!');
});
and a HTML page which does a GET on this route
<a href="/getHello">
<input type="button" value="Visit Helo World page" />
</a>
This standalone app works as intended. It displays Hello, world! when the button is pressed.
Now, here's the problem:
I have setup a reverse proxy on this app using nginx. Here's the config file from sites-available (linked with sites-enabled)
server {
listen 80;
server_name localhost;
location /routetest/ {
proxy_pass http://127.0.0.1:3000/;
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_cache_bypass $http_upgrade;
}
}
The app opens fine when I visit http://localhost/routetest but when I click on the button, it opens http://localhost/getHello whereas I wanted it to open http://localhost/routetest/getHello. Since http://localhost/getHello is not defined, I end up with a 404. (On the other hand, http://localhost/routetest/getHello correctly returns the Hello, world message)
Now, my question is:
My frontend is written with / as the base path (to make all GET and POST requests), for example <a href="/getHello"> and I feel manually appending /routetest/ before all URLs in my HTML not a good practice (since I may want to change this base path later and then I'll have to update it everywhere). So, is there a way for NGINX or some express middleware to add /routetest/ for URLs that are written with / as the base path, without having to change it in my HTML manually?

By a long way the easiest solution is to use relative paths without the leading / if that's an option that's available to you.
Rewriting URLs in the response is somewhere between tricky and impossible in the general case. If you think you can successfully identify the relevant URLs in your content you might be able to use nginx's catchily titled ngx_http_sub_module:
http://nginx.org/en/docs/http/ngx_http_sub_module.html
There are various Express middleware implementations that do a similar thing but I couldn't find one that looked reliable enough to recommend. The basic approach would be similar to the compression middleware if you fancy giving it a go yourself. Again it's quite difficult in general but depending on the specifics of how you serve up your HTML it might not be too bad.

Related

How do i configure nginx (and strapis) to correctly serve two strapi instances on the same server?

i want to host two small websites, both made with strapi backend and react frontend, on my server which is a digital ocean droplet.
I already configured nginx in order to work for one of the websites and everything is working correctly. I can access strapi from site1.com/dashboard and my queries point to site1.com/api/graphql. I followed some tutorials for that.
Here are the nginx files i added:
/etc/nginx/sites-available/site1.com:
server {
listen 80;
listen [::]:80;
root /var/www/site1.com/react;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name site1.com www.site1.com;
location /api/ {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://strapi;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass_request_headers on;
}
location /dashboard {
proxy_pass http://strapi/dashboard;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_pass_request_headers on;
}
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ /index.html;
}
}
file /etc/nginx/conf.d/upstream.conf:
upstream strapi {
server 127.0.0.1:1337;
}
What i now want to do is to deploy another website to the same server and configure nginx to serve it as well (on another domain, for example site2.com).
So i added the nginx virtual server file for the second website, copying the first one and changing the domain name and root directory.
The site 2 frontend is now working correctly and accessible on its domain.
However when i start the site 2 strapi instance it says the port 1337 is already in use (obviously, it is used by site 1 strapi instance that is running with pm2). So i changed the port in strapi config to be 1338 (is it ok?) but now i don't know what do to in nginx in order to serve the two different strapi instances on different domains.
The hostname you are using for the proxy_pass directive is the upstream name you defined in the separate config file. This is basically an indirection to one or more real backends. In your case that's the application running on port 1337 on the same machine. It could also be a list of external domains where nginx takes care to distribute the load to.
Your approach with additional virtual hosts already looks good. The frontend that already "works" under site2 is probably the old instance served under the new domain if your proxy_pass directive still points to http://strapi for site2 (which probably still resolves to `localhost:1337).
As you already mentioned, a second instance of the backend needs to run on a different port. The port number you use is not really important as you will control this with your upstream configuration. Just make sure to not use a port number below 1024 (which requires root permissions), don't conflict with other processes on the system (you will notice) and as best practice, don't use port numbers that are default for some other protocols (maybe check this list).
To fix your setup, just define a second upstream like the first one but pointing to the new url e.g. localhost:1338 and then reference this new name in the proxy_pass directive of site2.
Technically, with just one backend per upstream, you could also skip the upstream part completely and reference the URL directly in the proxy_pass directives, but using a shorthand name can also support readability of your configuration.

Express and Nginx figthing over routing

So i have a strange configuration that i need until i get to refactor a project later this year.
it is a static index.html landing page served with a location root "/" by NGINX.
i have a slash location like so:
location /channel/ {
proxy_pass http://node_server:5000/;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Ssl on;
proxy_cache_bypass $http_upgrade;
}
that express.js then handles. Express just serves a static folder with auto-generated content that a user can then navigate around in.
It works well in the sense that any appending slashes like:
/channel/open/
but ONLY if i have a trailing slash after "open". If i leave it out then NGINX will throw a "404 Not Found", if i keep it in, it works as intended.
The thing is that a user should be able to click on a folder in the static served directory to traverse a file structure, and the way that works in a browser is that it does not append a trailing slash to a directory name when clicked, so NGINX then throws the "404".I have tried a NPM package called 'express-slash' but that did not solve the issue.
i was hoping someone here had some suggestions on what i could try out next?
Details about trailing slash behavior can be found here: https://serverfault.com/questions/607615/using-trailing-slashes-in-nginx-configuration
But if your goal is to configure NGINX to allow directory traversal, you may be looking for the autoindex directive: http://nginx.org/en/docs/http/ngx_http_autoindex_module.html

Server Side Rendering with Nginx outputs HTML strings

I have a vue-ssr project, not using nuxt.js. And the server is express.
Project ran at port 3000.
When not using nginx, visited ip:3000, the page worked well.
When using nginx, visited my domain, I still could get HTML strings, but the strings were not rendered, like that:
(I don't have enought reputation to post images.)
html strings.png
And the request and response headers like that:
request and response headers.png
Here is my nginx config:
server {
server_name mydomain;
root /my/path;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://localhost:3000;
proxy_redirect off;
}
}
I've tried the config introduced in nuxt.js documents, but didn't worked either.
If removing the nosniff header fixes the problem, that would be an indication that for some reason express is not sending the right mime types with your responses.
You could check that by hitting express directly and examining the headers it is returning, e.g.:
curl -D - -o /dev/null http://localhost:3000
If it's NOT returning text/html, it could be a sign you're accidentally breaking the default mime type somewhere in your application code.

Nginx - Reverse proxy to an already reverse proxied location or double reverse proxy?

I am using nginx on centos 7.
I am reverse proxying a remote nodejs server on the same LAN to the nginx root / as per the below:
location / {
proxy_pass http://192.168.1.104:3000/;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
This works fine for serving my website - all external requests on port 80 are rewritten to use https which has been configured in nginx e.g nginx forwards any incoming http requests to https and deals with rewrites and forwarding so that the nodejs content is served over ssl even though ssl hasn't been configured within the node application.
e.g my site can be accessed at https://example.com
I now want to reverse proxy another nodejs app so that it appears at a location which is prefixed with https://example.com e.g: https://example.com/node2/
I've tried using the below config for the second node server...
location /node2/ {
proxy_pass http://192.168.1.100:3000/;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
When I load the https://example.com/node2/ url, the html of the root page of the second node server is displayed but none of the css, js or images are loaded so the page doesn't look or work as it should and I see the following in the browser console...
Mixed Content: The page at 'https://example.com/node2/' was loaded
over HTTPS, but requested an insecure image
'http://192.168.1.100:3000/assets/graphics/logo.png'. This content
should also be served over HTTPS.
and
Failed to load resource: net::ERR_CONNECTION_CLOSED https://192.168.1.100:3000/assets/css/styles.min.css
for css and js assets... so it seems that no redirection is taking place for assets and also any links on the html page do not show the /node2/ suffix when hovered over or when clicked as the page tries to load the resource from https://example.com instead of https://example.com/node2/
Is it possible to actually do what I want in terms of reverse proxy two locations and can anyone point me in the right direction on how I can get this work as need?
The application being loaded is set up to load assets from the server root, not with server root + /node2/

nginx link nodejs app to subdomain

I have a dropplet on digital-ocean where I have a node.js app and nginx.
So, I installed nginx and then in /etc/nginx/sites-available/ I created a new file called api where I have the following content:
server {
server_name api.my-website.com;
location / {
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $proxy_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://127.0.0.1:9000; // this is where the node app lives
}
}
After I create that I restared nginx but if I go to api.my-website.com I dont see anything (my api has a landing page on /), but if I go to my-website.com:9000 I see the landing that should be on api.my-website.com.
Can someone explain me please what I'm doing wrong?
You did not included your /etc/nginx/sites-available/api file into /etc/nginx/nginx.conf. You need to include /etc/nginx/sites-available/api in /etc/nginx/nginx.conf inside http module and then restart or reload nginx. Your /etc/nginx/nginx.conf may looks like
http {
# other lines for logging, gzip, etc.
# other include may here
# Your include as following
include /etc/nginx/sites-available/api;
}

Resources