Skip to content

Local HTTPS Setup for Development on macOS

Access the local development server on Mac via HTTPS from any WiFi device using its hostname.

This entry records a local development and testing setup used to access a macOS-hosted development server over HTTPS from other devices on the same WiFi network. The need arose while testing HTTPS-only behavior (secure cookies, service workers, authentication callbacks) on real devices, without modifying the application itself. The setup follows a production-style access pattern: TLS is terminated at an nginx reverse proxy bound to a hostname resolvable via mDNS, while the development application continues to run unmodified on 127.0.0.1 over HTTP.

Device (WiFi)
↓
https://myhostname.local:ACCESS_PORT
↓
nginx (TLS termination)
↓
127.0.0.1:APP_PORT (dev app, HTTP)

1. Execution Outline

  1. Hostname setup
  2. nginx installation and service management
  3. SSL certificate generation
  4. nginx reverse proxy configuration
  5. nginx reload

2. Implementation Record

2.1 Hostname setup

2.1.1 Check current hostname

# Check current hostname
scutil --get LocalHostName

# Check full hostname
hostname

2.1.2 Set hostname (if required)

sudo scutil --set HostName myhostname
sudo scutil --set LocalHostName myhostname
sudo scutil --set ComputerName myhostname
Replace myhostname as required.

2.1.3 Restart mDNS

sudo killall -HUP mDNSResponder

2.1.4 Verify resolution

scutil --get LocalHostName
ping myhostname.local -c 1

2.2 nginx installation and service management

2.2.1 Install nginx

brew install nginx

2.2.2 Start the nginx service

brew services start nginx

2.2.3 Verify service status

brew services info nginx

2.3 SSL certificate generation

2.3.1 Create certificate directory

mkdir -p ~/dev-ssl
cd ~/dev-ssl

2.3.2 Create OpenSSL configuration

cat > cert-config.cnf << 'EOF'
[req]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn
x509_extensions = v3_req

[dn]
CN = myhostname.local

[v3_req]
subjectAltName = @alt_names

[alt_names]
DNS.1 = myhostname.local
DNS.2 = localhost
IP.1 = 127.0.0.1
EOF

Replace myhostname as required.

2.3.3 Generate a self-signed certificate

openssl req -x509 -newkey rsa:4096 \
-keyout key.pem \
-out cert.pem \
-days 730 \
-nodes \
-config cert-config.cnf
This creates:

  • ~/dev-ssl/key.pem - Private key
  • ~/dev-ssl/cert.pem - SSL certificate

valid for 730 days

2.4 nginx reverse proxy configuration

2.4.1 Create nginx configuration file

Create local-dev.conf in /opt/homebrew/etc/nginx/servers

cat > /opt/homebrew/etc/nginx/servers/local-dev.conf << 'EOF'
server {
    listen ACCESS_PORT ssl;
    server_name myhostname.local localhost;

    ssl_certificate /Users/MAC_USERNAME/dev-ssl/cert.pem;
    ssl_certificate_key /Users/MAC_USERNAME/dev-ssl/key.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    # Increase buffer sizes for large static files
    proxy_buffer_size 128k;
    proxy_buffers 4 256k;
    proxy_busy_buffers_size 256k;

    location / {
        proxy_pass http://127.0.0.1:APP_PORT;
        proxy_set_header Host $host:ACCESS_PORT;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_redirect off;

        # Disable buffering for dynamic content
        proxy_buffering off;
    }
}
EOF

Replace-
a. MAC_USERNAME with macOS username ($ whoami)
b. ACCESS_PORT with a free port to use in the access URL
c. APP_PORT with the development server's port
d. myhostname with the OS hostname

2.4.2 Validate nginx configuration

sudo nginx -t

2.4.3 Reload nginx to apply changes

brew services restart nginx

2.4.4 Verify nginx status

brew services info nginx

3. Verification

  1. Start the development server on localhost (127.0.0.1) on the configured port APP_PORT
  2. Access from any Device in the WiFi
https://myhostname.local:ACCESS_PORT

Replace myhostname and ACCESS_PORT with values from the configuration.

Observed browser behavior with self-signed certificates:

  • iOS / Safari presents a warning; access proceeds after selecting "Show Details" → "visit this website"
  • Android / Chrome blocks by default; access proceeds via "Advanced" → "Proceed to myhostname.local"
  • Desktop Chrome / Edge requires confirmation via "Advanced" → "Proceed to myhostname.local (unsafe)"
  • Firefox requires "Advanced" → "Accept the Risk and Continue"

4. Troubleshooting

4.1 Hostname not resolving

Restart the mDNS responder.

sudo killall -HUP mDNSResponder

Verify hostname

scutil --get LocalHostName

Test from Mac itself

ping myhostname.local -c 1

Use the configured host in place of myhostname.local

4.2 nginx not starting

Check the nginx configuration.

sudo nginx -t

Check error logs.

tail -f /opt/homebrew/var/log/nginx/error.log

Check if port ACCESS_PORT is already in use.

lsof -i :ACCESS_PORT

Use ACCESS_PORT as configured.

4.3 macOS firewall blocking

Check firewall status.

sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate

Allow nginx through the firewall, if needed.

sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add /opt/homebrew/bin/nginx
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblockapp /opt/homebrew/bin/nginx

4.4 Static files not loading

Error: ERR_CONTENT_LENGTH_MISMATCH

The nginx config above already includes buffer size fixes. Adjust these.

nano /opt/homebrew/etc/nginx/servers/local-dev.conf

proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
proxy_buffering off;
Restarting nginx is needed if local-dev.conf is changed.

brew services restart nginx

4.5 Wrong port configuration

Check what port the development app is running on.

lsof -i :APP_PORT

Use the configured APP_PORT.

Check the nginx port configuration.

grep proxy_pass /opt/homebrew/etc/nginx/servers/local-dev.conf

4.6 Certificate expired

Regenerate certificate.

cd ~/dev-ssl
openssl req -x509 \
-newkey rsa:4096 \
-keyout key.pem \
-out cert.pem \
-days 730 \
-nodes \
-config cert-config.cnf

nginx needs to be restarted after regenerating the certificate.

brew services restart nginx

5. Version Anchor

  1. macOS Tahoe 26.1 (Apple Silicon)
  2. nginx 1.29.1
  3. OpenSSL 3.6.0
  4. Homebrew 4.6.15

The directory paths used here are macOS Tahoe 26.1 (Apple Silicon) specific.