node.js 如何在 Express.js 中强制使用 SSL/https
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8605720/
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
How to force SSL / https in Express.js
提问by Elias
I am trying to create a middleware for Express.js to redirect all non-secure (port 80) traffic to the secured SSL port (443). Unfortunately there is no information in an Express.js request that lets you determine if the request comes over http or https.
我正在尝试为 Express.js 创建一个中间件,以将所有非安全(端口 80)流量重定向到安全 SSL 端口(443)。不幸的是,Express.js 请求中没有信息可以让您确定请求是通过 http 还是 https 来的。
One solution would be to redirect everyrequest but this is not an option for me.
一种解决方案是重定向每个请求,但这对我来说不是一个选择。
Notes:
笔记:
There is no possibility to handle it with Apache or something else. It hasto be done in node.
Only oneserver can be fired up in the application.
没有可能用 Apache 或其他东西来处理它。它必须在节点中完成。
在应用程序中只能启动一台服务器。
How would you solve that?
你会怎么解决呢?
采纳答案by Elias
Since I was working on nginx, I had access to the header's x-forwarded-proto property so I could write a tiny middleware to redirect all traffic as described here: http://elias.kg/post/14971446990/force-ssl-with-express-js-on-heroku-nginx
由于我在 nginx 上工作,因此我可以访问标头的 x-forwarded-proto 属性,因此我可以编写一个小中间件来重定向所有流量,如下所述:http: //elias.kg/post/14971446990/force-ssl- with-express-js-on-heroku-nginx
Edit: Updated the url
编辑:更新了网址
回答by Lavamantis
Just in case you're hosting on Heroku and just want to redirect to HTTPS regardless of port, here's the middleware solution we're using.
以防万一您在 Heroku 上托管并且只想重定向到 HTTPS 而不管端口如何,这里是我们正在使用的中间件解决方案。
It doesn't bother to redirect if you're developing locally.
如果您在本地开发,则无需重定向。
function requireHTTPS(req, res, next) {
// The 'x-forwarded-proto' check is for Heroku
if (!req.secure && req.get('x-forwarded-proto') !== 'https' && process.env.NODE_ENV !== "development") {
return res.redirect('https://' + req.get('host') + req.url);
}
next();
}
You can use it with Express (2.x and 4.x) like so:
您可以将它与 Express(2.x 和 4.x)一起使用,如下所示:
app.use(requireHTTPS);
回答by MeetM
Although the question looks a year old, I would like to answer as it might help others. Its actually really simple with the latest version of expressjs (2.x). First create the key and cert using this code
虽然这个问题看起来已经有一年了,但我还是想回答一下,因为它可能会帮助其他人。使用最新版本的expressjs(2.x)实际上非常简单。首先使用此代码创建密钥和证书
openssl genrsa -out ssl-key.pem 1024
openssl genrsa -out ssl-key.pem 1024
$ openssl req -new -key ssl-key.pem -out certrequest.csr.. bunch of prompts
$ openssl req -new -key ssl-key.pem -out certrequest.csr..一堆提示
$ openssl x509 -req -in certrequest.csr -signkey ssl-key.pem -out ssl-cert.pem
$ openssl x509 -req -in certrequest.csr -signkey ssl-key.pem -out ssl-cert.pem
Store the cert and key files in the folder containing app.js. Then edit the app.js file and write the following code before express.createServer()
将证书和密钥文件存储在包含 app.js 的文件夹中。然后编辑app.js文件,在express.createServer()之前编写如下代码
var https = require('https');
var fs = require('fs');
var sslkey = fs.readFileSync('ssl-key.pem');
var sslcert = fs.readFileSync('ssl-cert.pem')
var options = {
key: sslkey,
cert: sslcert
};
Now pass the optionsobject in the createServer() function
现在options在 createServer() 函数中传递对象
express.createServer(options);
Done!
完毕!
回答by Peter Lyons
First, let me see if I can clarify the problem. You are limited to one (1) node.js process, but that process can listen on two (2) network ports, both 80 and 443, right? (When you say oneserver it's not clear if you mean one process or only one network port).
首先,让我看看我是否可以澄清问题。您仅限于一 (1) 个 node.js 进程,但该进程可以侦听两 (2) 个网络端口,80 和 443,对吗?(当你说一台服务器时,不清楚你是指一个进程还是只有一个网络端口)。
Given that constraint, you problem seems to be, for reasons you don't provide, somehow your clients are connecting to the wrong port. This is a bizarre edge case because by default, clients will make HTTP requests to port 80 and HTTPS to port 443. And when I say "by default", I mean if no specific ports are included in the URLs. So unless you are explicitly using criss-crossed URLs like http://example.com:443and https://example.com:80, you really shouldn't have any criss-crossed traffic hitting your site. But since you asked the question, I guess you must have it, although I bet you are using non-standard ports as opposed to the 80/443 defaults.
鉴于这种限制,您的问题似乎是,由于您没有提供的原因,您的客户端以某种方式连接到错误的端口。这是一个奇怪的边缘情况,因为默认情况下,客户端会将 HTTP 请求发送到端口 80,将 HTTPS 请求发送到端口 443。当我说“默认”时,我的意思是 URL 中没有包含特定端口。因此,除非您明确使用像http://example.com:443和https://example.com:80这样的交叉网址,否则您的网站不应该有任何交叉的流量。但是既然你问了这个问题,我想你一定有它,尽管我敢打赌你使用的是非标准端口而不是 80/443 默认端口。
So, for background: YES some web servers handle this reasonably well. For example, if you do http://example.com:443to nginx, it will respond with an HTTP 400 "Bad Request" response indicating "The plain HTTP request was sent to HTTPS port". YES, you can listen on both 80 and 443 from the same node.js process. You just need to create 2 separate instances of express.createServer(), so that's no problem. Here's a simple program to demonstrate handling both protocols.
因此,对于背景:是的,某些 Web 服务器可以很好地处理此问题。例如,如果您对 nginx执行http://example.com:443,它将响应 HTTP 400“Bad Request”响应,指示“纯 HTTP 请求已发送到 HTTPS 端口”。是的,您可以在同一个 node.js 进程中同时监听 80 和 443。您只需要创建 2 个单独的实例express.createServer(),所以这没问题。这是一个简单的程序来演示处理这两种协议。
var fs = require("fs");
var express = require("express");
var http = express.createServer();
var httpsOptions = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
};
var https = express.createServer(httpsOptions);
http.all('*', function(req, res) {
console.log("HTTP: " + req.url);
return res.redirect("https://" + req.headers["host"] + req.url);
});
http.error(function(error, req, res, next) {
return console.log("HTTP error " + error + ", " + req.url);
});
https.error(function(error, req, res, next) {
return console.log("HTTPS error " + error + ", " + req.url);
});
https.all('*', function(req, res) {
console.log("HTTPS: " + req.url);
return res.send("Hello, World!");
});
http.listen(80);
And I can test this via cURL like this:
我可以像这样通过 cURL 进行测试:
$ curl --include --silent http://localhost/foo
HTTP/1.1 302 Moved Temporarily
X-Powered-By: Express
Content-Type: text/html
Location: https://localhost/foo
Connection: keep-alive
Transfer-Encoding: chunked
<p>Moved Temporarily. Redirecting to <a href="https://localhost/foo">https://localhost/foo</a></p>
$ curl --include --silent --insecure https://localhost:443/foo
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 13
Connection: keep-alive
Hello, World!%
And showing the redirect from HTTP to HTTPS...
并显示从 HTTP 到 HTTPS 的重定向...
curl --include --silent --location --insecure 'http://localhost/foo?bar=bux'
HTTP/1.1 302 Moved Temporarily
X-Powered-By: Express
Content-Type: text/html
Location: https://localhost/foo?bar=bux
Connection: keep-alive
Transfer-Encoding: chunked
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 13
Connection: keep-alive
Hello, World!%
So that will work to serve both protocols for the regular case and redirect properly. However, criss-crosses don't work at all. I believe a criss-crossed request hitting an express server isn't going to get routed through the middleware stack because it will be treated as an error from the very beginning and won't even get the request URI parsed properly, which is necessary to send it through the route middleware chain. The express stack doesn't even get them I think because they are not valid requests, so they get ignored somewhere in the node TCP stack. It's probably possible to write a server to do this, and there may already be a module out there, but you'd have to write it at the TCP layer directly. And you'd have to detect a regular HTTP request in the first chunk of client data that hits the TCP port and wire that connection to an HTTP server instead of your normal TLS handshake.
因此,这将适用于常规情况下的两种协议并正确重定向。但是,纵横交错根本不起作用。我相信击中 express 服务器的纵横交错的请求不会通过中间件堆栈路由,因为它会从一开始就被视为错误,甚至不会正确解析请求 URI,这是必要的通过路由中间件链发送它。我认为快速堆栈甚至没有得到它们,因为它们不是有效的请求,因此它们在节点 TCP 堆栈中的某处被忽略。可能可以编写一个服务器来执行此操作,并且可能已经有一个模块,但是您必须直接在 TCP 层编写它。和你'
When I do either of these, my express error handlers do NOT get called.
当我执行其中任何一个操作时,我的快速错误处理程序都不会被调用。
curl --insecure https://localhost:80/foo
curl: (35) Unknown SSL protocol error in connection to localhost:80
curl http://localhost:443/foo
curl: (52) Empty reply from server
回答by Jonathan Tran
Based on Elias's answer but with inline code. This works if you have node behind nginx or a load balancer. Nginx or the load balancer will always hit node with plain old http, but it sets a header so you can distinguish.
基于 Elias 的回答,但带有内联代码。如果您在 nginx 或负载均衡器后面有节点,则此方法有效。Nginx 或负载均衡器将始终使用普通的旧 http 命中节点,但它会设置一个标头,以便您可以区分。
app.use(function(req, res, next) {
var schema = req.headers['x-forwarded-proto'];
if (schema === 'https') {
// Already https; don't do anything special.
next();
}
else {
// Redirect to https.
res.redirect('https://' + req.headers.host + req.url);
}
});
回答by TJ Holowaychuk
http.createServer(app.handle).listen(80)
http.createServer(app.handle).listen(80)
https.createServer(options, app.handle).listen(443)
https.createServer(options, app.handle).listen(443)
for express 2x
快递 2x
回答by Ashok Kumawat
Try this example :
试试这个例子:
var express = require('express');
var app = express();
// set up a route to redirect http to https
app.use(function (req, res, next) {
if (!/https/.test(req.protocol)) {
res.redirect("https://" + req.headers.host + req.url);
} else {
return next();
}
});
var webServer = app.listen(port, function () {
console.log('Listening on port %d', webServer.address().port);
});
回答by robocat
This code looks like it does what you need: https://gist.github.com/903596
此代码看起来可以满足您的需求:https: //gist.github.com/903596

