---
title: How to set up local HTTPS for Next.js and other dev servers
description: Learn when you need local HTTPS, how to enable it with next dev --experimental-https, and how to clear certificate warnings on localhost.
url: "https://vercel.com/kb/guide/access-nextjs-localhost-https-certificate-self-signed"
published: 2025-11-03
last_updated: 2026-10-01
authors: Vercel
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

`next dev --experimental-https` generates a locally trusted certificate, so your Next.js development server runs on `https://localhost:3000` instead of `http://localhost:3000`. Most local development doesn't need HTTPS, because browsers already treat `localhost` as a secure origin. The exceptions are cookies, auth callbacks, custom hostnames, and requests from another device. Every Vercel deployment is served over HTTPS, so matching that protocol locally makes those cases behave the way they will in production.

In this guide, you'll learn how to:

- Decide whether your project needs local HTTPS
  
- Enable HTTPS in `next dev` with one flag
  
- Fix certificate warnings in Firefox, in Node.js, and on other devices
  
- Use a mkcert certificate with Vite and other dev servers
  
- Update auth provider callbacks for an HTTPS origin
  

## When you need local HTTPS

Chrome, Firefox, and Safari follow the Secure Contexts specification, which treats `http://localhost` and `http://127.0.0.1` as trustworthy origins. Service workers, `getUserMedia`, WebAuthn, and the Clipboard API all work over plain HTTP on those origins, so those APIs aren't a reason to switch.

Local HTTPS fixes these problems:

- **Secure cookies in Safari**: Safari won't set or send cookies with the `Secure` attribute over `http://localhost`, so a session that works in Chrome fails in Safari.
  
- **Auth.js cookie names**: Auth.js (formerly NextAuth.js) uses `Secure-` **and** `Host-` cookie names only when the app runs over HTTPS, so cookie names on `http://localhost` don't match production.
  
- **Cross-site cookies on custom hostnames**: `SameSite=None` cookies also require `Secure`, so testing them between two custom hostnames, such as `app.local.test` and `api.local.test`, needs HTTPS on both.
  
- **Auth providers without a localhost exception**: Google and Microsoft accept `http://localhost:3000` as a redirect URI, but Sign in with Apple rejects `localhost` and IP addresses.
  
- **Requests from another device**: A phone loading `http://192.168.1.10:3000` isn't on a trustworthy origin, so service workers, camera access, and passkeys fail on the phone.
  

Custom hostnames follow the same rule as a network address. Browsers check the hostname rather than the address it resolves to, so `app.local.test` mapped to `127.0.0.1` in `/etc/hosts` is an insecure origin and needs its own certificate.

## What you need to run HTTPS on localhost

Next.js added `--experimental-https` in version 13.5. Update to the latest version to get current certificate handling:

```bash
npm i next@latest
```

If you don't have a project yet, create one with `npx create-next-app@latest`.

Firefox on macOS and Linux, and Chromium on Linux, read certificates from their own Network Security Services (NSS) database instead of the system trust store. To install the certificate authority there as well, install `certutil` before you start the server:

- **Linux**: Run `sudo apt install libnss3-tools`, or install your distribution's equivalent NSS tools package.
  
- **macOS with Firefox**: Run `brew install nss`.
  

## How to enable local HTTPS in Next.js

Add the `--experimental-https` flag to `next dev`. If you start the server through the `dev` script in `package.json`, pass the flag after `--`:

```bash
npm run dev -- --experimental-https
```

To make HTTPS the default, change the script to `"dev": "next dev --experimental-https"` instead.

On the first run, the terminal output looks like this:

```text
⚠ Self-signed certificates are currently an experimental feature, use with caution.
   Downloading mkcert package...
   Download response was successful, writing to disk
   Attempting to generate self signed certificate. This may prompt for your password
   CA Root certificate created in /Users/your_username/Library/Application Support/mkcert
   Certificates created in /path/to/your_project/certificates
   Adding certificates to .gitignore

   ▲ Next.js 16.3.8 (Turbopack)
   - Local:        https://localhost:3000
   - Network:      https://192.168.1.10:3000
```

Next.js downloads mkcert into a local cache and uses it to create a local certificate authority. Installing that authority into your system trust store requires administrator access, so expect a password prompt.

Next.js then issues a certificate for `localhost`, `127.0.0.1`, and `::1` and saves the certificate and its key to a `certificates` directory in the folder you ran the command from. If that folder already has a `.gitignore`, Next.js appends `certificates` to it. A monorepo that keeps its ignore rules at the repository root needs the entry added by hand. Later runs reuse the saved certificate as long as it still covers the hostname and matches its key.

