Local SSL/HTTPS in Next.js • Works for Subdomains!
Set up trusted SSL certificates for local Next.js development with full subdomain support, including automated scripts for certificate generation and installation.
This guide demonstrates how to configure HTTPS for local Next.js development with full subdomain support. You'll set up trusted SSL certificates that work seamlessly with subdomains like tenant1.app.localhost or admin.app.localhost.
Quick Start: The Easy Way
Next.js 15+ includes built-in HTTPS support via an experimental flag:
next dev --experimental-httpsOr add it to your package.json:
{
"scripts": {
"dev": "next dev --experimental-https"
}
}Next.js will automatically generate and install SSL certificates on first run. However, this approach doesn't support subdomains — you'll need custom certificates for that.
Why Custom Certificates Matter
The default Next.js certificates only cover localhost. If you're building multi-tenant applications or testing subdomain-based routing, you need certificates that support:
localhostapp.localhost(recommended base domain)*.app.localhost(wildcard for all subdomains)
Custom certificates also give you control over the certificate authority and domain configuration.
Manual Certificate Generation
Step 1: Generate Certificate Authority
Create a root Certificate Authority (CA) that will sign your domain certificates:
openssl req -x509 -nodes -new -sha256 -days 1024 -newkey rsa:2048 \
-keyout ./RootCA.key \
-out ./RootCA.pem \
-subj "/C=CA/CN=MyCompany-Root-CA"
openssl x509 -outform pem -in ./RootCA.pem -out ./RootCA.crtThis generates three files:
RootCA.key— Private key (never commit)RootCA.pem— Certificate (never commit)RootCA.crt— Public certificate (safe to commit)
Step 2: Configure Domains
Create a domains.ext file specifying all domains and subdomains:
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = app.localhost
DNS.3 = *.app.localhostThe wildcard *.app.localhost enables any subdomain like tenant1.app.localhost or admin.app.localhost.
Step 3: Generate Domain Certificate
Create the certificate for your localhost domains:
openssl req -new -nodes -newkey rsa:2048 \
-keyout ./localhost.key \
-out ./localhost.csr \
-subj "/C=CA/ST=Ontario/L=Ottawa/O=MyCompany-Dev/CN=*.app.localhost"
openssl x509 -req -sha256 -days 1024 \
-in ./localhost.csr \
-CA ./RootCA.pem \
-CAkey ./RootCA.key \
-CAcreateserial \
-CAserial ./RootCA.srl \
-extfile ./domains.ext \
-out ./localhost.crtStep 4: Install Certificates
On macOS, install both the CA and domain certificates:
# Install Root CA
sudo security add-trusted-cert -d -r trustRoot -p ssl -p codeSign \
-k /Library/Keychains/System.keychain ./RootCA.crt
# Install domain certificate
sudo security add-trusted-cert -d -r trustAsRoot -p ssl -p codeSign \
-k /Library/Keychains/System.keychain ./localhost.crtRestart your browser after installation for changes to take effect.
Step 5: Configure Next.js
Update your package.json with the certificate paths:
{
"scripts": {
"dev:ssl": "next dev --experimental-https --experimental-https-ca ./config/ssl/certificates/RootCA.crt --experimental-https-key ./config/ssl/certificates/localhost.key --experimental-https-cert ./config/ssl/certificates/localhost.crt"
}
}Now pnpm dev:ssl starts Next.js with your custom certificates.
Automated Setup Scripts
This repository includes two bash scripts that automate the entire process:
Setup Script
The setup.sh script generates all certificates with an interactive CLI:
cd config/ssl
./setup.shThe script prompts for:
- Certificate Authority name
- Country, state, and city
- Organization name
- Additional domains (optional)
It automatically:
- Generates CA and domain certificates
- Creates the
domains.extconfiguration - Updates
.gitignoreto exclude sensitive files - Optionally updates
package.jsonwithdev:sslandpredev:sslscripts
Key features:
- Uses Node.js fallback if
jqisn't installed - Preserves existing
devscript configuration - Only commits safe files (
.crt,.keyfor localhost,domains.ext)
See the complete implementation.
Trust Script
The trust.sh script ensures certificates are installed before starting the dev server:
./config/ssl/trust.shThis script:
- Checks if certificates are already trusted
- Prompts for sudo only when needed
- Installs certificates to both System and Login keychains
- Auto-detects CA and domain certificate files
The predev:ssl script automatically runs this before dev:ssl:
{
"scripts": {
"predev:ssl": "./config/ssl/trust.sh",
"dev:ssl": "next dev --experimental-https ..."
}
}When you run pnpm dev:ssl, the trust script runs first, ensuring certificates are installed.
See the complete implementation.
Subdomain Routing Configuration
Why app.localhost?
Chrome treats localhost specially for cookies. Using app.localhost as your base domain provides:
- Proper subdomain cookie behavior — Cookies set on
app.localhostcan be shared with*.app.localhost - Domain + extension structure — Chrome sees
appas the domain andlocalhostas the extension - Consistent with production — Mirrors how cookies work on real domains
Without this, subdomain cookies won't work correctly in Chrome.
Next.js Configuration
The createSubdomainConfig utility handles subdomain routing:
import { createSubdomainConfig } from "./config/ssl/next-subdomains";
const { rewrites, redirects, allowedDevOrigins } = createSubdomainConfig(
env,
["sashkode.dev", "sashkode.app"]
);
const nextConfig = {
allowedDevOrigins,
rewrites,
redirects,
};This configuration:
- Rewrites subdomain requests to appropriate app directories
- Redirects
localhosttoapp.localhostin development - Detects SSL mode via
npm_lifecycle_event(checks fordev:ssl) - Supports custom domains for preview/production environments
The redirects ensure you always use app.localhost instead of plain localhost:
redirects: () => {
if (isDev) {
return isDevSSL
? [
{
permanent: false,
source: "/:path*",
has: [{ type: "host", value: "localhost" }],
destination: "https://app.localhost:3000/:path*",
},
]
: // ... http fallback
}
return [];
}See the full subdomain configuration for details or read the How to Build Multi-Tenant Apps in Next.js • Without Middleware? article.
App Directory Structure
Organize your routes by subdomain:
Access your tenant parameter in pages:
export default async function TenantPage({
params,
}: {
params: Promise<{ tenant: string }>;
}) {
const { tenant } = await params;
return <div>Welcome to {tenant}</div>;
}Complete Workflow
Here's the recommended development workflow:
-
Initial setup (one-time):
cd config/ssl ./setup.sh -
Start development:
pnpm dev:ssl -
Access your app:
- Root domain:
https://app.localhost:3000 - Blog subdomain:
https://blog.app.localhost:3000 - Tenant subdomain:
https://acme.app.localhost:3000
- Root domain:
The certificates work seamlessly across all subdomains thanks to the wildcard configuration.
Security Considerations
Safe to commit:
RootCA.crt— Public certificatelocalhost.crt— Public certificatelocalhost.key— Development-only keydomains.ext— Domain configuration
Never commit:
RootCA.key— Private CA keyRootCA.pem— CA certificate with private datalocalhost.csr— Certificate signing requestRootCA.srl— Serial number file
The setup.sh script automatically configures .gitignore to exclude sensitive files.
Troubleshooting
Browser shows "Not Secure":
- Restart your browser after installing certificates
- Check certificates are installed:
security find-certificate -c "MyCompany-Root-CA" - Re-run
./config/ssl/trust.shto ensure proper installation
Subdomains don't work:
- Verify
domains.extincludes*.app.localhost - Check Next.js config uses
createSubdomainConfig - Ensure you're accessing
https://subdomain.app.localhost:3000(not plainlocalhost)
Cookies not working across subdomains:
- Use
app.localhostas your base domain, not plainlocalhost - Set cookie domain to
.app.localhostto share across subdomains - Check Chrome DevTools → Application → Cookies to verify domain
Further Reading
- Next.js HTTPS Documentation
- mkcert — Cross-platform certificate generation tool
- OpenSSL Documentation
Related Code
Explore the complete implementation in the repository:
config/ssl/setup.sh— Automated certificate setupconfig/ssl/trust.sh— Certificate installation scriptconfig/ssl/next-subdomains.ts— Subdomain routing configurationnext.config.ts— Next.js configuration with SSL support