How to serve static files (CSS, ...) with multiples Express app + NGINX as reverse proxy server - node.js

Context
I'm runnig multiples nodesJS/Express app on the same server with the same IP adress.
I use Nginx to reverse proxy those apps and redirect it to subfolder adress (and not subdomain, i don't want to).
ex : http://123.0.0.1:8000 => http://monsite.com/Site1
Problem
My assets files (css, images, ...) do not load, I have a 404 error on those static files when the page loads. It happens only when I access the site via the proxy redirect http://monsite.com/Site1 and not when I use the IP adress : http://123.0.0.1:8000
I don't have this problem if a use the reverse proxy location from the root in the nginx conf :
location / {
but I want to access the site from a subfolder adress
My integration
Tree files:
var/www/html
|Site1/
| |server.js
| |Views/
| | |index.pug
| |Public/
| | |Css/
| | | |Style.css
|Site2/
|....
nodejs server code
const PORT = 8000;
const HOSTNAME = 'www.monsite.com';
// Dependencies.
const express = require('express');
const http = require('http');
// Initialization.
var app = express();
var server = http.Server(app);
app.set('port', PORT);
app.set('view engine', 'pug');
app.set('views','Views');
app.use(express.static('Public'));
app.use('/', (request, response) => {
response.render('index');
});
server.listen(PORT, HOSTNAME, function() {
console.log(`STARTING SERVER ON PORT ${PORT}`);
});
index pug code
doctype html
html
head
title Site 1
link(rel="stylesheet" href="/Css/style.css")
body
p Hello
nginx conf
server {
listen 80;
listen [::]:80;
root /var/www/html;
index index.html index.htm index.nginx-debian.html index.php;
server_name www.monsite.com;
location / {
#Reserved for another site
}
location /Site1/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header HOST $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_redirect off;
proxy_pass http://123.0.0.1:8000/;
}
}
PS : I tried almost all the solutions and code I found searching for this problem and nothing worked, that's why I'm asking directly here. Thank you.

I think the issue is with the url in the link tag to load the css, the url is invalid because the url is actually /Site1/Css/style.css.

Related

Do I change app.listen('port number') when I finally publish my website to a hostserver?

This is my first time publishing a website so excuse me if my question sounds dumb or easy. So I finally finished the frontend and backend of my website and I'm going to publish it to a hostname from namecheap.com. I'm using express and an abstract example of how my backend looks would be like this:
const express = require("express");
const app = express();
const path = require('path');
app.use(express.static("./public"));
app.listen(5000, () => {
"server is listening!!"
})
I realize that using app.listen(5000, () => {}) is due to listening to a port in the local host. So how do I change it to finally publish it and for my hostserver to recognize it.
use nginx or apache upstream ,
apache and nginx as ssl termination , more secure and if you add caching you could handle more request per second
nginx config :
server {
#listen 80 ;
#listen [::]:80 ;
listen 443 ssl ;
server_name namecheap.com;
ssl_certificate /etc/nginx/ssl/domain-crt.txt;
ssl_certificate_key /etc/nginx/ssl/domain-key.txt;
ssl_session_cache shared:SSL:1m; # holds approx 4000 sessions
ssl_session_timeout 1h; # 1 hour during which sessions can be re-used.
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_buffer_size 4k;
location / {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://localhost:5000;
proxy_buffering on;
}
}
If you are gonna host the website you might wanna add a port variable and assign the following value to it.
const PORT = process.env.PORT || 5000;
When you are on localhost testing the website the port variable will use the
5000
value otherwise when hosting the hosting service provides its own port to run the server which is the
process.env.PORT
make sure "PORT" is all caps. Then for running the server-
app.listen(PORT, '0.0.0.0', () => {
console.log(`Server Started at Port ${PORT}`)
});
That should do it!

What's the correct way to get rid of /public from a domain using node.js and nginx?

I've been structuring my website as follows:
someSite
app.js
public
index.html
css
assets
Project1
Project2
...
I'd like for the path to /public/Project1/index.html to be accessible by going to someSite.com/Project1, and similarly for subfolders. I've found this can be accomplished like this:
var express = require('express');
var app = express();
var http = require('http').createServer(app);
var io = require('socket.io')(http);
app.use('/public',express.static(path.join(__dirname, 'public')));
app.get('/', (req, res) => {
res.sendFile(__dirname + '/public/index.html');
});
app.get('/Project1', (req, res) => {
res.sendFile(__dirname + '/public/Project1/index.html');
});
But I don't know if this is standard, or inefficient. It's also breaking all of my relative calls, such as <img src="../assets/someImage.svg">, which no longer point to the correct place. It can be fixed by using absolute urls everywhere, but that feels wrong.
I'm using nginx as a reverse proxy server, which I think is the right place to make this change. My configuration is as follows:
server {
listen 80;
server_name someSite.com www.someSite.com;
rewrite ^(.*) https://$host$1 permanent;
}
server {
listen 443 ssl;
root /var/www/someSite/public;
server_name someSite.com;
ssl_certificate /path/to/credentials.crt;
ssl_certificate_key /path/to/credentials.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://localhost:3000/;
}
}
I still haven't figured out exactly what's wrong with my nginx configuration, but the hosting from node issue was just a matter of changing
app.use('/public',express.static(path.join(__dirname, 'public')));
to
app.use('/',express.static(path.join(__dirname, 'public')));
and removing subsequent app.get calls.

