Local Development with HTTPS

Local Development with HTTPS

Set up HTTPS for local development to test secure cookies, OAuth flows, and other TLS-dependent features.

Quick Start

1. Generate Self-Signed Certificates

Option A: Built-in generator (recommended)

modula --gen-certs

This creates certs/localhost.crt and certs/localhost.key.

Option B: OpenSSL

mkdir -p certs
openssl req -x509 -newkey rsa:4096 -nodes \
  -keyout certs/localhost.key \
  -out certs/localhost.crt \
  -days 365 \
  -subj "/CN=localhost"

2. Configure for Local HTTPS

Set these fields in your modula.config.json:

{
  "environment": "local",
  "os": "darwin",
  "port": ":8080",
  "ssl_port": ":4443",
  "cert_dir": "./certs/",
  "client_site": "localhost",
  "admin_site": "localhost",
  "db_driver": "sqlite",
  "db_url": "./modula.db",
  "cookie_secure": false,
  "cors_origins": ["https://localhost:4443"]
}

Key settings:

  • environment set to "local" enables HTTPS using self-signed certificates from cert_dir
  • ssl_port determines the HTTPS port (:4443 avoids needing root privileges)
  • cookie_secure should be false for local development to avoid cookie issues with self-signed certs

3. Build and Run

just dev
modula

Output:

Server is running at https://localhost:4443
Server is running at http://localhost:8080

4. Handle the Browser Warning

Your browser will show a security warning for self-signed certificates. You have two options:

Accept the warning (quick): Visit https://localhost:4443, click "Advanced", then "Proceed to localhost (unsafe)".

Trust the certificate (persistent):

macOS:

sudo security add-trusted-cert -d -r trustRoot \
  -k /Library/Keychains/System.keychain certs/localhost.crt

Linux:

sudo cp certs/localhost.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

5. Test the Connection

# Test HTTP
curl http://localhost:8080/

# Test HTTPS (skip certificate verification)
curl -k https://localhost:4443/

# Test HTTPS (with certificate)
curl --cacert certs/localhost.crt https://localhost:4443/

Using mkcert (No Browser Warnings)

mkcert creates locally-trusted certificates that browsers accept without warnings.

Install mkcert:

# macOS
brew install mkcert
brew install nss  # for Firefox support

# Linux
sudo apt install libnss3-tools
# Download mkcert binary from https://github.com/FiloSottile/mkcert/releases

Set up the local CA and generate certificates:

mkcert -install
mkdir -p certs
mkcert -key-file certs/localhost.key -cert-file certs/localhost.crt localhost 127.0.0.1 ::1

Run ModulaCMS normally. Browsers will trust the certificate without any warnings.

HTTP-Only Mode

To disable HTTPS entirely and run only the HTTP server, set environment to "local":

{
  "environment": "local",
  "port": ":8080",
  "client_site": "localhost",
  "admin_site": "localhost"
}

Local environments skip TLS entirely. This is useful when you do not need to test TLS-dependent features. For Docker, use "local-docker" which binds to 0.0.0.0 instead of localhost.

Custom Local Domain

To use a domain like modulacms.local instead of localhost:

1. Add a hosts entry

sudo nano /etc/hosts
# Add:
127.0.0.1   modulacms.local

2. Generate a certificate for the custom domain

# With OpenSSL
openssl req -x509 -newkey rsa:4096 -nodes \
  -keyout certs/localhost.key \
  -out certs/localhost.crt \
  -days 365 \
  -subj "/CN=modulacms.local" \
  -addext "subjectAltName=DNS:modulacms.local"

# Or with mkcert
mkcert -key-file certs/localhost.key -cert-file certs/localhost.crt modulacms.local "*.modulacms.local"

Good to know: ModulaCMS expects the certificate files to be named localhost.crt and localhost.key in the cert_dir directory regardless of the domain.

3. Update modula.config.json

{
  "environment": "local",
  "client_site": "modulacms.local",
  "admin_site": "admin.modulacms.local",
  "port": ":80",
  "ssl_port": ":443",
  "cert_dir": "./certs/"
}

Running on ports 80 and 443 requires elevated privileges:

sudo modula

Environment Modes

Each environment stage can run natively or in Docker by appending -docker (e.g., development-docker). Docker variants bind to 0.0.0.0; native variants bind to localhost or client_site.

Environment HTTPS Certificate Source Use Case
local No N/A HTTP-only local development
development Yes Self-signed from cert_dir Dev server with HTTPS
staging Yes Let's Encrypt (autocert) Staging server
production Yes Let's Encrypt (autocert) Production server

The admin panel favicon changes color based on the environment stage so you can identify which instance you are working in at a glance: blue for local, green for development, amber for staging, red for production.

Troubleshooting

Certificate not found

Error: failed to load certificate

Verify the certificate files exist in your cert_dir:

ls -la ./certs/
# Should contain: localhost.crt and localhost.key

Port already in use

Error: bind: address already in use

Find and stop the process using the port:

lsof -i :4443
kill <PID>

HTTPS server not starting

Check for certificate-related errors in the output:

modula 2>&1 | grep -i cert

Verify file permissions: chmod 644 certs/localhost.crt and chmod 600 certs/localhost.key.