Edit: The ExpressJs solution below won't work if you want to host it on Vercel for example which costs a bit but offers the best Next.js hosting with built-in Edge Caching and image scaling and optimisations. So if you want to host it on Vercel than add authentication to every single route using: https://nextjs.org/docs/authentication (might be no small feet unless done from the start of the project).
If you however intend to host it somewhere else that supports Node.js and Express.js hosting (and offers nothing special for Next.js) then the below is a great solution.
ExpressJs solution:
This post doesn't cover storing of Basic Auth credentials or using multiple credentials. It's more of a starting point because I couldn't find anything at all on google.
We'll be using Express.js to host NextJS (in production mode!) in both examples below.
To start the server in both examples: node index.mjs (currently using node -v 14.9.0)
First: create index.mjs file in the root.
(Option 1) Using latest NextJS:
- Use latest (9.5 as of writing) NextJS + Express.js node server (Latest nextjs has fixed problems (I haven't verified) with CSP
https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
- Inside index.mjs add the following code (install required modules):
import next from 'next'
import express from 'express'
import auth from 'basic-auth'
const dev = process.env.NODE_ENV === 'development'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare()
.then(() => {
const server = express()
server.use(function (req, res, next) {
const credentials = auth(req)
if (!credentials || credentials.name !== '[change_me_username]' || credentials.pass !== '[change_me_password]') {
res.status(401)
res.header('WWW-Authenticate', 'Basic realm="example"')
res.send('Access denied')
} else {
next()
}
});
server.get('*', (req, res) => {
return handle(req, res)
})
const port = 80;
server.listen(port, (err) => {
if (err) {
throw err
}
console.log(`Ready on http://localhost:${port}`)
})
})
.catch((ex) => {
console.error(ex.stack)
process.exit(1)
})
(Option 2) Using NextJS 9.4 (if you can't upgrade)
This answer does not deal with potential ramifications from potential CSP security wholes, best and easiest way to deal with that is to upgrade to NextJs 9.5 period.
- Inside index.mjs add the following code (install required modules):
import next from 'next'
import express from 'express'
import auth from 'basic-auth'
const dev = process.env.NODE_ENV === 'development'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare()
.then(() => {
const server = express()
server.use(function (req, res, next) {
const credentials = auth(req)
if (!credentials || credentials.name !== '[change_me_username]' || credentials.pass !== '[change_me_password]') {
res.status(401)
res.header('WWW-Authenticate', 'Basic realm="example"')
res.send('Access denied')
} else {
// this is different from above NextJS 9.5, 9.4 requires inline js.
res.setHeader(
'Content-Security-Policy',
"default-src * 'self' data: 'unsafe-inline' *"
);
next()
}
});
server.get('*', (req, res) => {
return handle(req, res)
})
const port = 80;
server.listen(port, (err) => {
if (err) {
throw err
}
console.log(`Ready on http://localhost:${port}`)
})
})
.catch((ex) => {
console.error(ex.stack)
process.exit(1)
})
- Inside
next.config.js if you use next-images, otherwise just put the object: module.exports = { // ... }
const withImages = require('next-images')
module.exports = withImages({
async headers() {
return [
{
source: '/:path*',
headers: [
{
// // https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
key: 'Content-Security-Policy',
value: "default-src * 'self' data: 'unsafe-inline' *"
}
]
}
]
}
})