【问题标题】:Heroku NodeJS http to https ssl forced redirectHeroku NodeJS http 到 https ssl 强制重定向
【发布时间】:2011-11-03 08:18:05
【问题描述】:

我在 Heroku 上使用 Express.js 在 Node.js 上使用 https 启动并运行了一个应用程序。如何识别协议以在 Heroku 上使用 Node.js 强制重定向到 https

我的应用程序只是一个简单的http-server,它(还)没有意识到 Heroku 正在发送它https-requests:

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

【问题讨论】:

  • Heroku 支持回答了我上面的问题,我发现这里还没有发布,所以我想我会公开发布并分享知识。他们传递了大量关于原始请求的信息,其请求标头以“x-”为前缀。这是我现在使用的代码(在我的路由定义的顶部):app.get('*',function(req,res,next){ if(req.headers['x-forwarded-proto']!='https') res.redirect('https://mypreferreddomain.com'+req.url) else next() })
  • 好的,我知道你像这样检查 https 并在需要时重定向。但是有没有办法与您的域名提供商在 dns 级别进行重新路由。因此,在浏览器解析 DNS 之前,它已经位于 https。因为使用这种方法,我认为鉴于我对重定向的了解,一旦请求是通过 http 发出的,然后又是通过 https 发出的。因此,如果发送了敏感数据,则通过 http 发送一次。然后通过https。这有点违背了目的。如果我错了,请告诉我。
  • @MuhammadUmer,你的推理在这里似乎是正确的,你有没有发现更多?
  • 我只是使用 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

标签: redirect ssl node.js https heroku


【解决方案1】:

截至今天,2014 年 10 月 10 日,使用 Heroku Cedar 堆栈ExpressJS ~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 以及任何其他策略,例如:

'*': ['authenticated', 'enforceSsl']

【讨论】:

  • 关于使用sails 策略的注意事项:如sailsjs.org/#/documentation/concepts/Policies 中所述:“默认策略映射不会“级联”或“涓滴”。控制器操作的指定映射将覆盖默认映射。 "这意味着,一旦您有针对特定控制器/操作的其他策略,您就必须确保在这些控制器/操作上添加“enforceSsl”。
  • "下表列出了 Express 4 中其他小的但重要的变化: ... app.configure() 函数已被删除。使用 process.env.NODE_ENV 或 app.get('env ') 功能来检测环境并相应地配置应用程序。"
  • 另外,请注意 res.redirect 这默认为 302 重定向(至少在 express 4.x 中)。出于 SEO 和缓存的原因,您可能需要 301 重定向。将对应行替换为return res.redirect(301, ['https://', req.get('Host'), req.url].join(''));
  • 注意:在Express 4.x中,去掉app.configure这一行,只使用内层药水。 app.configure 是遗留代码,不再包含在 express 中。
【解决方案2】:

答案是使用 Heroku 转发的“x-forwarded-proto”的标头,因为它是代理 thingamabob。 (旁注:它们还传递了其他几个可能很方便的 x- 变量,check them out)。

我的代码:

/* 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 小时的延迟,这让我无法回答自己的问题。

【讨论】:

  • 这不会让GET以外的其他方法通过吗?
  • @Aaron:好吧,如果你透明地重定向一个 POST 请求,你可能会丢失信息。我认为您应该在其他请求上返回 400,而不是 GET 的 http。
  • 如果您只希望它在您的生产环境中工作,您可以将&& process.env.NODE_ENV === "production" 添加到您的条件中。
  • 307(使用相同方法重定向)可能比 400 错误更好。
  • 这个答案有多个问题,请参阅下面的下一个答案 (stackoverflow.com/a/23894573/14193) 并给这个答案打分。
【解决方案3】:

接受的答案中有一个硬编码的域,如果您在多个域(例如:dev-yourapp.com、test-yourapp.com、yourapp.com)上有相同的代码,这不是很好。

改用这个:

/* Redirect http to https */
app.get("*", function (req, res, next) {

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

});

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

【讨论】:

  • 效果很好。我不知道为什么我不得不用req.headers.host 替换req.hostname,也许是我在4.2 中的表达版本
【解决方案4】:

我编写了一个小型节点模块,用于在 express 项目上强制执行 SSL。它既适用于标准情况,也适用于反向代理(Heroku、nodejitsu 等)

https://github.com/florianheinemann/express-sslify

【讨论】:

    【解决方案5】:

    如果您想测试本地主机上的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;
      }
    }
    

    这里的重要一点是您将所有请求代理到 localhost 端口 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
    

    【讨论】:

      【解决方案6】:

      你应该看看heroku-ssl-redirect。它就像一个魅力!

      var sslRedirect = require('heroku-ssl-redirect');
      var express = require('express');
      var app = express();
      
      // enable ssl redirect
      app.use(sslRedirect());
      
      app.get('/', function(req, res){
        res.send('hello world');
      });
      
      app.listen(3000);
      

      【讨论】:

        【解决方案7】:

        如果您结合使用 cloudflare.com 作为 CDN 和 heroku,您可以像这样在 cloudflare 中轻松启用自动 ssl 重定向:

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

        2. 选择页面规则

        3. 添加您的域,例如www.example.com 和 switch 总是使用 https 来打开

        【讨论】:

          【解决方案8】:

          Loopback 用户可以使用稍作修改的 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());
          

          【讨论】:

            【解决方案9】:

            这是一种更具体的 Express 方式。

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

            【讨论】:

              【解决方案10】:

              我正在使用 Vue、Heroku 并且遇到了同样的问题:

              我更新了我的 server.js 如下,我不再碰它,因为它正在工作:):

              const serveStatic = require('serve-static')
              const sts = require('strict-transport-security');
              const path = require('path')
              
              var express = require("express");
              
              require("dotenv").config();
              var history = require("connect-history-api-fallback");
              
              const app = express()
              const globalSTS = sts.getSTS({'max-age':{'days': 365}});
              app.use(globalSTS);
              
              app.use(
                history({
                  verbose: true
                })
              );
              
              app.use((req, res, next) => {
                if (req.header('x-forwarded-proto') !== 'https') {
                  res.redirect(`https://${req.header('host')}${req.url}`)
                } else {
                  next();
                }
              });
              
              app.use('/', serveStatic(path.join(__dirname, '/dist')));
              app.get(/.*/, function (req, res) {
              res.sendFile(path.join(__dirname, '/dist/index.html'))
              })
              
              const port = process.env.PORT || 8080
              app.listen(port)
              console.log(`app is listening on port: ${port}`)
              

              【讨论】:

                【解决方案11】:
                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 */
                  }
                });
                

                【讨论】:

                  【解决方案12】:

                  带有 app.use 和动态 url。为我在本地和 Heroku 上工作

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

                  【讨论】:

                    【解决方案13】:

                    正如 Derek 指出的那样,在 Heroku 上检查 X-Forwarded-Proto 标头中的协议可以正常工作。对于它的价值,here is a gist 我使用的 Express 中间件及其相应的测试。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2016-11-14
                      • 2018-05-12
                      • 2020-05-28
                      • 1970-01-01
                      • 2020-07-23
                      • 2014-08-29
                      • 2017-03-26
                      • 2014-09-18
                      相关资源
                      最近更新 更多