Multiple node.js projects with one domain name, each using path '/' as base url. Nginx & Express.js

I am successfully able to reverse proxy multiple node.js projects to different ports as in the Nginx set up below. What I am trying to accomplish however, is viewing the url as the root when it gets to the Node.js server.
For example, When someone goes to mydomain.com/projects/music_player, is it possible to have the Express application view the request url as just '/' instead of '/projects/music_player'.
My current Nginx setup, and Express configuration example are as follows.
Nginx:
server {
listen 80;
server_name mydomain_name.com;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /projects/music_player/ {
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
I want to do this:
app.get("/", function (req, res) {
res.sendFile("index.html");
});
app.listen(8000);
Instead of this:
app.get("/projects/music_player", function (req, res) {
res.sendFile("index.html");
});
app.listen(8000);
I am not sure if that is even possible. The reason is that I would like each of my node node.js/express applications to be deployable as standalone applications, without restructuring the code. I am trying to avoid having a bunch of domain names for each project, plus I think it would be pretty cool.
Yes! Of course you can.
I think the best way to do this is to rewrite your entire program into one express app, but you say you want to avoid this.
Then I think the short way with your current setup is to make each program listen to a different port and then map routes to them in your nginx.
For example:
dog.js
app.get("/", function (req, res) {
res.sendFile("dog.html");
});
app.listen(8001);
cat.js
app.get("/", function (req, res) {
res.sendFile("cat.html");
});
app.listen(8002);
And finally in your nginx config file:
server {
listen 80;
server_name mydomain_name.com;
location / {
proxy_pass http://localhost:8001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /cat/ {
rewrite /cat/ / break;
proxy_pass http://localhost:8002;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Notice the rewrite directive, this prevents for the /cat/ location from being sent to your cat.js app that only expects the / route.
I ran into this same problem, where I didn't want to fully restructure my backend services while wanting to change the endpoint to match others.
I ended up using node-http-proxy with http-proxy-rules. Grabbing an example from the http-proxy-rules github README, you're solution would resemble the following.
var http = require('http'),
httpProxy = require('http-proxy'),
HttpProxyRules = require('http-proxy-rules');
// Set up proxy rules instance
var proxyRules = new HttpProxyRules({
rules: {
'.*/test': 'http://localhost:8080/cool', // Rule (1)
'.*/test2/': 'http://localhost:8080/cool2/' // Rule (2)
},
default: 'http://localhost:8080' // default target
});
// Create reverse proxy instance
var proxy = httpProxy.createProxy();
// Create http server that leverages reverse proxy instance
// and proxy rules to proxy requests to different targets
http.createServer(function(req, res) {
// a match method is exposed on the proxy rules instance
// to test a request to see if it matches against one of the specified rules
var target = proxyRules.match(req);
if (target) {
return proxy.web(req, res, {
target: target
});
}
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end('The request url and path did not match any of the listed rules!');
}).listen(6010, cb);
Happy hacking!

node.js: route request to different port on same host

I have a host computer which serves a number of webapplications (not node.js based). It does this using different ports. This means that for example the following applications are live:
app1: http://hostname:3000
app2: http://hostname:3001
app3: http://hostname:3003
Next to that I have a node.js based webapp (running on port 80) which I want to use as a sort of router. When someone navigates to http://localhost/app/app1. I want it to navigate to http://hostname:3000. This is relatively straightforward using a simple redirect. However, I would want to preserve the url http://localhost/app/app1. Can someone point me to a way to make this work using node.js/express?
My routing logic looks somewhat like this (pseudo-code).
app.route('/app/:appName')
.get(appEngine.gotoApp);
appEngine.gotoApp = function(req, res) {
redirectToApp logic
}
You probably better use Nginx setting up a reverse proxy with different locations per application.
It's not what you ask for because it does not use node.js, but if it's the only purpose, Nginx really suits your needs.
For example a Nginx configuration file like should work the way you want :
server {
listen 80;
server_name myapp.com;
location /app1 {
proxy_pass http://APP_PRIVATE_IP_ADDRESS:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /app2 {
proxy_pass http://APP_PRIVATE_IP_ADDRESS:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /app3 {
proxy_pass http://APP_PRIVATE_IP_ADDRESS:3003;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
If you use express, you can try to create the app with the cli express application generator.
It creates an express app and returns it with module exports.
In the server.js file it pass to listen function of the server instance the express app object.
You can create more server object and listen different app with different port.
var server = http.createServer(app);
server.listen(port);
var server2 = http.createServer(app2);
server2.listen(port2);
If you want to point different app based on the url, you can instance an express router instead of express object.
var app1 = express.Router();
Then you can set all your routes into this object with classic get or post or other methods.
Now you are able to pass the router as a middleware of your main express app.
app.use( "app1/", app1 );
You can also pass an express app to middleware, instead of router object, in order to gain the possibility of exec the app with a different url and port server listening.
There is a nice http-proxy lib designed exactly for that!
const httpProxy = require('http-proxy');
const url = require('url');
const proxy = httpProxy.createProxy();
const options = {
'/app/app1': 'http://localhost:3000',
'/app/app2': 'http://localhost:3001',
'/app/app3': 'http://localhost:3003',
}
require('http').createServer((req, res) => {
const pathname = url.parse(req.url).pathname;
for (const [pattern, target] of Object.entries(options)) {
if (pathname === pattern ||
pathname.startsWith(pattern + '/')
) {
proxy.web(req, res, {target});
}
}
}).listen(80);

Express.js: how to get remote client address

I don't completely understand how I should get a remote user IP address.
Let's say I have a simple request route such as:
app.get(/, function (req, res){
var forwardedIpsStr = req.header('x-forwarded-for');
var IP = '';
if (forwardedIpsStr) {
IP = forwardedIps = forwardedIpsStr.split(',')[0];
}
});
Is the above approach correct to get the real user IP address or is there a better way?
And what about proxies?
If you are running behind a proxy like NGiNX or what have you, only then you should check for 'x-forwarded-for':
var ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress
If the proxy isn't 'yours', I wouldn't trust the 'x-forwarded-for' header, because it can be spoofed.
While the answer from #alessioalex works, there's another way as stated in the Express behind proxies section of Express - guide.
Add app.set('trust proxy', true) to your express initialization code.
When you want to get the ip of the remote client, use req.ip or req.ips in the usual way (as if there isn't a reverse proxy)
Optional reading:
Use req.ip or req.ips. req.connection.remoteAddress does't work with this solution.
More options for 'trust proxy' are available if you need something more sophisticated than trusting everything passed through in x-forwarded-for header (for example, when your proxy doesn't remove preexisting x-forwarded-for header from untrusted sources). See the linked guide for more details.
If your proxy server does not populated x-forwarded-for header, there are two possibilities.
The proxy server does not relay the information on where the request was originally. In this case, there would be no way to find out where the request was originally from. You need to modify configuration of the proxy server first.
For example, if you use nginx as your reverse proxy, you may need to add proxy_set_header X-Forwarded-For $remote_addr; to your configuration.
The proxy server relays the information on where the request was originally from in a proprietary fashion (for example, custom http header). In such case, this answer would not work. There may be a custom way to get that information out, but you need to first understand the mechanism.
In nginx.conf file:
proxy_set_header X-Real-IP $remote_addr;
In node.js server file:
var ip = req.headers['x-real-ip'] || req.connection.remoteAddress;
note that express lowercases headers
If you are fine using 3rd-party library. You can check request-ip.
You can use it is by
import requestIp from 'request-ip';
app.use(requestIp.mw())
app.use((req, res) => {
const ip = req.clientIp;
});
The source code is quite long, so I won't copy here, you can check at https://github.com/pbojinov/request-ip/blob/master/src/index.js
Basically,
It looks for specific headers in the request and falls back to some
defaults if they do not exist.
The user ip is determined by the following order:
X-Client-IP
X-Forwarded-For (Header may return multiple IP addresses in the format: "client IP, proxy 1 IP, proxy 2 IP", so we take the the first
one.)
CF-Connecting-IP (Cloudflare)
Fastly-Client-Ip (Fastly CDN and Firebase hosting header when forwared to a cloud function)
True-Client-Ip (Akamai and Cloudflare)
X-Real-IP (Nginx proxy/FastCGI)
X-Cluster-Client-IP (Rackspace LB, Riverbed Stingray)
X-Forwarded, Forwarded-For and Forwarded (Variations of #2)
req.connection.remoteAddress
req.socket.remoteAddress
req.connection.socket.remoteAddress
req.info.remoteAddress
If an IP address cannot be found, it will return null.
Disclose: I am not associated with the library.
Particularly for node, the documentation for the http server component, under event connection says:
[Triggered] when a new TCP stream is established. [The] socket is an object of type
net.Socket. Usually users will not want to access this event. In
particular, the socket will not emit readable events because of how
the protocol parser attaches to the socket. The socket can also be
accessed at request.connection.
So, that means request.connection is a socket and according to the documentation there is indeed a socket.remoteAddress attribute which according to the documentation is:
The string representation of the remote IP address. For example,
'74.125.127.100' or '2001:4860:a005::68'.
Under express, the request object is also an instance of the Node http request object, so this approach should still work.
However, under Express.js the request already has two attributes: req.ip and req.ips
req.ip
Return the remote address, or when "trust proxy" is enabled - the upstream address.
req.ips
When "trust proxy" is true, parse the "X-Forwarded-For" ip address list and return an array, otherwise an empty array is
returned. For example if the value were "client, proxy1, proxy2" you
would receive the array ["client", "proxy1", "proxy2"] where "proxy2"
is the furthest down-stream.
It may be worth mentioning that, according to my understanding, the Express req.ip is a better approach than req.connection.remoteAddress, since req.ip contains the actual client ip (provided that trusted proxy is enabled in express), whereas the other may contain the proxy's IP address (if there is one).
That is the reason why the currently accepted answer suggests:
var ip = req.headers['x-forwarded-for'] ||
req.connection.remoteAddress;
The req.headers['x-forwarded-for'] will be the equivalent of express req.ip.
Add app.set('trust proxy', true)
Use req.ip or req.ips in the usual way
This is just additional information for this answer.
If you are using nginx, you would add proxy_set_header X-Real-IP $remote_addr; to the location block for the site. /etc/nginx/sites-available/www.example.com for example. Here is a example server block.
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://127.0.1.1:3080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
After restarting nginx, you will be able to access the ip in your node/express application routes with req.headers['x-real-ip'] || req.connection.remoteAddress;
I know this question has been answered, but here's how I got mine to work.
let ip = req.connection.remoteAddress.split(`:`).pop();
I wrote a package for that purpose. You can use it as express middleware. My package is published here: https://www.npmjs.com/package/express-ip
You can install the module using
npm i express-ip
Usage
const express = require('express');
const app = express();
const expressip = require('express-ip');
app.use(expressip().getIpInfoMiddleware);
app.get('/', function (req, res) {
console.log(req.ipInfo);
});
According to Express behind proxies, req.ip has taken into account reverse proxy if you have configured trust proxy properly. Therefore it's better than req.connection.remoteAddress which is obtained from network layer and unaware of proxy.
In my case, similar to this solution, I ended up using the following x-forwarded-for approach:
let ip = (req.headers['x-forwarded-for'] || '').split(',')[0];
x-forwarded-for header will keep on adding the route of the IP from the origin all the way to the final destination server, thus if you need to retrieve the origin client's IP, this would be the first item of the array.
This worked for me better than the rest. My sites are behind CloudFlare and it seemed to require cf-connecting-ip.
req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress
Didn't test Express behind proxies as it didn't say anything about this cf-connecting-ip header.
var ip = req.connection.remoteAddress;
ip = ip.split(':')[3];
The headers object has everything you need, just do this:
var ip = req.headers['x-forwarded-for'].split(',')[0];
With could-flare, nginx and x-real-ip support
var user_ip;
if(req.headers['cf-connecting-ip'] && req.headers['cf-connecting-ip'].split(', ').length) {
let first = req.headers['cf-connecting-ip'].split(', ');
user_ip = first[0];
} else {
let user_ip = req.headers['x-forwarded-for'] || req.headers['x-real-ip'] || req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress;
}
Putting all together witk #kakopappa solution plus morgan logging of the client ip address:
morgan.token('client_ip', function getId(req) {
return req.client_ip
});
const LOG_OUT = ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" :client_ip'
self.app.use(morgan(LOG_OUT, {
skip: function(req, res) { // custom logging: filter status codes
return res.statusCode < self._options.logging.statusCode;
}
}));
// could-flare, nginx and x-real-ip support
var getIpInfoMiddleware = function(req, res, next) {
var client_ip;
if (req.headers['cf-connecting-ip'] && req.headers['cf-connecting-ip'].split(', ').length) {
var first = req.headers['cf-connecting-ip'].split(', ');
client_ip = first[0];
} else {
client_ip = req.headers['x-forwarded-for'] || req.headers['x-real-ip'] || req.connection.remoteAddress || req.socket.remoteAddress || req.connection.socket.remoteAddress;
}
req.client_ip = client_ip;
next();
};
self.app.use(getIpInfoMiddleware);

Resources