我一直在尝试用我正在做的一个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,我希望所有的流量从他们访问网站的那一刻起就使用https。

我怎么能得到节点(和Express,因为这是我正在使用的框架)把所有传入的流量交给https,不管它是否被指定?我还没有找到任何解决这个问题的文档。或者只是假设在生产环境中,节点在它前面有一些东西(例如nginx)来处理这种重定向?

这是我第一次尝试web开发,所以如果这是显而易见的,请原谅我的无知。


当前回答

我使用Basarat提出的解决方案,但我还需要覆盖端口,因为我曾经有2个不同的端口用于HTTP和HTTPS协议。

res.writeHead(301, { "Location": "https://" + req.headers['host'].replace(http_port,https_port) + req.url });

我也喜欢使用非标准端口,这样就可以在没有root权限的情况下启动nodejs。 我喜欢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端口上forword80和443流量。

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

其他回答

我找到了req。协议工作时,我使用express(没有测试,但我怀疑它工作)。使用当前节点0.10.22和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();
  } 
});

如果你遵循传统端口,因为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通过相同的端口

这个脚本在加载页面时保存URL页面,并检查地址是https还是http。如果是http,脚本会自动将您重定向到相同的https页面

(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;
  }
})();

这个答案需要更新以与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);

使用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);  
}