I recently ran into the case where I wanted to ensure all traffic to my NextJS site was happening over HTTPS. Since my site is deployed using next start
on heroku, I ended up implementing this using a custom server that checks all requests are https before handling them.
Here is the completed server.js
implementation.
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
const port = process.env.PORT
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url, true)
if (!dev && req.headers['x-forwarded-proto'] != 'https') {
const { host } = parse(process.env.HOSTNAME)
res.writeHead(302, {
Location: `https://${host}${req.url}`
})
res.end()
} else {
handle(req, res, parsedUrl)
}
}).listen(port, err => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
The redirect only fires in production mode and it uses a environment variable that contains the servers hostname. You'll notice that it does not check the requests URL or protocol. This is because all traffic from Heroku's router to your server happens over HTTP. You can see if the request is https by checking the value of the x-forwarded-proto
header that Heroku adds.
You'll also have to update your start script to run your server and not the nextjs default.
...
"scripts": {
"dev": "next",
"build": "next build",
"export": "next export",
"start": "NODE_ENV=production node server.js"
}
...
Notes and Limitations:
- This does not support sites hosted using
next export
- Changes to
server.js
are not hot reloaded.