Rewrite www. in Node.JS - node.js

I want to rewrite http://www.xyz.com/abc to http://xyz.com/abc.
How can that be done in Node.js.
I'm using node-http-proxy on port 80 and forward requests to Express on port 3000.
Thanks in advance.

Return a 301 with appropriate location headers if request hostname == www.xyz.com
The browser will do the rest.
You can do it on either side of the proxy. But i'd assume it makes sense to do something like this:
http.createServer(function (req, res) {
//
// Put your custom server logic here, then proxy
//
if (req.headers.host == 'www.xyz.com') {
res.writeHead(301, {'Location': 'xyz.com/' + req.url});
res.end();
}
else {
proxy.proxyRequest(req, res, {
host: 'localhost',
port: 9000
});
}
}).listen(80);

Related

How do I properly force HTTPS in a Node.js + Express.js application? [duplicate]

I've been trying to get HTTPS set up with a node.js project I'm working on. I've essentially followed the node.js documentation for this example:
// curl -k https://localhost:8000/
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};
https.createServer(options, function (req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000);
Now, when I do
curl -k https://localhost:8000/
I get
hello world
as expected. But if I do
curl -k http://localhost:8000/
I get
curl: (52) Empty reply from server
In retrospect this seems obvious that it would work this way, but at the same time, people who eventually visit my project aren't going to type in https://yadayada, and I want all traffic to be https from the moment they hit the site.
How can I get node (and Express as that is the framework I'm using) to hand off all incoming traffic to https, regardless of whether or not it was specified? I haven't been able to find any documentation that has addressed this. Or is it just assumed that in a production environment, node has something that sits in front of it (e.g. nginx) that handles this kind of redirection?
This is my first foray into web development, so please forgive my ignorance if this is something obvious.
Ryan, thanks for pointing me in the right direction. I fleshed out your answer (2nd paragraph) a little bit with some code and it works. In this scenario these code snippets are put in my express app:
// set up plain http server
var http = express();
// set up a route to redirect http to https
http.get('*', function(req, res) {
res.redirect('https://' + req.headers.host + req.url);
// Or, if you don't want to automatically detect the domain name from the request header, you can hard code it:
// res.redirect('https://example.com' + req.url);
})
// have it listen on 8080
http.listen(8080);
The https express server listens ATM on 3000. I set up these iptables rules so that node doesn't have to run as root:
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 3000
All together, this works exactly as I wanted it to.
To prevent theft of cookies over HTTP, see this answer (from the comments) or use this code:
const session = require('cookie-session');
app.use(
session({
secret: "some secret",
httpOnly: true, // Don't let browser javascript access cookies.
secure: true, // Only use cookies over https.
})
);
Thanks to this guy:
https://www.tonyerwin.com/2014/09/redirecting-http-to-https-with-nodejs.html
If secure, requests via https, otherwise redirects to https
app.enable('trust proxy')
app.use((req, res, next) => {
req.secure ? next() : res.redirect('https://' + req.headers.host + req.url)
})
If you follow conventional ports since HTTP tries port 80 by default and HTTPS tries port 443 by default you can simply have two server's on the same machine:
Here's the code:
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('./key.pem'),
cert: fs.readFileSync('./cert.pem')
};
https.createServer(options, function (req, res) {
res.end('secure!');
}).listen(443);
// Redirect from http port 80 to https
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(301, { "Location": "https://" + req.headers['host'] + req.url });
res.end();
}).listen(80);
Test with https:
$ curl https://127.0.0.1 -k
secure!
With http:
$ curl http://127.0.0.1 -i
HTTP/1.1 301 Moved Permanently
Location: https://127.0.0.1/
Date: Sun, 01 Jun 2014 06:15:16 GMT
Connection: keep-alive
Transfer-Encoding: chunked
More details : Nodejs HTTP and HTTPS over same port
With Nginx you can take advantage of the "x-forwarded-proto" header:
function ensureSec(req, res, next){
if (req.headers["x-forwarded-proto"] === "https"){
return next();
}
res.redirect("https://" + req.headers.host + req.url);
}
As of 0.4.12 we have no real clean way of listening for HTTP & HTTPS on the same port using Node's HTTP/HTTPS servers.
Some people have solved this issue by having having Node's HTTPS server (this works with Express.js as well) listen to 443 (or some other port) and also have a small http server bind to 80 and redirect users to the secure port.
If you absolutely have to be able to handle both protocols on a single port then you need to put nginx, lighttpd, apache, or some other web server on that port and have act as a reverse proxy for Node.
I use the solution proposed by Basarat but I also need to overwrite the port because I used to have 2 different ports for HTTP and HTTPS protocols.
res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(http_port,https_port) + req.url });
I prefer also to use not standard port so to start nodejs without root privileges.
I like 8080 and 8443 because I came from lots of years of programming on tomcat.
My complete file become
var fs = require('fs');
var http = require('http');
var http_port = process.env.PORT || 8080;
var app = require('express')();
// HTTPS definitions
var https = require('https');
var https_port = process.env.PORT_HTTPS || 8443;
var options = {
key : fs.readFileSync('server.key'),
cert : fs.readFileSync('server.crt')
};
app.get('/', function (req, res) {
res.send('Hello World!');
});
https.createServer(options, app).listen(https_port, function () {
console.log('Magic happens on port ' + https_port);
});
// Redirect from http port to https
http.createServer(function (req, res) {
res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(http_port,https_port) + req.url });
console.log("http request, will go to >> ");
console.log("https://" + req.headers['host'].replace(http_port,https_port) + req.url );
res.end();
}).listen(http_port);
Then I use iptable for forwording 80 and 443 traffic on my HTTP and HTTPS ports.
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8443
You can use the express-force-https module:
npm install --save express-force-https
var express = require('express');
var secure = require('express-force-https');
var app = express();
app.use(secure);
I find req.protocol works when I am using express (have not tested without but I suspect it works). using current node 0.10.22 with express 3.4.3
app.use(function(req,res,next) {
if (!/https/.test(req.protocol)){
res.redirect("https://" + req.headers.host + req.url);
} else {
return next();
}
});
This answer needs to be updated to work with Express 4.0. Here is how I got the separate http server to work:
var express = require('express');
var http = require('http');
var https = require('https');
// Primary https app
var app = express()
var port = process.env.PORT || 3000;
app.set('env', 'development');
app.set('port', port);
var router = express.Router();
app.use('/', router);
// ... other routes here
var certOpts = {
key: '/path/to/key.pem',
cert: '/path/to/cert.pem'
};
var server = https.createServer(certOpts, app);
server.listen(port, function(){
console.log('Express server listening to port '+port);
});
// Secondary http app
var httpApp = express();
var httpRouter = express.Router();
httpApp.use('*', httpRouter);
httpRouter.get('*', function(req, res){
var host = req.get('Host');
// replace the port in the host
host = host.replace(/:\d+$/, ":"+app.get('port'));
// determine the redirect destination
var destination = ['https://', host, req.url].join('');
return res.redirect(destination);
});
var httpServer = http.createServer(httpApp);
httpServer.listen(8080);
If your app is behind a trusted proxy (e.g. an AWS ELB or a correctly configured nginx), this code should work:
app.enable('trust proxy');
app.use(function(req, res, next) {
if (req.secure){
return next();
}
res.redirect("https://" + req.headers.host + req.url);
});
Notes:
This assumes that you're hosting your site on 80 and 443, if not, you'll need to change the port when you redirect
This also assumes that you're terminating the SSL on the proxy. If you're doing SSL end to end use the answer from #basarat above. End to end SSL is the better solution.
app.enable('trust proxy') allows express to check the X-Forwarded-Proto header
you can use "net" module to listening for HTTP & HTTPS on the same port
var https = require('https');
var http = require('http');
var fs = require('fs');
var net=require('net');
var handle=net.createServer().listen(8000)
var options = {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};
https.createServer(options, function (req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(handle);
http.createServer(function(req,res){
res.writeHead(200);
res.end("hello world\n");
}).listen(handle)
Most answers here suggest to use the req.headers.host header.
The Host header is required by HTTP 1.1, but it is actually optional since the header might not be actually sent by a HTTP client, and node/express will accept this request.
You might ask: which HTTP client (e.g: browser) can send a request missing that header? The HTTP protocol is very trivial. You can craft a HTTP request in few lines of code, to not send a host header, and if each time you receive a malformed request you throw an exception, and depending on how you handle such exceptions, this can take your server down.
So always validate all input. This is not paranoia, I have received requests lacking the host header in my service.
Also, never treat URLs as strings. Use the node url module to modify specific parts of a string. Treating URLs as strings can be exploited in many many many ways. Don't do it.
var express = require('express');
var app = express();
app.get('*',function (req, res) {
res.redirect('https://<domain>' + req.url);
});
app.listen(80);
This is what we use and it works great!
This worked for me:
app.use(function(req,res,next) {
if(req.headers["x-forwarded-proto"] == "http") {
res.redirect("https://[your url goes here]" + req.url, next);
} else {
return next();
}
});
This script while loading the page saves the URL page and checks if the address is https or http. If it is http, the script automatically redirects you to the same https page
(function(){
var link = window.location.href;
if(link[4] != "s"){
var clink = "";
for (let i = 4; i < link.length; i++) {
clink += link[i];
}
window.location.href = "https" + clink;
}
})();
You can instantiate 2 Node.js servers - one for HTTP and HTTPS
You can also define a setup function that both servers will execute, so that you don't have to write much duplicated code.
Here's the way I did it: (using restify.js, but should work for express.js, or node itself too)
http://qugstart.com/blog/node-js/node-js-restify-server-with-both-http-and-https/
Updated code for jake's answer. Run this alongside your https server.
// set up plain http server
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
// set up a route to redirect http to https
app.get('*', function(req, res) {
res.redirect('https://' + req.headers.host + req.url);
})
// have it listen on 80
server.listen(80);
This works with express for me:
app.get("*",(req,res,next) => {
if (req.headers["x-forwarded-proto"]) {
res.redirect("https://" + req.headers.host + req.url)
}
if (!res.headersSent) {
next()
}
})
Put this before all HTTP handlers.
This worked for me:
/* Headers */
require('./security/Headers/HeadersOptions').Headers(app);
/* Server */
const ssl = {
key: fs.readFileSync('security/ssl/cert.key'),
cert: fs.readFileSync('security/ssl/cert.pem')
};
//https server
https.createServer(ssl, app).listen(443, '192.168.1.2' && 443, '127.0.0.1');
//http server
app.listen(80, '192.168.1.2' && 80, '127.0.0.1');
app.use(function(req, res, next) {
if(req.secure){
next();
}else{
res.redirect('https://' + req.headers.host + req.url);
}
});
Recommend add the headers before redirect to https
Now, when you do:
curl http://127.0.0.1 --include
You get:
HTTP/1.1 302 Found
//
Location: https://127.0.0.1/
Vary: Accept
Content-Type: text/plain; charset=utf-8
Content-Length: 40
Date: Thu, 04 Jul 2019 09:57:34 GMT
Connection: keep-alive
Found. Redirecting to https://127.0.0.1/
I use express 4.17.1
The idea is to check if the incoming request is made with https, if so simply don't redirect it again to https but continue as usual. Else, if it is http, redirect it with appending https.
app.use (function (req, res, next) {
if (req.secure) {
next();
} else {
res.redirect('https://' + req.headers.host + req.url);
}
});
From the long years of reseaching for a perfect redirection from http to https, I've found the perfect solution here.
const http = require("http");
const https = require("https");
const { parse } = require("url");
const next = require("next");
const fs = require("fs");
const ports = {
http: 3000,
https: 3001
}
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
const httpsOptions = {
key: fs.readFileSync("resources/certificates/localhost-key.pem"),
cert: fs.readFileSync("resources/certificates/localhost.pem")
};
// Automatic HTTPS connection/redirect with node.js/express
// source: https://stackoverflow.com/questions/7450940/automatic-https-
connection-redirect-with-node-js-express
app.prepare().then(() => {
// Redirect from http port to https
http.createServer(function (req, res) {
res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(ports.http, ports.https) + req.url });
console.log("http request, will go to >> ");
console.log("https://" + req.headers['host'].replace(ports.http, ports.https) + req.url);
res.end();
}).listen(ports.http, (err) => {
if (err) throw err;
console.log("ready - started server on url: http://localhost:" + ports.http);
});
https.createServer(httpsOptions, (req, res) => {
const parsedUrl = parse(req.url, true);
handle(req, res, parsedUrl);
}).listen(ports.https, (err) => {
if (err) throw err;
console.log("ready - started server on url: https://localhost:" + ports.https);
});
});
My case i must change also the port and listen both ports:
appr.get("/", (req, res) => {
res.redirect('https://' + req.headers['host'].replace(PORT, PORTS) + req.url);
});
if your node application install on IIS you can do like this in web.config
<configuration>
<system.webServer>
<!-- indicates that the hello.js file is a node.js application
to be handled by the iisnode module -->
<handlers>
<add name="iisnode" path="src/index.js" verb="*" modules="iisnode" />
</handlers>
<!-- use URL rewriting to redirect the entire branch of the URL namespace
to hello.js node.js application; for example, the following URLs will
all be handled by hello.js:
http://localhost/node/express/myapp/foo
http://localhost/node/express/myapp/bar
-->
<rewrite>
<rules>
<rule name="HTTPS force" enabled="true" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="^OFF$" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" redirectType="Permanent" />
</rule>
<rule name="sendToNode">
<match url="/*" />
<action type="Rewrite" url="src/index.js" />
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<add segment="node_modules" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
</configuration>

