Using subdomains locally on an express app - node.js

I have node app running on express. I'd like to set up two different account types (one for buyers, one for sellers) and have them accessible through different subdomains (ie buyers.example.com and sellers.example.com). I'm not sure how to set this up locally. I know that I can edit the hosts file on my machine to have *.localhost resolve to 127.0.0.1, but that doesn't solve my problem. In that case, buyers.localhost/login and sellers.localhost/login will route to the same place. Is this a common issue, and if so, what is the standard way of handling this? What are some options for me to separate the logic for handling two account types?

First add this:
app.get('*', function(req, res, next){
if (req.headers.host == 'buyers.localhost:5000') { //Port is important if the url has it
req.url = '/buyers' + req.url;
}
else if(req.headers.host == 'sellers.localhost:5000') {
req.url = '/sellers' + req.url;
}
next();
});
And then:
app.get('/login', function(){
//Default case, no subdomain
})
app.get('/buyers/login', function(){
//Buyers subdomain
})
app.get('/sellers/login', function(){
//Sellers subdomain
})
Learned this here: https://groups.google.com/forum/#!topic/express-js/sEu66n8Oju0

app.all("*", function (req: Request, res: Response, next: NextFunction) {
if (req.headers.host == `v1.${process.env.SERVER_DOMAIN}`) req.url = `/v1${req.url}`; // ? <= direct from v1 to domain/v1
if (req.headers.host == `api.${process.env.SERVER_DOMAIN}`) req.url = `/api${req.url}`; // ? <= direct from v1 to domain/api
next();
}); //Port is important if the url has it

Related

Why does HTTP -> HTTPS redirect fail in nodejs when no subdomain is specified?

I'm running a nodejs webapp on Heroku and I want to redirect all users that access via http to https with the corresponding URL.
I have it mostly working however the user is redirected to the home page if no subdomain is specified. Any idea what is going on here?
The node rerouting middleware:
app.enable('trust proxy');
app.use((req, res, next) => {
if (req.get('X-Forwarded-Proto') !== 'https') {
res.redirect(`https://${req.headers.host + req.url}`);
} else {
next();
}
});
Works:
http://www.example.com/page redirects to https://www.example.com/page
Fails:
http://example.com/page redirects to https://www.example.com
Because req.url is an inherited property from Node.JS's http module and does not necessarily contain the original URL. Express.JS documentation also states it, https://expressjs.com/en/api.html#req.originalUrl
If you want to retain the original url you should use the correct property, which is originalUrl.
app.enable('trust proxy');
app.use((req, res, next) => {
if (req.get('X-Forwarded-Proto') !== 'https') {
res.redirect(`https://${req.headers.host + req.originalUrl}`);
} else {
next();
}
});

Prevent access through ip address

Using express.js how do I prevent requests from accessing my server through the ip address instead of through the domain fronted by cloudflare?
This is to prevent googlebot from indexing http://xx.xx.xx.xxx/
This is what I ended up with, hope it works.
app.get('/*', function(req, res, next) {
if (req.host === '129.8d.xx.xxx') {
res.redirect(301, 'https://example.com' + req.path)
}
else {
next();
}
})

express.static() and sendFile() problem... creating a dynamic host with nodejs

