Search code examples
reactjssslherokuhttpsnext.js

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:

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


Solution

  • 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.