express node-http-proxy , proxy.proxyRequest to https server?

Is it possible to use the node-http-prox module and proxy.proxyRequest to a https server?
I tried to do the following but doesnt seem to work.
app.get('/c/users/moreuser', function(req, res) {
proxy.proxyRequest(req, res, {
host: 'api.example.com',
port: 80,
https: true
});
});
doing this i dont get any response from the server. But i can get response directly from the server.
try port: 443, which is the default port for HTTPS.

node http proxy doesent route to host.com but instead host.com#localhost?

Going nuts on..
This
var app = express(),
proxy = new httpProxy.HttpProxy({
target: {
host: 'host.com',
port: 5280 // Port of XMPP server
}
});
..
// Proxy BOSH request to XMPP server
app.all('/http-bind', function(req, res) {
util.puts('Request successfully proxied: ' + req.url);
util.puts(JSON.stringify(req.headers, true, 2));
proxy.proxyRequest(req, res);
});
results in
body rid="3965133021" xmlns="http://jabber.org/protocol/httpbind" to="host.com#localhost" .. etc
host.com#localhost? that localhost has to go but cannot get it off ;9
How can that be?! it should go to host.com instead! spend an hour looking trying, anyone knows whats wrong?
This was a local issue with strophe framework. its fixed now.

Default route using node-http-proxy?

