使用 node.js/express 自动 HTTPS 连接/重定向

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7450940/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 14:34:53  来源:igfitidea点击:

Automatic HTTPS connection/redirect with node.js/express

node.jshttpsexpress

提问by Jake

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 documentationfor this example:

我一直在尝试使用我正在处理的 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);

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.

回想起来,这似乎很明显它会以这种方式工作,但与此同时,最终访问我的项目的人不会输入https://yadayada,而且我希望从他们点击的那一刻起所有流量都是 https网站。

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?

我怎样才能让节点(和 Express 作为我正在使用的框架)将所有传入流量移交给 https,无论是否指定?我找不到任何解决此问题的文档。或者只是假设在生产环境中,节点前面有一些东西(例如 nginx)来处理这种重定向?

This is my first foray into web development, so please forgive my ignorance if this is something obvious.

这是我第一次涉足 Web 开发,所以如果这很明显,请原谅我的无知。

回答by Jake

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:

瑞安,谢谢你给我指明了正确的方向。我用一些代码充实了你的答案(第 2 段),并且它有效。在这种情况下,这些代码片段放在我的 express 应用程序中:

// 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://' + 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:

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

All together, this works exactly as I wanted it to.

总之,这完全符合我的要求。

回答by basarat

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:

如果您使用传统端口,因为 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);

Test with https:

用 https 测试:

$ curl https://127.0.0.1 -k
secure!

With http:

使用 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

更多细节:Nodejs HTTP 和 HTTPS 在同一端口上

回答by Jeremias Binder

Thanks to this guy: https://www.tonyerwin.com/2014/09/redirecting-http-to-https-with-nodejs.html

感谢这个人:https: //www.tonyerwin.com/2014/09/redirecting-http-to-https-with-nodejs.html

app.use (function (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
                res.redirect('https://' + req.headers.host + req.url);
        }
});

回答by ccyrille

With Nginx you can take advantage of the "x-forwarded-proto" header:

使用 Nginx,您可以利用“x-forwarded-proto”标头:

function ensureSec(req, res, next){
    if (req.headers["x-forwarded-proto"] === "https"){
       return next();
    }
    res.redirect("https://" + req.headers.host + req.url);  
}

回答by Ryan Olds

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.

从 0.4.12 开始,我们没有真正干净的方法来使用 Node 的 HTTP/HTTPS 服务器在同一端口上侦听 HTTP 和 HTTPS。

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.

有些人通过让 Node 的 HTTPS 服务器(这也适用于 Express.js)侦听 443(或其他一些端口)并使用一个小的 http 服务器绑定到 80 并将用户重定向到安全端口来解决这个问题。

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.

如果您绝对必须能够在单个端口上处理这两种协议,那么您需要将 nginx、lighttpd、apache 或其他一些 Web 服务器放在该端口上,并充当 Node.js 的反向代理。

回答by Mateus Ferreira

You can use the express-force-httpsmodule:

您可以使用express-force-https模块:

npm install --save express-force-https

npm install --save express-force-https

var express = require('express');
var secure = require('express-force-https');

var app = express();
app.use(secure);

回答by NoBrainer

This answer needs to be updated to work with Express 4.0. Here is how I got the separate http server to work:

需要更新此答案才能与 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);

回答by Lorenzo Eccher

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.

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

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.

我也更喜欢使用非标准端口,以便在没有 root 权限的情况下启动 nodejs。我喜欢 8080 和 8443,因为我在 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.

然后我使用 iptable 在我的 HTTP 和 HTTPS 端口上转发 80 和 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

回答by Jamie McCrindle

If your app is behind a trusted proxy (e.g. an AWS ELB or a correctly configured nginx), this code should work:

如果您的应用程序位于受信任的代理(例如 AWS ELB 或正确配置的 nginx)之后,则此代码应该可以工作:

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
  • 这假设您在 80 和 443 上托管您的网站,否则,您需要在重定向时更改端口
  • 这还假设您要终止代理上的 SSL。如果您正在端到端地进行 SSL,请使用上面@basarat 的答案。端到端 SSL 是更好的解决方案。
  • app.enable('trust proxy') 允许 express 检查 X-Forwarded-Proto 标头

回答by Catalyst

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

我发现 req.protocol 在我使用 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();
  } 
});