How to set up local SSL with Next.js

How to set up local SSL with Next.js


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')) {

const domains = ['my-cool-domain.local'];

  .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);

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('\nError: Missing SSL certificates\n'));

  console.error(`To fix this error, run the command below:`);
  console.error(`→ MacOS: ${macOsCommand}`);
  console.error(`→ Linux: ${linuxCommand}\n`);


  .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) => {

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



That's it! Hope the tutorial was straightforward :)