Local Web Development With HTTPS

When developing a web application I usually reach a point where the backend/API is ready, and I need to develop and test its frontend with the real API in conditions as close as possible to the supposed environment that it is going to run. One of those conditions is using HTTPS to access the API.

One common solution to this problem is to use a paid service like Ngrok, to map a public HTTPS enabled URL to a localhost port.

But in this post, I will describe another option that is free and does not require access to a remote service.

I will show you how to do it in a Debian machine but it can easily be ported to other Unix derivatives like Mac OSX.

Three steps are needed:

  • Configure a local development domain name
  • Create valid local SSL certificates
  • Configure a local reverse proxy

Configure DNS

Supose the new local development domain name is api.local

As root, edit the file /etc/hosts and add a line like this:

127.0.0.1 api.local

Create Certificates With Mkcert

Follow the installation instructions at Mkcert.

Install a local Certificate Authority (CA) with:

mkcert -install

Create a new certificate/key pair with a command like this: mkcert api.local localhost 127.0.0.1 ::1

As root, copy the generated files to/etc/ssl/certs.

Configure Nginx as a reverse HTTP proxy:

Install nginx:

sudo apt install nginx

Create a new file on /etc/nginx/sites-available and name it like api-local.conf.

Copy and paste the following snippet to the configuration file. Pay attention to make the appropriate changes in:

  • local server address and port
  • log files folder
  • cert files names and folder
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
upstream api.local {
    server 127.0.0.1:3000; # http server address/port
    keepalive 32;
}
server {
    listen 80;
    server_name _;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name api.local;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header Cache-Control no-cache;
    add_header Expires: 0;

    # configure where to write logs
    access_log /var/log/nginx/api.local.access.log;
    error_log /var/log/nginx/api.local.error.log debug;

    # configure where the certs are located
    ssl_certificate /etc/ssl/certs/api.local+4.pem;
    ssl_certificate_key /etc/ssl/certs/api.local+4-key.pem;


# Defining used protocol versions.
    ssl_protocols TLSv1.3 TLSv1.2;

# Defining ciphers to use.
    ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;

# Enabling ciphers
    ssl_prefer_server_ciphers on;
    ssl_session_timeout 10m;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;

# ssl_stapling on;
    ssl_stapling_verify on;

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header  X-Forwarder-For     $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://api.local;
        proxy_redirect off;
    }
}

As root, create a symbolic link to this file in the /etc/nginx/sites-enabled folder:

ln -s /etc/nginx/sites-enabled/dev-local.conf /etc/nginx/sites-available/api-local.conf

Restart nginx:

sudo systemctl restart nginx

The configuration done so far does not need to be changed between reboots. For every development or testing session, you only need to start the local backend server. After that, the API will be available at the address https://api.local.