自动HTTPS连接/redirect与node.js / express
我一直在尝试使用正在处理的node.js项目来设置HTTPS。 我基本上遵循这个例子的node.js文档 :
// 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);
现在,当我这样做
curl -k https://localhost:8000/
我明白了
hello world
如预期。 但是,如果我这样做
curl -k http://localhost:8000/
我明白了
curl: (52) Empty reply from server
回想起来,这似乎很明显,它会这样工作,但同时,最终访问我的项目的人不会打字https :// yadayada,我希望所有的stream量从他们打的时候都是https网站。
我怎么能得到节点(和快递,因为这是我使用的框架)切换到https的所有传入stream量,无论是否指定? 我一直没能find解决这个问题的任何文件。 还是假设在生产环境中,节点有一些位于它前面的东西(例如nginx)来处理这种redirect?
这是我第一次进入网页开发,所以请原谅我的无知,如果这是明显的东西。
瑞恩,谢谢你指出我在正确的方向。 我用一些代码充实了你的答案(第二段),它起作用。 在这种情况下,这些代码片段放在我的快速应用程序中:
// set up plain http server var http = express.createServer(); // set up a route to redirect http to https http.get('*',function(req,res){ res.redirect('https://mydomain.com'+req.url) }) // have it listen on 8080 http.listen(8080);
https express服务器在3000上监听ATM。我设置了这些iptables规则,以便节点不必以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
所有这一切,这完全符合我的意图。
如果遵循传统的端口,因为默认情况下HTTP尝试端口80,HTTPS默认情况下尝试端口443,您可以简单地在同一台机器上有两台服务器:代码如下:
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);
用https:
$ curl https://127.0.0.1 -k secure!
用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
更多细节: 通过同一端口的Nodejs HTTP和HTTPS
使用Nginx,你可以利用“x-forward-proto”头文件:
function ensureSec(req, res, next){ if (req.headers["x-forwarded-proto"] === "https"){ return next(); } res.redirect("https://" + req.headers.host + req.url); }
从0.4.12开始,我们没有使用Node的HTTP / HTTPS服务器在同一个端口上监听HTTP和HTTPS的真正的干净方式。
有些人已经通过让Node的HTTPS服务器(也适用于Express.js)监听443(或其他某个端口),并且还有一个小的http服务器绑定到80并将用户redirect到安全端口来解决此问题。
如果你必须能够在一个端口上同时处理这两个协议,那么你需要在该端口上放置nginx,lighttpd,apache或其他一些web服务器,并作为Node的反向代理。
这个答案需要更新才能使用Express 4.0。 这是我如何得到单独的http服务器工作:
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);
当我使用express时,我发现req.protocol有效(没有testing,但是我怀疑它是有效的)。 使用当前节点0.10.22与快车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(); } });
这里的大多数答案build议使用req.headers.host标头。
Host头是HTTP 1.1所必需的,但它实际上是可选的,因为头可能实际上不是由HTTP客户端发送的,并且node / express将接受这个请求。
您可能会问:哪个HTTP客户端(例如:浏览器)可以发送缺less该标头的请求? HTTP协议是非常微不足道的。 您可以在几行代码中创build一个HTTP请求,不发送主机头,如果每次收到格式错误的请求,都会抛出一个exception,根据处理这些exception的方式,这可能会导致服务器closures。
所以总是validation所有的input 。 这不是偏执狂,我收到了我的服务中缺less主机头的请求。
另外, 绝对不要把URL当作string 。 使用节点url模块修改string的特定部分。 将URL作为string处理可以在许多方面被利用。 不要这样做。
您可以使用“net”模块在同一端口上侦听HTTP和HTTPS
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)
这对我工作:
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(); } });
您可以实例化2个Node.js服务器 – 一个用于HTTP和HTTPS
您还可以定义两个服务器都将执行的设置函数,以便您不必编写大量重复的代码。
这是我做的方式:(使用restify.js,但应该为express.js或节点本身工作)
http://qugstart.com/blog/node-js/node-js-restify-server-with-both-http-and-https/
var express = require('express'); var app = express(); app.get('*',function (req, res) { res.redirect('https://<domain>' + req.url); }); app.listen(80);
这是我们使用,它很好!
如果您的应用程序位于受信任的代理(例如,AWS ELB或正确configuration的nginx)之后,则此代码应该可以工作:
app.enable('trust proxy'); app.use(function(req, res, next) { if (req.secure){ return next(); } res.redirect("https://" + req.headers.host + req.url); });
笔记:
- 这假设你在80和443上托pipe你的站点,如果没有,你需要在redirect时更改端口
- 这也假定你正在代理上终止SSL。 如果你端到端使用SSL @basarat的答案。 端到端SSL是更好的解决scheme。
- app.enable('trust proxy')允许express来检查X-Forwarded-Proto头
我使用Basarat提出的解决scheme,但我也需要覆盖端口,因为我曾经有2个不同的端口用于HTTP和HTTPS协议。
res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(http_port,https_port) + req.url });
我更喜欢使用不标准的端口,以启动没有root权限的nodej。 我喜欢8080和8443,因为我来自多年的tomcat编程。
我的完整文件成为
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);
然后,我使用iptable在我的HTTP和HTTPS端口上为80和443stream量进行stream量分类。
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