Summary
This guide will teach you a cross-platform solution on how to add local SSL in a Next.js project.
The name and file structure used here is not mandatory. You can name files or reorganize the folder structure however you see fit.
Before we start
We'll create a new Next.js project using npx create-next-app
.
Step 1
We'll use devcert to automatically generate certificates for us. It automates almost the whole process and works on every SO. Refer to its documentation to see how it works.
Add it to your project as a devDependency.
yarn add devcert --dev
Step 2
Create a scripts
folder in your project.
Create a new file create-ssl-certs.js
inside the scripts folder:
const devcert = require('devcert');
const fs = require('fs');
if (!fs.existsSync('./certs')) {
fs.mkdirSync('./certs');
}
const domains = ['my-cool-domain.local'];
devcert
.certificateFor(domains, { getCaPath: true })
.then(({ key, cert, caPath }) => {
fs.writeFileSync('./certs/devcert.key', key);
fs.writeFileSync('./certs/devcert.cert', cert);
fs.writeFileSync('./certs/.capath', caPath);
})
.catch(console.error);
Change my-cool-domain.local
to the domain you wish to use. You can pass multiple domains if needed.
The script automatically creates a certs
folder and generates the certificate files, plus another file with devcert's caPath, which we'll need in Step 6.
Step 3
Add a new script to your package.json
:
"ssl:setup": "node scripts/create-ssl-certs.js"
Step 4
We don't want to push locally generated certs to the repository.
Add the certs
folder to your .gitignore
Step 5
Run yarn dev
and try accessing http://my-cool-domain.local:3000.
You'll notice it's accessible, and that's because devcert automatically added an entry in /etc/hosts
for you.
It doesn't have SSL enabled yet though, as we haven't setup Next.js to use those certs yet. In order to do that, we need to create a local custom server.
Install chalk
as a devDependency
yarn add chalk --dev
We will use chalk to create a nice error message for developers who try to run the project without setting up SSL first. This is entirely optional.
Create a new file create-local-server.js
inside the scripts folder
const { createServer: createHttpsServer } = require('https');
const next = require('next');
const fs = require('fs');
const chalk = require('chalk');
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
const PORT = process.env.PORT || 3000;
if (!fs.existsSync('./certs/.capath')) {
const macOsCommand = chalk.greenBright('sudo yarn ssl:setup');
const linuxCommand = chalk.greenBright('yarn ssl:setup');
console.error(chalk.red('\nError: Missing SSL certificates\n'));
console.error(`To fix this error, run the command below:`);
console.error(`→ MacOS: ${macOsCommand}`);
console.error(`→ Linux: ${linuxCommand}\n`);
process.exit();
}
app
.prepare()
.then(() => {
const server = createHttpsServer(
{
key: fs.readFileSync('./certs/devcert.key'),
cert: fs.readFileSync('./certs/devcert.cert'),
},
(req, res) => handle(req, res)
);
return server.listen(PORT, (err) => {
if (err) throw err;
console.log('> Ready on https://my-cool-domain.local:3000')
});
})
.catch((err) => {
console.error(err);
});
Step 6
Update the dev
script in your package.json:
"dev": "NODE_EXTRA_CA_CERTS=\"$(cat ./certs/.capath)\" node scripts/create-local-server.js",
Why do we need NODE_EXTRA_CA_CERTS?
Node does not use the system root store, so it won't accept devcert certificates automatically. This might not cause any apparent issue at first, but it will cause errors when using Next.js's Image component.
You'll see 500 internal error
page when trying to load a _next/image?url=...
route, and UNABLE_TO_VERIFY_LEAF_SIGNATURE
errors on the console when trying to load images if you don't pass the NODE_EXTRA_CA_CERTS
.
Step 7
Run your project using yarn dev
You should see our beautiful error message, as we didn't generate the certs yet. So go ahead, read the message and generate your certificates.
Run yarn dev
again and access https://my-cool-domain.local
Conclusion
That's it! Hope the tutorial was straightforward :)
You can check out the code in this example repository in GitHub.
Follow me on twitter: @waltergalvao_