I want to do a simple node.js reverse proxy to host multiple Node.JS applications along with my apache server on the same port 80. So I found this example here
var http = require('http')
, httpProxy = require('http-proxy');
httpProxy.createServer({
hostnameOnly: true,
router: {
'www.my-domain.com': '127.0.0.1:3001',
'www.my-other-domain.de' : '127.0.0.1:3002'
}
}).listen(80);
The problem is that I want to have for example app1.my-domain.com pointing to localhost:3001, app2.my-domain.com pointing to localhost:3002, and all other go to port 3000 for example, where my apache server will be running. I couldn't find anything in the documentation on how to have a "default" route.
Any ideas?
EDIT I want to do that because I have a lot of domains/subdomains handled by my apache server and I don't want to have to modify this routing table each time I have want to add a new subdomain.
For nearly a year, I had successfully used the accepted answer to have a default host, but there's a much simpler way now that node-http-proxy allows for RegEx in the host table.
var httpProxy = require('http-proxy');
var options = {
// this list is processed from top to bottom, so '.*' will go to
// '127.0.0.1:3000' if the Host header hasn't previously matched
router : {
'example.com': '127.0.0.1:3001',
'sample.com': '127.0.0.1:3002',
'^.*\.sample\.com': '127.0.0.1:3002',
'.*': '127.0.0.1:3000'
}
};
// bind to port 80 on the specified IP address
httpProxy.createServer(options).listen(80, '12.23.34.45');
The requires that you do NOT have hostnameOnly set to true, otherwise the RegEx would not be processed.
This isn't baked into node-http-proxy, but it's simple to code:
var httpProxy = require('http-proxy'),
http = require('http'),
addresses;
// routing hash
addresses = {
'localhost:8000': {
host: 'localhost',
port: 8081
},
'local.dev:8000': {
host: 'localhost',
port: 8082
},
'default': {
host: 'xkcd.com',
port: 80
}
};
// create servers on localhost on ports specified by param
function createLocalServer(ports) {
ports.forEach(function(port) {
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('<h1>Hello from ' + port + '</h1');
}).listen(port);
});
console.log('Servers up on ports ' + ports.join(',') + '.');
}
createLocalServer([8081, 8082]);
console.log('======================================\nRouting table:\n---');
Object.keys(addresses).forEach(function(from) {
console.log(from + ' ==> ' + addresses[from].host + ':' + addresses[from].port);
});
httpProxy.createServer(function (req, res, proxy) {
var target;
// if the host is defined in the routing hash proxy to it
// else proxy to default host
target = (addresses[req.headers.host]) ? addresses[req.headers.host] : addresses.default;
proxy.proxyRequest(req, res, target);
}).listen(8000);
If you visit localhost on port 8000 it will proxy to localhost port 8081.
If you visit 127.0.0.1 on port 8000 (which is not defined in our routing hash) it will go to the default 'location', namely xkcd.com on port 80.

