14

I have an express server deployed on Heroku: https://server.mydomain.com

and a Next.js React app also deployed on Heroku: https://app.mydomain.com

Both have their SSL certificates automatically configured by Heroku, and when I visit the https domains, they work as expected.

The problem I have is that when I visit http://app.mydomain.com, it does not redirect to https://app.mydomain.com.

All the solutions I've found online point to forcing SSL on the server:

/* 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://app.mydomain.com'+req.url)
 else
   next() /* Continue to other routes if we're not redirecting */
})

These solutions work fine for the server requests, but loading a React client page does not necessarily trigger app.get(). Obviously, a React client can run independently of a server.

So the question is: How does someone force https for a subdomain Next.js React client app on Heroku? Without using express server methods?

5tormTrooper
  • 893
  • 7
  • 21
  • Are you paying for your dynos? – Jake Luby Jul 08 '19 at 19:37
  • I have two apps deployed to Heroku, one on a paid account, and one on a non-paid account. The paid one will automatically re-route to https out of the box, the free one does not. – Jake Luby Jul 09 '19 at 14:14
  • Yes. Both are paid. – 5tormTrooper Jul 09 '19 at 15:14
  • What does your Procfile look like? – Jake Luby Jul 09 '19 at 15:40
  • I don't have one. – 5tormTrooper Jul 10 '19 at 07:57
  • Where do you load the React app from? is it from http://app.mydomain.com? – kkkkkkk Jul 10 '19 at 09:50
  • Yes. The React (Next.js) app is hosted on it's own app on a hobby dyno with a custom domain. – 5tormTrooper Jul 10 '19 at 16:12
  • 1
    Slept on it and `Obviously, a React client can run independently of a server.` sticks out. A client needs a server and heroku is serving up your static folder most likely via a node server (I think that's the default web server). What you'll need to do is build an express app that serves up your static build files instead and have a Procfile that tells heroku to run node against that express app instead of directly against your react app. – Jake Luby Jul 10 '19 at 18:43
  • @JakeLuby this sounds like a good route, but I'm not sure how exactly to do that. I have the Next.js app, and deploy using Heroku's cli. How exactly do you build an express server that serves it up? Another app in Heroku? – 5tormTrooper Jul 12 '19 at 17:04
  • If it helps, https://stackoverflow.com/questions/26489519/how-to-redirect-to-https-with-htaccess-on-heroku-cedar-stack – Sakul Budhathoki Jul 13 '19 at 02:32

2 Answers2

7

I do this in one of my production applications.

We prepare the next app object and init an express server. This is done in the server.js file. You can read more about it in the docs about a custom server.

Next.js also has an example in their github in the examples folder about a custom express server. It's here.

const express = require('express');
const next = require('next');

const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });

const handle = app.getRequestHandler();

app
  .prepare()
  .then(() => {

    const server = express();

    server.use((req, res, next) => {
      const hostname = req.hostname === 'www.app.domain.com' ? 'app.domain.com' : req.hostname;

      if (req.headers['x-forwarded-proto'] === 'http' || req.hostname === 'www.app.domain.com') {
        res.redirect(301, `https://${hostname}${req.url}`);
        return;
      }

      res.setHeader('strict-transport-security', 'max-age=31536000; includeSubDomains; preload');
      next();
    });

    server.get('*', (req, res) => handle(req, res));

    server.listen(
      4242,
      error => {
        if (error) throw error;
        console.error('Listening on port 4242');
      }
    );

  })
  .catch(error => {
    console.error(error);
    process.exit(1);
  });

As for deploying to Heroku you should be able to just customize the npm start script to start nextjs like so:

"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start"
}

Heroku also runs npm run build automatically so it should build the app for you.

João Cunha
  • 9,929
  • 4
  • 40
  • 61
2

Heroku does not currently "offer out of the box" functionality to force the use of https for node apps.

However, with the release of Nextjs v12 you can accomplish this without having to setup a custom server and use middleware instead.

See this answer for example code and advantages of middleware vs custom server.

I also published a npm package to handle this:

import sslRedirect from 'next-ssl-redirect-middleware';
export default sslRedirect({});
NSjonas
  • 10,693
  • 9
  • 66
  • 92