The app is served at `https://localhost:3000`. Next.js uses port 3000 unless you set another with `-p`, `--port`, or the `PORT` environment variable.

### Use a certificate you already have

To skip generation, pass an existing certificate and key:

```bash
next dev --experimental-https --experimental-https-key ./certs/localhost-key.pem --experimental-https-cert ./certs/localhost.pem --experimental-https-ca ./certs/rootCA.pem
```

The `--experimental-https-ca` flag is optional. Next.js uses that root certificate to trust your HTTPS origin in server-side requests, which the next section covers. Both approaches are for development only, and Next.js prints a warning on every start noting that the feature is experimental.

## Why your browser still warns about the localhost HTTPS certificate

mkcert installs its certificate authority into the system trust store, so most setups show no warning. A warning means a trust store or a hostname was missed.

Work through these causes:

- **Firefox, or Chromium on Linux**: These browsers read an NSS database. Install `certutil` as described in the prerequisites, delete the `certificates` directory so Next.js generates and installs the certificate again, then restart the dev server and the browser.
  
- **A second device or machine**: The certificate authority exists only on the machine that generated it, and the certificate only covers `localhost`, `127.0.0.1`, and `::1`. Follow the steps in the next section to test from a phone or another computer.
  
- **Server-side requests in Node.js**: Node.js checks its own bundled certificate list rather than the system store. When Next.js generates the certificate, it sets `NODE_EXTRA_CA_CERTS` for the dev server, so server-side `fetch` calls to your HTTPS origin work. Set the variable yourself when you pass your own certificate without `--experimental-https-ca`, or when another Node.js process, such as a script or test runner, calls the HTTPS server.
  

The root certificate is the `rootCA.pem` file in the folder that Next.js prints as `CA Root certificate created in` on the first run. If you've installed mkcert yourself, `mkcert -CAROOT` prints the same folder:

```bash
export NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem"
```

Fix the trust store rather than saving a permanent browser exception, which would hide real certificate problems later.

## How to test local HTTPS from a phone or another device

`next dev` listens on `0.0.0.0` by default and prints a network address, such as `https://192.168.1.10:3000`, beside the local one. A phone opening that address shows a certificate warning, because the phone doesn't trust your certificate authority and the generated certificate doesn't cover your network address.

To trust the server from another device:

1. Start the server on your network address, so Next.js adds that address to the certificate. With `--hostname` set, the server listens only on that address, so use the same URL on your development machine too.
   
   ```bash
   next dev --experimental-https --hostname 192.168.1.10
   ```
   
2. Copy `rootCA.pem` from the certificate authority folder to the device and install it. On iOS, install the downloaded profile in **Settings**, then turn on full trust for it under **Settings** > **General** > **About** > **Certificate Trust Settings**. On other devices, follow the platform's steps for installing a trusted root certificate.
   
3. Open `https://192.168.1.10:3000` on the device.
   

For a one-off visual check, you can accept the browser warning instead. Features that need a trusted origin, such as service workers, may still fail with an untrusted certificate.

## How to run local development over HTTPS without `next dev`

The `--experimental-https` flag only covers `next dev`. Custom servers and frameworks such as Vite expect you to supply a certificate, and the `vercel dev` command has no HTTPS option. Generate a certificate with mkcert and pass it to whichever server you run.

The copy of mkcert that Next.js downloads isn't added to your `PATH`, so install mkcert separately, for example with `brew install mkcert` on macOS. Then create a locally trusted certificate:

```bash
mkcert -install
mkcert localhost 127.0.0.1 ::1
```

`mkcert -install` reuses the certificate authority that Next.js created if one already exists. The second command writes `localhost+2.pem` and `localhost+2-key.pem` to the current directory. mkcert doesn't update `.gitignore`, so add `*.pem` or both filenames yourself.

In Vite, pass both files to `server.https`:

```typescript
import { defineConfig } from 'vite'
import fs from 'node:fs'

export default defineConfig({
  server: {
    https: {
      key: fs.readFileSync('./localhost+2-key.pem'),
      cert: fs.readFileSync('./localhost+2.pem'),
    },
  },
})
```

Any Node.js HTTPS server takes the same two files through the `key` and `cert` options of `https.createServer()`, so the pattern carries across frameworks.

## How to configure auth providers for HTTPS on localhost

Switching the origin to `https://localhost:3000` breaks any callback still registered against the HTTP origin, so update the provider and your application together:

