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¶
- Hostname setup
- nginx installation and service management
- SSL certificate generation
- nginx reverse proxy configuration
- 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
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
~/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¶
- Start the development server on
localhost (127.0.0.1)on the configured portAPP_PORT - 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;
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¶
- macOS Tahoe 26.1 (Apple Silicon)
- nginx 1.29.1
- OpenSSL 3.6.0
- Homebrew 4.6.15
The directory paths used here are macOS Tahoe 26.1 (Apple Silicon) specific.