Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Heroku redirect Next.js React client app http to https

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:

  • this popular question says to check for the x-forwarded-proto value:
/* 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 */
})
  • and others suggest using a package like express-sslify or heroku-ssl-redirect.

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?

like image 517
5tormTrooper Avatar asked Jul 02 '19 11:07

5tormTrooper


2 Answers

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.

like image 91
João Cunha Avatar answered Nov 01 '22 12:11

João Cunha


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({});
like image 2
NSjonas Avatar answered Nov 01 '22 14:11

NSjonas