Skip to content
Docs

How to set up local HTTPS for Next.js and other dev servers

Learn when you need local HTTPS, how to enable it with next dev --experimental-https, and how to clear certificate warnings on localhost.

Vercel

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

Copy link to headingWhen 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.

Copy link to headingWhat 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:

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.

Copy link to headingHow 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 --:

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:

⚠ 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.

Copy link to headingUse a certificate you already have

To skip generation, pass an existing certificate and key:

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.

Copy link to headingWhy 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:

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.

Copy link to headingHow 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.
    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.

Copy link to headingHow 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:

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:

vite.config.ts
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.

Copy link to headingHow 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.

Copy link to headingUse 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:
    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:
    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.

Copy link to headingWhat 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.

Copy link to headingNext steps

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

Copy link to headingFrequently asked questions

Copy link to headingIs 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.

Copy link to headingCan 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.

Copy link to headingHow 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.

Copy link to headingShould 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.

Related documentation

More Next.js guides