Serving a Next.js application over HTTPS in localhost

Serving a Next.js application over HTTPS in localhost

Why HTTPS in localhost?

For most applications, not having HTTPS in localhost is completely fine and that is the usual way. Recently, I needed a way to set cross-site cookies. Using express-session, it's quite simple to do this. Set cookie.sameSite to none. But to do that, I have to set cookie.secure to true and this is possible only over HTTPS connections.

I could have deployed my application to a dev server so it's served over HTTPS but that just takes way too long. So I set up HTTPS for my local server and this is how I did it.

Enter, mkcert

mkcert is awesome. It took me only about 5 minutes to have my application served over HTTPS on my local machine.

First, we have to install mkcert on our machine. I use Ubuntu and had Linuxbrew installed already, so I just had to run:

brew install mkcert

This will also work on macOS with Homebrew. On Windows, you can use the pre-built binaries available here: https://github.com/FiloSottile/mkcert/releases.

Once it's installed, run:

mkcert -install

This will add a local certificate authority to your system and notify your browsers. You have to restart your browsers to pick up this change. Then run:

mkcert localhost

This will generate two pem (localhost.pem and localhost-key.pem) files in your home directory. If you've used AWS EC2 before you will already be familiar with this file type. It's a file format used to store cryptographic keys and is used for authentication and authorization.

Creating a custom local server for Next.js

There is no way, that I know of, to change the default Next.js server, so it serves over HTTPS. So we'll create a custom server. If you're deploying to Vercel, don't worry. We're using this custom server only for local development.

Create a server.js file in the project root:

const { createServer } = require('https');
const { parse } = require('url');
const next = require('next');
const fs = require('fs');
const port = 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();

const httpsOptions = {
    key: fs.readFileSync('./certs/localhost-key.pem'),
    cert: fs.readFileSync('./certs/localhost.pem')
};

app.prepare().then(() => {
    createServer(httpsOptions, async (req, res) => {
        const parsedUrl = parse(req.url, true);
        await handle(req, res, parsedUrl);
    }).listen(port, (err) => {
        if (err) throw err;
        console.log('ready - started server on url: https://localhost:' + port);
    });
});

Here we're using the createServer from the Node.js https library and passing in our local keys in the httpsOptions.

Now, all we have to do is, run:

node server.js

Go to https://localhost:3000 on your favorite browser. Now, our application is served over HTTPS on localhost! Yaay!


Setting up HTTPS for a localhost server was surprisingly easy than I thought it would be. If you need your Next.js application to be served over HTTPS in localhost, this is how to do it. With some small modifications, this will also work for Express.js servers. I hope this article was informative.

Until next time.