Heroku NodeJS http到https ssl强制重定向


105

我在heroku上运行了一个应用程序,并在带有https的节点上运行了express。我如何识别协议以在heroku上使用nodejs强制重定向到https?

我的应用程序只是一个简单的http服务器,它(尚未)意识到heroku正在向其发送https请求:

/* Heroku provides the port they want you on in this environment variable (hint: it's not 80) */
app.listen(process.env.PORT || 3000);

6
Heroku的支持人员回答了我上面的问题,但是我还没有发现它已经发布在这里,所以我认为我应该公开发布它并分享知识。他们传递了大量有关原始请求的信息,该请求的请求标头带有“ x-”前缀。这是我现在使用的代码(在路线定义的顶部):app.get('*',function(req,res,next){ if(req.headers['x-forwarded-proto']!='https') res.redirect('https://mypreferreddomain.com'+req.url) else next() })
Derek Bredensteiner

1
好的,这样我就可以像这样检查https并根据需要重定向。但是有没有一种方法可以与您的域名提供商在dns级别进行重新路由。因此,在浏览器解析DNS之前,它已经位于https上。因为采用这种方法,所以我认为鉴于我对重定向的了解,一旦请求通过HTTP发出,然后再次通过https发出。因此,如果发送了敏感数据,则会通过http发送一次。然后通过https。哪一个失败了。如果我错了,请告诉我。
穆罕默德·乌默尔

@MuhammadUmer,您的推理在这里似乎很棘手,您有没有发现更多?
卡罗(Karoh)2013年

我只是简单地将cloudflare用作了可以用作nginx的名称服务器,并且让我仅通过单击切换按钮即可重定向到ssl版本。您也可以执行以下操作:developer.mozilla.org/en-US/docs/Web/HTTP/Headers / ... 此外,通常没有人立即发送数据,他们通常登陆表单然后提交。因此,在服务器端代码,dns服务器,http标头,javascript上,您可以检查并重定向到https developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
Muhammad Umer

Answers:


107

截至今天(2014年10月10日),使用Heroku Cedar stackExpressJS〜3.4.4,这是一个工作代码集。

这里要记住的主要事情是我们正在部署到Heroku。SSL终止发生在负载平衡器上,然后加密的流量才到达您的节点应用程序。可以使用req.headers ['x-forwarded-proto'] ==='https'测试是否使用https发出请求。

如果您在其他环境中托管,则无需担心在应用程序等内部拥有本地SSL证书。但是,如果使用自己的证书,子域等,则应首先通过Heroku加载项应用SSL加载项。

然后只需添加以下内容即可完成从HTTPS到HTTPS以外的任何重定向。这与上面接受的答案非常接近,但是:

  1. 确保您使用“ app.use”(用于所有操作,而不仅仅是获取)
  2. 明确将forceSsl逻辑外部化为声明的函数
  3. 不要将“ *”与“ app.use”一起使用-当我测试它时,这实际上失败了。
  4. 在这里,我只想在生产中使用SSL。(根据您的需要进行更改)

