Skip to content

Local Development with Self Signed Certificate

By Jasper Frumau

Inspiration

To realize a self signed certificate that would be accepted by all browsers I failed many times in the past. Until I bumped into a great to follow post by Thijs Busser. He explains all well in a great post .  He even explains final steps for Windows and Mac users. I was only interested in the MacOS part of things. So here is the gist of things.

Root Certificate

The thing I had been missing all along was the need for a root certificate. This you need to generate your self signed certificate and this is the only one you need to add and get accepted by MacOS in the Keychain. So let’s tag along here.

The following commands are needed to create a root certificate:

openssl genrsa -des3 -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024  -out rootCA.pem

SSL Certificate

Once you have your root certificate you can work on the actual SSL certificate. You can do this as follows:

The following commands are needed to create an SSL certificate issued by the self created root certificate:

openssl req -new -nodes -out server.csr -newkey rsa:2048 -keyout server.key
openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 500 -sha256 -extfile v3.ext

V3 Extension

The final command does need to read data from v3.ext. This contains some necessary details including the alternate names for your local website.

The referenced v3.ext file should look something like this:

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = acme-site.dev
DNS.2 = acme-static.dev

You of course need to use your local domains like laravel.test and or *.laravel.test for example.

Nginx Certs Loading

To use these certificates you need to refer to them in your Nginx conf.  Did it like so in a Laradock setup:

server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    listen 443 default_server ssl http2;
    listen [::]:443 default_server ssl http2;
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;

    root /var/www/public;
    index index.php index.html index.htm;

    location / {
         try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        try_files $uri /index.php =404;
        fastcgi_pass php-upstream;
        fastcgi_index index.php;
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        #fixes timeouts
        fastcgi_connect_timeout 60;
        fastcgi_read_timeout 1200;
        fastcgi_send_timeout 360;
        client_max_body_size 500M;
        # fastcgi_intercept_errors on; 
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }

    location /.well-known/acme-challenge/ {
        root /var/www/letsencrypt/;
        log_not_found off;
    }

    error_log /var/log/nginx/laravel_error.log;
    access_log /var/log/nginx/laravel_access.log;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name laravel.test;
    root /var/www/public;

    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;

    index index.php index.html index.htm;

    location / {
         try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        try_files $uri /index.php =404;
        fastcgi_pass php-upstream;
        fastcgi_index index.php;
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        #fixes timeouts
        fastcgi_connect_timeout 60;
        fastcgi_read_timeout 1200;
        fastcgi_send_timeout 360;
        client_max_body_size 500M;
        # fastcgi_intercept_errors on; 
        include fastcgi_params;
    }

    location ~ /\.ht {
        deny all;
    }

    location /.well-known/acme-challenge/ {
        root /var/www/letsencrypt/;
        log_not_found off;
    }

    error_log /var/log/nginx/laravel_error.log;
    access_log /var/log/nginx/laravel_access.log;
}

Worked like a charm with certificates in place.

Root Certificate Keychain Addition

Once this is done you do need to add the root certificate to your keychain and have it accepted.