I use this technique to host an app server locally on MacOS using nginx and
self-signed certificates:
mkdir <project-dir>
cd <project-dir>
echo "hello world" >> index.html
python -m http.server
:: visit http://localhost:8000 and verify I see "hello world"
openssl genrsa -out localhost.key 4096
openssl req -new -x509 -subj "/CN=localhost/" -addext "subjectAltName = DNS:localhost" -nodes -sha256 -key localhost.key -out localhost.pem
Chrome and safari enforce TLS certs contain a subjectAltName.
Verify there is a section called “X509v3 extensions”, and that it contains “subjectAltName”:
openssl x509 -noout -text -in localhost.pem
openssl x509 -in localhost.pem -inform pem -outform der -out localhost.cer
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain localhost.cer
:: Open Keychain Access > System and double tap 'localhost'
:: Tap the 'Trust' chevron and verify that SSL is set to "Always Trust"
If I am building an iOS app, drag localhost.cer
on to the iOS simulator, and then trust it at Settings > General > About > Certificate Trust Settings > "Enable Full Trust for Root Certificates"
brew install nginx
Add /opt/homebrew/etc/nginx/servers/localhost.conf
with the contents (replace /path/to/<project-dir>
):
upstream app-server {
server 127.0.0.1:8000;
}
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /path/to/<project-dir>/localhost.pem;
ssl_certificate_key /path/to/<project-dir>/localhost.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X_FORWARDED_PROTO https;
proxy_set_header X_FORWARDED_HOST $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
proxy_max_temp_file_size 0;
if (!-f $request_filename) {
proxy_pass http://app-server;
break;
}
}
}
Start nginx:
nginx
Browse in Chrome or Safari to https://localhost (Firefox doesn’t trust self signed certs)
Stop nginx:
nginx -s stop
Debug by tailing logs at /opt/homebrew/var/log/nginx/*
Also see config at /opt/homebrew/etc/nginx/nginx.conf
This works to make a request to an IP over https from safari and curl on MacOS but NOT from mobile safari on iOS nor chrome on MacOS:
openssl req -x509 -nodes -newkey rsa:2048 -keyout myrootCA.key -out myrootCA.pem -days 365 -subj "/CN=192.168.1.4"
I fiddled with this for a while trying to get it to work with iOS, taking these steps:
airdrop myrootCA.pem to iOS
Go to settings > General > VPN & Device Management > Configuration Profile > 192.168.1.4 > Install
Go to settings > General > About > Certificate Trust Settings > Enable Full Trust For Root Certificates > Toggle trust slider
If the ‘Enable Full Trust For Root Certificates’ option is not there, it’s because iOS isn’t happy with how the cert was generated.
Still, once iOS Setting was happy with it, I still couldn’t use mobile safari to browse to https://192.168.1.4
I also tried this, which is accepted by MacOS safari and chrome, but is not accepted by iOS settings (the ‘Enable Full Trust for Root Certificate’ option is not present)
openssl req -x509 -nodes -newkey rsa:2048 -keyout myrootCA.key -out myrootCA.pem -days 365 -subj "/CN=192.168.1.4" -extensions SAN -config <(echo "[SAN]"; echo "subjectAltName=IP:192.168.1.4")