1. Register the HTTPS callback URL as a redirect URI in the provider's dashboard, and keep the HTTP entry if you switch between the two. For Google with Auth.js, that URL is `https://localhost:3000/api/auth/callback/google`.
   
2. Set your auth library's base URL variable to the HTTPS origin, such as `AUTH_URL=https://localhost:3000` for Auth.js, so it builds matching callback URLs and cookie names.
   
3. Restart the development server so the new origin takes effect.
   

### Use a custom hostname for providers that reject localhost

Sign in with Apple only accepts HTTPS return URLs on a domain name, so it rejects `localhost` and IP addresses. Use a subdomain of a domain you control, such as `dev.example.com`, and add that domain to your Sign in with Apple configuration.

1. Map the hostname to `127.0.0.1` in your `/etc/hosts` file:
   
   ```text
   127.0.0.1 dev.example.com
   ```
   
2. Start the server with that hostname. Next.js adds it to the generated certificate alongside `localhost` and regenerates any saved certificate that doesn't cover it:
   
   ```bash
   next dev --experimental-https --hostname dev.example.com
   ```
   
3. Register `https://dev.example.com:3000/api/auth/callback/apple` as a return URL with Apple, and set `AUTH_URL=https://dev.example.com:3000`. The return URL has to match the URL your app sends exactly, including the port.
   

## What changes when you deploy instead of running HTTPS locally

The certificates you generate locally never leave your machine. Vercel serves every deployment over HTTPS and provisions certificates automatically for deployment URLs and custom domains, so deployments need no certificate setup. HTTP requests to a deployment redirect to HTTPS with a `308` status code. Running HTTPS locally means the cookie behavior you tested matches what your application sees in production.

## Next steps

With local HTTPS working, you can test authentication flows and cookie behavior over the same protocol you deploy on.

- [Deploy your project to Vercel](https://vercel.com/new)
  
- [Start from a Vercel template](https://vercel.com/templates)
  
- [Review every](https://nextjs.org/docs/app/api-reference/cli/next) [`next dev`](https://nextjs.org/docs/app/api-reference/cli/next) [option in the Next.js CLI reference](https://nextjs.org/docs/app/api-reference/cli/next)
  

## Related resources

- [Encryption and TLS](https://vercel.com/docs/cdn-security/encryption)
  
- [vercel dev](https://vercel.com/docs/cli/dev)
  

## Frequently asked questions

### Is `http://localhost` already secure?

For most purposes, yes. Browsers treat `http://localhost` and `http://127.0.0.1` as potentially trustworthy origins, so secure-context APIs such as service workers and WebAuthn work there without HTTPS. The traffic isn't encrypted, but it never leaves your machine. Cookie handling, especially in Safari, is the main behavior that still differs from a real HTTPS origin.

### Can I use local HTTPS with a custom domain?

Yes. Map the hostname to `127.0.0.1` in your `/etc/hosts` file, then run `next dev --experimental-https --hostname your_hostname_here`, and Next.js adds that hostname to the generated certificate. Use a subdomain of a domain you own, because providers such as Sign in with Apple don't accept reserved or local-only names.

### How do I open my local HTTPS server on a phone?

Start `next dev --experimental-https` with `--hostname` set to your machine's network address, so the certificate covers that address. Then install the mkcert `rootCA.pem` file on the phone and open the network address. Without the root certificate, the phone shows a certificate warning, and you can accept it for a visual check only.

### Should I commit the generated certificates to Git?

No. Anyone holding the private key can impersonate your local development server. Next.js appends the `certificates` directory to the `.gitignore` in the folder you ran the command from, but only when that `.gitignore` already exists. Add the entry by hand if your ignore rules live elsewhere, and do the same for certificates you generate with mkcert. Every developer on the project can create their own certificate by running the same command.

## More Next.js guides

- [How do I reduce my build time with Next.js on Vercel?](/kb/guide/how-do-i-reduce-my-build-time-with-next-js-on-vercel): Reduce Next.js build times on Vercel by pre-rendering fewer pages at build time, deferring generation with ISR and image optimization, and using faster build machines.
- [Integrating AWS Secrets Manager with Vercel Using Terraform](/kb/guide/integrating_aws_secrets_manager_with_vercel_using_terraform): Learn how to seamlessly integrate AWS Secrets Manager with Vercel for enhanced security and efficiency in your web deployments using Terraform with our comprehensive guide.
- [Can I redirect from a subdomain to a subpath?](/kb/guide/can-i-redirect-from-a-subdomain-to-a-subpath): Learn how to redirect from your subdomain to a subpath on Vercel with a vercel.json file or with Next.js