After configure my web server with nginx, i redirected all *.example.com to my nodejs server.
But before, i handle the http request, i check the url and host to see if it is correct or not.
For example, if the user writes something like what.ever.example.com
I redirect him to the main website because that host is not valid.
otherwise if the user writes something like mydomain.example.com
The user should access to this website and receive the angular APP.
So i am doing something like this.
UPDATED CODE
const express = require('express');
const cors = require('cors');
const mongoose = require('./server/database');
const bodyParser = require('body-parser');
const app = express();
var path = require('path');
// Settings
app.set('port', process.env.PORT || 4000)
// Middlewares
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.json());
app.use(cors());
// Routes API
app.use('/api/users', require('./server/routes/usuarios.routes'));
app.use('/api/almacenes', require('./server/routes/almacen.routes'))
app.use('/api/updates', require('./server/routes/update.routes'))
app.use('/api/dominios', require('./server/routes/dominios.routes'))
app.get('/', checkHost);
app.get('/', express.static('../nginx/app'));
app.get('/*', checkPath);
function checkHost(req, res, next) { //With this function what i pretend is check the subdomain that the user send, and if it doesn't exist. redirect it.
var domain = req.headers.host
subDomain = domain.split('.')
if (subDomain.length == 3) {
subDomain = subDomain[0].split("-").join(" ");
let query = { dominio: subDomain }
var dominiosModel = mongoose.model('dominios');
dominiosModel.findOne(query).exec((err, response) => {
if (response != null) {
if (response.dominio == subDomain) {
next();
} else {
res.writeHead(303, {
location: 'http://www.example.com/index.html'
})
res.end()
}
} else {
res.writeHead(303, {
location: 'http://www.example.com/index.html'
})
res.end()
}
})
} else {
res.writeHead(303, {
location: 'http://www.example.com/index.html'
})
res.end()
}
}
function checkPath(req, res, next) { //With this function what i want to do is.. if the user send *.example.com/whatever, i redirect it to *.example.com
if (req.url !== '/') {
res.writeHead(303, {
location: `http://${req.headers.host}`
})
res.end()
} else {
next()
}
}
// Starting Server.
app.listen(app.get('port'), () => {
console.log('Server listening on port', app.get('port'));
});
All redirects are working well, but when in checkHost the subDomain matched, it doesnt send nothing to the front... so what can i do here?
Try removing the response.end(). Since .sendFile() accepts a callback, it is most likely an async function, which means that calling .end() right after .sendFile() will most probably result in a blank response.
The sendFile function requires absolute path of the file to be sent, if root is not provided. If root is provided, a relative path could be used, but the root itself should be absolute. Check documentation here: https://expressjs.com/en/api.html#res.sendFile
You should try to send your index.html in following manner:
app.get('*', checkPath, checkHost, function (req, response) {
response.sendFile('index.html', { root: path.join(__dirname, '../nginx/app') });
}
This should work provided that the path ../nginx/app/index.html is valid, relative to the file in which this code is written.
Additionally, based on the sample code (and the comments), you probably don't need the express.static(...) at all. Unless, you need to serve 'other' files statically.
If it is needed, then the app.use(express.static('../nginx/app')) should be outside the controller. It should probably be added before the bodyParser, but since you are concerned about someone being able to access 'index.html' via the static middleware, you can consider following order for your middlewares:
//existing body parser and cors middlewares
// existing /api/* api middlewares.
app.use(checkPath);
app.use(checkHost);
app.use(express.static('../nginx/app'));
If the checkPath middleware is modified slightly to redirect to /index.html, the middleware with '*' path might not be required at all with this setup.

Forward HTTP to HTTPS - AWS Windows Node.js

I have a applications running on AWS Windows and Node.js. I can access using http and https. But i need it to forward http to https if anyone access through http.
I can think of many way, but would appreciate any advice on the best approach. The server is a EC2 instance, accessed through a load balancer.
If you're using express, this middleware module makes it easy to enforce https: https://www.npmjs.com/package/express-force-ssl
If you're using a reverse proxy in front of your app (ELB, nginx, etc), you'll need to set the trust proxy setting.
Here's a sample without the above module:
// Forward all requests to HTTPS.
// enable reverse proxy support in Express. This causes the
// the "X-Forwarded-Proto" header field to be trusted so its
// value can be used to determine the protocol. See
// http://expressjs.com/api#app-settings for more details.
app.enable('trust proxy');
// Add a handler to inspect the req.secure flag (see
// http://expressjs.com/api#req.secure). This allows us
// to know whether the request was via http or https.
app.use((req, res, next) => {
if (req.secure) {
// request was via https, so do no special handling
next();
} else {
// request was via http, so redirect to https
console.log('Redirecting to https');
res.redirect('https://' + req.headers.host + req.url);
}
});
Complete sample app.js
var express = require('express');
var app = express();
// Forward all requests to HTTPS.
// enable reverse proxy support in Express. This causes the
// the "X-Forwarded-Proto" header field to be trusted so its
// value can be used to determine the protocol. See
// http://expressjs.com/api#app-settings for more details.
app.enable('trust proxy');
// Add a handler to inspect the req.secure flag (see
// http://expressjs.com/api#req.secure). This allows us
// to know whether the request was via http or https.
app.use((req, res, next) => {
if (req.secure) {
// request was via https, so do no special handling
next();
} else {
// request was via http, so redirect to https
console.log('Redirecting to https');
res.redirect('https://' + req.headers.host + req.url);
}
});
// Respond to any GET requests with our message
app.get('*', (req, res) => {
res.send('This is only served over https');
});
// Listen on the assigned port
var port = process.env.PORT || 3001;
app.listen(port);
console.log('Hello started on port ' + port);
Redirect only GET requests, respond with error for non-GET requests
app.all('*', (req, res, next) => {
if (req.secure) {
next();
} else if (req.method === 'GET') {
res.redirect(`https://${req.headers.host}${req.url}`);
} else {
res.status(401).send('Secure channel required');
}
});

node.js expressjs pattern match not equal

I'm using expressjs with node and running both https and http.
I want to require that all routes for /secure/* use https. This is done:
app.all("/secure/*", function(req, res, next) {
if (!req.connection.encrypted) {
res.redirect("https://" + req.headers["host"].replace(new RegExp(config.http_port, "g"), config.https_port) + req.url);
} else {
return next();
};
});
However, I also want to require that all routes that are not using /secure/* and try to access https, are redirected using the same method to http.
I tried doing this:
app.all("*", function(req, res, next) {
console.log(req);
if (req.connection.encrypted) {
res.redirect("http://" + req.headers["host"].replace(new RegExp(config.https_port, "g"), config.http_port) + req.url);
} else {
return next();
};
});
But I end up in a redirect loop when accessing the https pages. Is there a way to specify all routes, except those with /secure/* ?
Thank you!
A simple solution to your problem is:
app.all("*", function(req, res, next) {
if (req.connection.encrypted && !/^\/secure/.test(req.url)) {
res.redirect("http://" + req.headers["host"].replace(new RegExp(config.https_port, "g"), config.http_port) + req.url);
} else {
return next();
};
});
Only do the redirect if the URL doesn't start with /secure.
However, I'd propose that instead of the redundant 'secure' label in the URLs, just mark certain paths as requireHTTP or requireHTTPS. You know you can pass multiple methods into app.get and other such router methods, right? Assuming you define requireHTTP and requireHTTPS (which would be identical to your original functions), you'd just do:
app.get("/path/to/keep/encrypted", requireHTTPS, function(req, res) {
// Rest of controller
});
app.get("/path/to/keep/public", requireHTTP, function(req, res) {
// Rest of controller
});
app.get("/path/you/dont/care/about/encryption/status", function(req, res) {
// Rest of controller
});
That should do it.

Resources