Automatic HTTPS connection/redirect with node.js/express

I've been trying to get HTTPS set up with a node.js project I'm working on. I've essentially followed the node.js documentation for this example:
// curl -k https://localhost:8000/
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};
https.createServer(options, function (req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000);
Now, when I do
curl -k https://localhost:8000/
I get
hello world
as expected. But if I do
curl -k http://localhost:8000/
I get
curl: (52) Empty reply from server
In retrospect this seems obvious that it would work this way, but at the same time, people who eventually visit my project aren't going to type in https://yadayada, and I want all traffic to be https from the moment they hit the site.
How can I get node (and Express as that is the framework I'm using) to hand off all incoming traffic to https, regardless of whether or not it was specified? I haven't been able to find any documentation that has addressed this. Or is it just assumed that in a production environment, node has something that sits in front of it (e.g. nginx) that handles this kind of redirection?
This is my first foray into web development, so please forgive my ignorance if this is something obvious.
Ryan, thanks for pointing me in the right direction. I fleshed out your answer (2nd paragraph) a little bit with some code and it works. In this scenario these code snippets are put in my express app:
// set up plain http server
var http = express();
// set up a route to redirect http to https
http.get('*', function(req, res) {
res.redirect('https://' + req.headers.host + req.url);
// Or, if you don't want to automatically detect the domain name from the request header, you can hard code it:
// res.redirect('https://example.com' + req.url);
})
// have it listen on 8080
http.listen(8080);
The https express server listens ATM on 3000. I set up these iptables rules so that node doesn't have to run as root:
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 3000
All together, this works exactly as I wanted it to.
To prevent theft of cookies over HTTP, see this answer (from the comments) or use this code:
const session = require('cookie-session');
app.use(
session({
secret: "some secret",
httpOnly: true, // Don't let browser javascript access cookies.
secure: true, // Only use cookies over https.
})
);
Thanks to this guy:
https://www.tonyerwin.com/2014/09/redirecting-http-to-https-with-nodejs.html
If secure, requests via https, otherwise redirects to https
app.enable('trust proxy')
app.use((req, res, next) => {
req.secure ? next() : res.redirect('https://' + req.headers.host + req.url)
})
If you follow conventional ports since HTTP tries port 80 by default and HTTPS tries port 443 by default you can simply have two server's on the same machine:
Here's the code:
var https = require('https');
var fs = require('fs');
var options = {
key: fs.readFileSync('./key.pem'),
cert: fs.readFileSync('./cert.pem')
};
https.createServer(options, function (req, res) {
res.end('secure!');
}).listen(443);
// Redirect from http port 80 to https
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(301, { "Location": "https://" + req.headers['host'] + req.url });
res.end();
}).listen(80);
Test with https:
$ curl https://127.0.0.1 -k
secure!
With http:
$ curl http://127.0.0.1 -i
HTTP/1.1 301 Moved Permanently
Location: https://127.0.0.1/
Date: Sun, 01 Jun 2014 06:15:16 GMT
Connection: keep-alive
Transfer-Encoding: chunked
More details : Nodejs HTTP and HTTPS over same port
With Nginx you can take advantage of the "x-forwarded-proto" header:
function ensureSec(req, res, next){
if (req.headers["x-forwarded-proto"] === "https"){
return next();
}
res.redirect("https://" + req.headers.host + req.url);
}
As of 0.4.12 we have no real clean way of listening for HTTP & HTTPS on the same port using Node's HTTP/HTTPS servers.
Some people have solved this issue by having having Node's HTTPS server (this works with Express.js as well) listen to 443 (or some other port) and also have a small http server bind to 80 and redirect users to the secure port.
If you absolutely have to be able to handle both protocols on a single port then you need to put nginx, lighttpd, apache, or some other web server on that port and have act as a reverse proxy for Node.
I use the solution proposed by Basarat but I also need to overwrite the port because I used to have 2 different ports for HTTP and HTTPS protocols.
res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(http_port,https_port) + req.url });
I prefer also to use not standard port so to start nodejs without root privileges.
I like 8080 and 8443 because I came from lots of years of programming on tomcat.
My complete file become
var fs = require('fs');
var http = require('http');
var http_port = process.env.PORT || 8080;
var app = require('express')();
// HTTPS definitions
var https = require('https');
var https_port = process.env.PORT_HTTPS || 8443;
var options = {
key : fs.readFileSync('server.key'),
cert : fs.readFileSync('server.crt')
};
app.get('/', function (req, res) {
res.send('Hello World!');
});
https.createServer(options, app).listen(https_port, function () {
console.log('Magic happens on port ' + https_port);
});
// Redirect from http port to https
http.createServer(function (req, res) {
res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(http_port,https_port) + req.url });
console.log("http request, will go to >> ");
console.log("https://" + req.headers['host'].replace(http_port,https_port) + req.url );
res.end();
}).listen(http_port);
Then I use iptable for forwording 80 and 443 traffic on my HTTP and HTTPS ports.
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8443
You can use the express-force-https module:
npm install --save express-force-https
var express = require('express');
var secure = require('express-force-https');
var app = express();
app.use(secure);
I find req.protocol works when I am using express (have not tested without but I suspect it works). using current node 0.10.22 with express 3.4.3
app.use(function(req,res,next) {
if (!/https/.test(req.protocol)){
res.redirect("https://" + req.headers.host + req.url);
} else {
return next();
}
});
This answer needs to be updated to work with Express 4.0. Here is how I got the separate http server to work:
var express = require('express');
var http = require('http');
var https = require('https');
// Primary https app
var app = express()
var port = process.env.PORT || 3000;
app.set('env', 'development');
app.set('port', port);
var router = express.Router();
app.use('/', router);
// ... other routes here
var certOpts = {
key: '/path/to/key.pem',
cert: '/path/to/cert.pem'
};
var server = https.createServer(certOpts, app);
server.listen(port, function(){
console.log('Express server listening to port '+port);
});
// Secondary http app
var httpApp = express();
var httpRouter = express.Router();
httpApp.use('*', httpRouter);
httpRouter.get('*', function(req, res){
var host = req.get('Host');
// replace the port in the host
host = host.replace(/:\d+$/, ":"+app.get('port'));
// determine the redirect destination
var destination = ['https://', host, req.url].join('');
return res.redirect(destination);
});
var httpServer = http.createServer(httpApp);
httpServer.listen(8080);
If your app is behind a trusted proxy (e.g. an AWS ELB or a correctly configured nginx), this code should work:
app.enable('trust proxy');
app.use(function(req, res, next) {
if (req.secure){
return next();
}
res.redirect("https://" + req.headers.host + req.url);
});
Notes:
This assumes that you're hosting your site on 80 and 443, if not, you'll need to change the port when you redirect
This also assumes that you're terminating the SSL on the proxy. If you're doing SSL end to end use the answer from #basarat above. End to end SSL is the better solution.
app.enable('trust proxy') allows express to check the X-Forwarded-Proto header
you can use "net" module to listening for HTTP & HTTPS on the same port
var https = require('https');
var http = require('http');
var fs = require('fs');
var net=require('net');
var handle=net.createServer().listen(8000)
var options = {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};
https.createServer(options, function (req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(handle);
http.createServer(function(req,res){
res.writeHead(200);
res.end("hello world\n");
}).listen(handle)
Most answers here suggest to use the req.headers.host header.
The Host header is required by HTTP 1.1, but it is actually optional since the header might not be actually sent by a HTTP client, and node/express will accept this request.
You might ask: which HTTP client (e.g: browser) can send a request missing that header? The HTTP protocol is very trivial. You can craft a HTTP request in few lines of code, to not send a host header, and if each time you receive a malformed request you throw an exception, and depending on how you handle such exceptions, this can take your server down.
So always validate all input. This is not paranoia, I have received requests lacking the host header in my service.
Also, never treat URLs as strings. Use the node url module to modify specific parts of a string. Treating URLs as strings can be exploited in many many many ways. Don't do it.
var express = require('express');
var app = express();
app.get('*',function (req, res) {
res.redirect('https://<domain>' + req.url);
});
app.listen(80);
This is what we use and it works great!
This worked for me:
app.use(function(req,res,next) {
if(req.headers["x-forwarded-proto"] == "http") {
res.redirect("https://[your url goes here]" + req.url, next);
} else {
return next();
}
});
This script while loading the page saves the URL page and checks if the address is https or http. If it is http, the script automatically redirects you to the same https page
(function(){
var link = window.location.href;
if(link[4] != "s"){
var clink = "";
for (let i = 4; i < link.length; i++) {
clink += link[i];
}
window.location.href = "https" + clink;
}
})();
You can instantiate 2 Node.js servers - one for HTTP and HTTPS
You can also define a setup function that both servers will execute, so that you don't have to write much duplicated code.
Here's the way I did it: (using restify.js, but should work for express.js, or node itself too)
http://qugstart.com/blog/node-js/node-js-restify-server-with-both-http-and-https/
Updated code for jake's answer. Run this alongside your https server.
// set up plain http server
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
// set up a route to redirect http to https
app.get('*', function(req, res) {
res.redirect('https://' + req.headers.host + req.url);
})
// have it listen on 80
server.listen(80);
This works with express for me:
app.get("*",(req,res,next) => {
if (req.headers["x-forwarded-proto"]) {
res.redirect("https://" + req.headers.host + req.url)
}
if (!res.headersSent) {
next()
}
})
Put this before all HTTP handlers.
This worked for me:
/* Headers */
require('./security/Headers/HeadersOptions').Headers(app);
/* Server */
const ssl = {
key: fs.readFileSync('security/ssl/cert.key'),
cert: fs.readFileSync('security/ssl/cert.pem')
};
//https server
https.createServer(ssl, app).listen(443, '192.168.1.2' && 443, '127.0.0.1');
//http server
app.listen(80, '192.168.1.2' && 80, '127.0.0.1');
app.use(function(req, res, next) {
if(req.secure){
next();
}else{
res.redirect('https://' + req.headers.host + req.url);
}
});
Recommend add the headers before redirect to https
Now, when you do:
curl http://127.0.0.1 --include
You get:
HTTP/1.1 302 Found
//
Location: https://127.0.0.1/
Vary: Accept
Content-Type: text/plain; charset=utf-8
Content-Length: 40
Date: Thu, 04 Jul 2019 09:57:34 GMT
Connection: keep-alive
Found. Redirecting to https://127.0.0.1/
I use express 4.17.1
The idea is to check if the incoming request is made with https, if so simply don't redirect it again to https but continue as usual. Else, if it is http, redirect it with appending https.
app.use (function (req, res, next) {
if (req.secure) {
next();
} else {
res.redirect('https://' + req.headers.host + req.url);
}
});
From the long years of reseaching for a perfect redirection from http to https, I've found the perfect solution here.
const http = require("http");
const https = require("https");
const { parse } = require("url");
const next = require("next");
const fs = require("fs");
const ports = {
http: 3000,
https: 3001
}
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
const httpsOptions = {
key: fs.readFileSync("resources/certificates/localhost-key.pem"),
cert: fs.readFileSync("resources/certificates/localhost.pem")
};
// Automatic HTTPS connection/redirect with node.js/express
// source: https://stackoverflow.com/questions/7450940/automatic-https-
connection-redirect-with-node-js-express
app.prepare().then(() => {
// Redirect from http port to https
http.createServer(function (req, res) {
res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(ports.http, ports.https) + req.url });
console.log("http request, will go to >> ");
console.log("https://" + req.headers['host'].replace(ports.http, ports.https) + req.url);
res.end();
}).listen(ports.http, (err) => {
if (err) throw err;
console.log("ready - started server on url: http://localhost:" + ports.http);
});
https.createServer(httpsOptions, (req, res) => {
const parsedUrl = parse(req.url, true);
handle(req, res, parsedUrl);
}).listen(ports.https, (err) => {
if (err) throw err;
console.log("ready - started server on url: https://localhost:" + ports.https);
});
});
My case i must change also the port and listen both ports:
appr.get("/", (req, res) => {
res.redirect('https://' + req.headers['host'].replace(PORT, PORTS) + req.url);
});
if your node application install on IIS you can do like this in web.config
<configuration>
<system.webServer>
<!-- indicates that the hello.js file is a node.js application
to be handled by the iisnode module -->
<handlers>
<add name="iisnode" path="src/index.js" verb="*" modules="iisnode" />
</handlers>
<!-- use URL rewriting to redirect the entire branch of the URL namespace
to hello.js node.js application; for example, the following URLs will
all be handled by hello.js:
http://localhost/node/express/myapp/foo
http://localhost/node/express/myapp/bar
-->
<rewrite>
<rules>
<rule name="HTTPS force" enabled="true" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{HTTPS}" pattern="^OFF$" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}{REQUEST_URI}" redirectType="Permanent" />
</rule>
<rule name="sendToNode">
<match url="/*" />
<action type="Rewrite" url="src/index.js" />
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<add segment="node_modules" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
</configuration>

Resources