Heroku NodeJS http 到 https ssl 强制重定向

发布于 2024-12-01 10:35:09 字数 418 浏览 0 评论 0原文

我有一个应用程序在 Heroku 上启动并运行,并在 Node.js 上使用 Express.js 和 https 。如何在 Heroku 上使用 Node.js 识别强制重定向到 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);

I have an application up and running on Heroku with Express.js on Node.js with https. How do I identify the protocol to force a redirect to https with Node.js on Heroku?

My app is just a simple http-server, it doesn't (yet) realize Heroku is sending it 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);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

水染的天色ゝ 2024-12-08 10:35:09

截至今天,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']

As of today, 10th October 2014, using Heroku Cedar stack, and ExpressJS ~3.4.4, here is a working set of code.

The main thing to remember here is that we ARE deploying to Heroku. SSL termination happens at the load balancer, before encrypted traffic reaches your node app. It is possible to test whether https was used to make the request with req.headers['x-forwarded-proto'] === 'https'.

We don't need to concern ourselves with having local SSL certificates inside the app etc as you might if hosting in other environments. However, you should get a SSL Add-On applied via Heroku Add-ons first if using your own certificate, sub-domains etc.

Then just add the following to do the redirect from anything other than HTTPS to HTTPS.
This is very close to the accepted answer above, but:

  1. Ensures you use "app.use" (for all actions, not just get)
  2. Explicitly externalises the forceSsl logic into a declared function
  3. Does not use '*' with "app.use" - this actually failed when I
    tested it.
  4. Here, I only want SSL in production. (Change as suits your needs)

Code:

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

Note for SailsJS (0.10.x) users. You can simply create a policy (enforceSsl.js) inside api/policies:

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

Then reference from config/policies.js along with any other policies, e.g:

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

世界等同你 2024-12-08 10:35:09

答案是使用 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 */
})

谢谢布兰登,只是在等待 6 小时的延迟,这让我无法回答自己的问题。

The answer is to use the header of 'x-forwarded-proto' that Heroku passes forward as it does it's proxy thingamabob. (side note: They pass several other x- variables too that may be handy, check them out).

My code:

/* 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 */
})

Thanks Brandon, was just waiting for that 6 hour delay thing that wouldn't let me answer my own question.

无人接听 2024-12-08 10:35:09

接受的答案中有一个硬编码的域,如果您在多个域上有相同的代码(例如: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/

The accepted answer has a hardcoded domain in it, which isn't too good if you have the same code on several domains (eg: dev-yourapp.com, test-yourapp.com, yourapp.com).

Use this instead:

/* 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/

一抹微笑 2024-12-08 10:35:09

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

https://github.com/florianheinemann/快速sslify

I've written a small node module that enforces SSL on express projects. It works both in standard situations and in case of reverse proxies (Heroku, nodejitsu, etc.)

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

夜夜流光相皎洁 2024-12-08 10:35:09

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

然后在你的应用程序中像往常一样检测该标头

Express

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

Koa

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

最后你必须将此行添加到你的 hosts 文件中

127.0.0.1 dummy.com

If you want to test out the x-forwarded-proto header on your localhost, you can use nginx to setup a vhost file that proxies all of the requests to your node app. Your nginx vhost config file might look like this

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

The important bits here are that you are proxying all requests to localhost port 3000 (this is where your node app is running) and you are setting up a bunch of headers including X-Forwarded-Proto

Then in your app detect that header as usual

Express

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

Koa

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

Finally you have to add this line to your hosts file

127.0.0.1 dummy.com
椵侞 2024-12-08 10:35:09

您应该查看 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);

You should take a look at heroku-ssl-redirect. It works like a charm!

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);
后知后觉 2024-12-08 10:35:09

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

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

  2. 选择页面规则

    选择页面规则

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

If you are using cloudflare.com as CDN in combination with heroku, you can enable automatic ssl redirect within cloudflare easily like this:

  1. Login and go to your dashboard

  2. Select Page Rules

    Select Page Rules

  3. Add your domain, e.g. www.example.com and switch always use https to on
    Switch always use https to on
掌心的温暖 2024-12-08 10:35:09

Loopback 用户可以使用 arcseldon 答案的稍微修改版本作为中间件:

server/middleware/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/server.js

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

Loopback users can use a slightly adapted version of arcseldon answer as middleware:

server/middleware/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/server.js

var forceSSL = require('./middleware/forcessl.js');
app.use(forceSSL());
你在看孤独的风景 2024-12-08 10:35:09

这是一种更 Express 特定的方法来执行此操作。

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

This is a more Express specific way to do this.

app.enable('trust proxy');
app.use('*', (req, res, next) => {
  if (req.secure) {
    return next();
  }
  res.redirect(`https://${req.hostname}${req.url}`);
});
写下不归期 2024-12-08 10:35:09

我正在使用 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}`)

I am using Vue, Heroku and had same problem :

I updated my server.js as below, and i am not touching it anymore because it is working :) :

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}`)
極樂鬼 2024-12-08 10:35:09
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 */
  }
});
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 */
  }
});
南街九尾狐 2024-12-08 10:35:09

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

With app.use and dynamic url. Works both localy and on Heroku for me

app.use(function (req, res, next) {
  if (req.header('x-forwarded-proto') === 'http') {
    res.redirect(301, 'https://' + req.hostname + req.url);
    return
  }
  next()
});
小霸王臭丫头 2024-12-08 10:35:09

正如 Derek 指出的那样,检查 X-Forwarded-Proto 标头中的协议在 Heroku 上运行良好。无论如何,这里是我使用的 Express 中间件及其相应测试的要点

Checking the protocol in the X-Forwarded-Proto header works fine on Heroku, just like Derek has pointed out. For what it's worth, here is a gist of the Express middleware that I use and its corresponding test.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文