码:

 var express = require('express'),
   env = process.env.NODE_ENV || 'development';

 var forceSsl = function (req, res, next) {
    if (req.headers['x-forwarded-proto'] !== 'https') {
        return res.redirect(['https://', req.get('Host'), req.url].join(''));
    }
    return next();
 };

 app.configure(function () {

    if (env === 'production') {
        app.use(forceSsl);
    }

    // other configurations etc for express go here...
}

对SailsJS(0.10.x)用户的注释。您可以在api / policies内简单地创建一个策略(enforceSsl.js):

module.exports = function (req, res, next) {
  'use strict';
  if ((req.headers['x-forwarded-proto'] !== 'https') && (process.env.NODE_ENV === 'production')) {
    return res.redirect([
      'https://',
      req.get('Host'),
      req.url
    ].join(''));
  } else {
    next();
  }
};

然后从config / policies.js以及其他任何策略中进行引用,例如:

'*':['已验证”,'enforceSsl']


1
关于使用Sails策略的注释:如sailsjs.org/#/documentation/concepts/Policies中所述:“默认策略映射不会“级联”或“滴流”。控制器操作的指定映射将覆盖默认映射。 ” 这意味着,一旦您对特定控制器/操作有其他策略,则必须确保在这些控制器/操作上添加“ enforceSsl”。
曼努埃尔·达尔沃

2
“下表列出了Express 4中的其他一些小的但重要的更改:... app.configure()函数已被删除。使用process.env.NODE_ENV或app.get('env')函数来检测环境和相应地配置应用程序。”
凯文·惠勒

9
另外,请注意,res.redirect这默认为302重定向(至少在express 4.x中)。出于SEO和缓存原因,您可能希望改用301重定向。将相应的行替换为return res.redirect(301, ['https://', req.get('Host'), req.url].join(''));
Kevin Wheeler

6
注意:在中Express 4.x,删除app.configure行并仅使用内部药水。 app.configure是旧版代码,不再包含在express中。
Augie Gardner

96

答案是使用Heroku向前传递的“ x-forwarded-proto”标头,因为它是代理thingamabob。(旁注:它们也传递了其他一些可能方便的x-变量,请检查出来)。

我的代码:

/* At the top, with other redirect methods before other routes */
app.get('*',function(req,res,next){
  if(req.headers['x-forwarded-proto']!='https')
    res.redirect('https://mypreferreddomain.com'+req.url)
  else
    next() /* Continue to other routes if we're not redirecting */
})

感谢Brandon,我只是在等待那6个小时的延迟,这让我无法回答自己的问题。


4
这会不会让其他方法GET通过?
杰德·施密特

1
@Aaron:好吧,如果您透明地重定向POST请求,您可能会丢失信息。我认为您应该针对GET以外的其他请求返回400。
theodorton 2014年

3
&& process.env.NODE_ENV === "production"如果只希望在生产环境中使用它,则可能会陷入困境。
keepitreal 2014年

307(使用相同方法重定向)可能要好于400错误。
贝尼·切尔尼亚夫斯基-帕斯金

此答案有多个问题,请参阅下面的下一个答案(stackoverflow.com/a/23894573/14193)并将其评分。
尼尔

22

接受的答案中包含一个硬编码域,如果您在多个域(例如:dev-yourapp.com,test-yourapp.com,yourapp.com)上具有相同的代码,则该域就不太好。

使用此代替:

/* Redirect http to https */
app.get('*', function(req,res,next) {
  if(req.headers['x-forwarded-proto'] != 'https' && process.env.NODE_ENV === 'production')
    res.redirect('https://'+req.hostname+req.url)
  else
    next() /* Continue to other routes if we're not redirecting */
});

https://blog.mako.ai/2016/03/30/redirect-http-to-https-on-heroku-and-node-generally/


效果不错。我不知道为什么我只需req.hostname要用req.headers.hostExpress版本代替我就在4.2
Jeremy Piednoel


6

如果要测试x-forwarded-proto本地主机上的标头,则可以使用nginx设置一个虚拟主机文件,该文件代理对节点应用程序的所有请求。您的nginx vhost配置文件可能如下所示

NginX

server {
  listen 80;
  listen 443;

  server_name dummy.com;

  ssl on;
  ssl_certificate     /absolute/path/to/public.pem;
  ssl_certificate_key /absolute/path/to/private.pem;

  access_log /var/log/nginx/dummy-access.log;
  error_log /var/log/nginx/dummy-error.log debug;

  # node
  location / {
    proxy_pass http://127.0.0.1:3000/;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

这里重要的一点是,您正在将所有请求代理到本地主机端口3000(这是您的节点应用程序运行的位置),并且您正在设置一堆标头,包括 X-Forwarded-Proto

然后在您的应用中照常检测该标头

表达

var app = express()
  .use(function (req, res, next) {
    if (req.header('x-forwarded-proto') == 'http') {
      res.redirect(301, 'https://' + 'dummy.com' + req.url)
      return
    }
    next()
  })

考阿

var app = koa()
app.use(function* (next) {
  if (this.request.headers['x-forwarded-proto'] == 'http') {
    this.response.redirect('https://' + 'dummy.com' + this.request.url)
    return
  }
  yield next
})

主持人

最后,您必须将此行添加到hosts文件中

127.0.0.1 dummy.com


4

如果将cloudflare.com作为CDN与heroku结合使用,则可以在cloudflare中轻松启用自动ssl重定向,如下所示:

  1. 登录并转到您的仪表板

  2. 选择页面规则

    选择页面规则

  3. 添加您的域,例如www.example.com并切换始终使用https启用 切换始终使用https启用

3

环回用户可以使用稍作调整的arcseldon Answer版本作为中间件:

服务器/中间件/forcessl.js

module.exports = function() {  
  return function forceSSL(req, res, next) {
    var FORCE_HTTPS = process.env.FORCE_HTTPS || false;
      if (req.headers['x-forwarded-proto'] !== 'https' && FORCE_HTTPS) {
        return res.redirect(['https://', req.get('Host'), req.url].join(''));
      }
      next();
    };
 };

服务器/ server.js

var forceSSL = require('./middleware/forcessl.js');
app.use(forceSSL());

2

这是更快速的方法。

app.enable('trust proxy');
app.use('*', (req, res, next) => {
  if (req.secure) {
    return next();
  }
  res.redirect(`https://${req.hostname}${req.url}`);
});

0
app.all('*',function(req,res,next){
  if(req.headers['x-forwarded-proto']!='https') {
    res.redirect(`https://${req.get('host')}`+req.url);
  } else {
    next(); /* Continue to other routes if we're not redirecting */
  }
});

0

带有app.use和动态网址。我可以在本地和Heroku上使用

app.use(function (req, res, next) {
  if (req.header('x-forwarded-proto') === 'http') {
    res.redirect(301, 'https://' + req.hostname + req.url);
    return
  }
  next()
});

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.