smkarber
smkarber

Reputation: 607

Why cannot I redirect my React app on Heroku from http to https?

I have an app on Heroku that was created using create-react-app. Just today, I got an SSL cert using Heroku's automated(-ish) SSL cert process ExpeditedSSL, and the documentation then suggests rerouting all http requests to https.

I have a server.js file and express I use just to attempt to run middleware and then serve my React app.

I know the SSL cert is working as if I go to https://myapp.com I see my Heroku app, but when I go to http://myapp.com it is not redirected to the https version of my Heroku app.

I have tried many, many solutions today from StackOverflow, Google, and otherwise and none of the the solutions have worked for me. I don't get any errors, either. It just doesn't work.

Attempt using https library:

const https = require("https");
const express = require('express');
const app = express();

app.use(express.static(path.join(__dirname, 'build')));

app.get('/', function (req, res) {
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

https.createServer(app).listen(3000);

Another attempt using heroku-ssl-redirect:

var sslRedirect = require('heroku-ssl-redirect');
var express = require('express');
var app = express();

// enable ssl redirect
app.use(sslRedirect(['production'], 301));

app.use(express.static(path.join(__dirname, 'build')));

app.get('*', (req, res, next) => {
  if (req.headers['x-forwarded-proto'] != 'https'){
    res.redirect('https://' + req.hostname + req.url);
  } else {
    next();
  }
});

app.get('/', function (req, res) {
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.listen(process.env.PORT || 3000);

Attempt using x-forward-proto:

const express = require('express');
const env = process.env.NODE_ENV || 'development';
const bodyParser = require('body-parser');
const path = require('path');
const app = express();

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

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

app.use(express.static(path.join(__dirname, 'build')));

app.get('/', function (req, res) {
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.listen(process.env.PORT || 8080);

I've also attempted a number of random node installs from various blogs and SO posts, and nothing has worked. Without any errors I am having a hard time figuring out why this doesn't work.

Upvotes: 16

Views: 6986

Answers (5)

atazmin
atazmin

Reputation: 5687

This seem to work for me React on Heroku

Buildpack https://github.com/mars/create-react-app-buildpack

root/static.json

{
  "root": "build/",
  "routes": {
    "/**": "index.html"
  },
  "https_only": true
}

https://youtu.be/LAjj_kzqbjw

Upvotes: 0

Frederiko Ribeiro
Frederiko Ribeiro

Reputation: 2358

UPDATED and SECURE solution below:

Since you are deploying your react app via Heroku using create-react-app and it's buidlpack (recommended, if you are not: create-react-app-buildpack). So as the official docs says:

The web server may be configured via the static buildpack.

The config file static.json should be committed at the root of the repo. It will not be recognized, if this file in a sub-directory

The default static.json, if it does not exist in the repo, is:

{
  "root": "build/",
  "routes": {
    "/**": "index.html"
  }
}

HTTPS-only

Enforce secure connections by automatically redirecting insecure requests to https://, in static.json:

{
  "root": "build/",
  "routes": {
    "/**": "index.html"
  },
  "https_only": true
}

The method by @misterfoxy works but is not the right way!

Upvotes: 10

iMyke
iMyke

Reputation: 613

Try adding this to your index.html file Place it in the header, so it would run before other scripts

<script>
const domain1 = "www.example.com";
const domain2 = "example.com";
const host = window.location.host;
if ((domain === host || domain2 === host) && (window.location.protocol !== "https:")){
  window.location.href = "https://www.example.com";
}
</script>

Upvotes: 0

misterfoxy
misterfoxy

Reputation: 194

Try adding the following to the header of your index.html file

<script>
var host = "www.URL.com" || "URL.com";
if ((host == window.location.host) && (window.location.protocol != "https:")){
window.location.protocol = "https";
}
</script>

I was dealing with a similar issue and this allowed me to resolve it. Placing it in the header ensures that the script will run before any of your bundled JavaScript, but I suppose it would work above your bundle.js file

Upvotes: 17

Yoni Rabinovitch
Yoni Rabinovitch

Reputation: 5261

Try using express-https-redirect.

Install with:

npm install express-https-redirect --save

Then you should be able to do something like:

const express = require('express');
const httpsRedirect = require('express-https-redirect');
const app = express();
app.use('/', httpsRedirect());
app.set('port', process.env.PORT || 3000);
app.use(express.static(path.join(__dirname, 'build')));

app.get('/', function (req, res) {
    res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.listen(app.get('port'), function () {
        console.log('Express server listening on port ' + app.get('port'));
});

Upvotes: 0

Related Questions