Skip to content

Enabling HTTPS on the OpenFlexure Microscope (microscope.local)

This guide documents how to serve the OpenFlexure Microscope web interface over HTTPS on a local network using nginx as a reverse proxy and mkcert as a local certificate authority (CA).

Because microscope.local is an mDNS name that only exists on your LAN, no public certificate authority (such as Let’s Encrypt) can issue a certificate for it. Instead, we create our own private CA, issue a certificate for the microscope, and install the CA’s root certificate on any device that should trust it. The result is a clean padlock in the browser with no security warnings.

Overview

Browser ──HTTPS (443)──▶ nginx (Raspberry Pi) ──HTTP (5000)──▶ OpenFlexure server
  • The OpenFlexure web app continues to run unchanged on port 5000 (plain HTTP).
  • nginx terminates TLS on port 443 and proxies requests to port 5000.
  • The certificate is issued by a private mkcert CA that you control.

Prerequisites

  • Raspberry Pi running the OpenFlexure Microscope software, reachable at microscope.local (port 5000 serving the web interface).
  • SSH access to the Pi (user pi in this guide).
  • A laptop or desktop where you will run mkcert (the “trusted machine”).
  • Both machines on the same local network.

Hardware Setup

The microscope used in this guide is an OpenFlexure Microscope with a 3D-printed body, driven by a Raspberry Pi with a motor controller board (Sangaboard-style HAT) mounted on top.

Full microscope assembly with stepper motors, next to the development laptop

The assembled microscope: the 3D-printed body houses three 28BYJ-48 stepper motors for the X/Y/Z stage, with the Raspberry Pi and motor controller board sitting in the printed electronics enclosure at the base.

Raspberry Pi with motor controller HAT and stepper motor wiring in the base enclosure

The electronics stack in the base: the Raspberry Pi (bottom, with the camera ribbon cable attached) and the motor controller board (top). The stepper motors connect via the white JST connectors; the black cable is wired Ethernet.

Close-up of the motor controller board pin labels

Close-up of the motor controller board’s auxiliary header (3V3, 5V, GND, SDA, SCL, SPI, ADC, and PWM breakouts) with the stepper motor wiring passing over the Pi’s USB and Ethernet ports.

Ethernet cable connected to the laptop

For a reliable connection during setup, the microscope can be reached over wired Ethernet — either through the LAN router or directly to a laptop. When connected directly, mDNS still resolves microscope.local as long as both interfaces are link-local or on the same subnet.

Step 1 — Install nginx on the Raspberry Pi

On the Pi:

sudo apt update
sudo apt install nginx -y

Verify it is running:

sudo systemctl status nginx

Step 2 — Create a local CA and certificate with mkcert

On your laptop/desktop (not the Pi):

# Install mkcert (Debian/Ubuntu)
sudo apt install mkcert libnss3-tools

# Create a local CA and install it into this machine's trust stores
mkcert -install

# Issue a certificate for the microscope
mkcert microscope.local "*.local" 192.168.1.50

Replace 192.168.1.50 with the microscope’s actual IP address (find it on the Pi with hostname -I). Including the IP means HTTPS also works when browsing by IP address.

This produces two files (the +2 suffix reflects the two extra names):

  • microscope.local+2.pem — the certificate
  • microscope.local+2-key.pem — the private key

Step 3 — Copy the certificate and key to the Pi

From the laptop:

scp microscope.local+2.pem microscope.local+2-key.pem pi@microscope.local:~

Step 4 — Install the files in the standard locations

On the Pi:

sudo mv ~/microscope.local+2.pem /etc/ssl/certs/
sudo mv ~/microscope.local+2-key.pem /etc/ssl/private/

sudo chown root:root /etc/ssl/certs/microscope.local+2.pem /etc/ssl/private/microscope.local+2-key.pem
sudo chmod 644 /etc/ssl/certs/microscope.local+2.pem
sudo chmod 600 /etc/ssl/private/microscope.local+2-key.pem

The private key must be readable only by root (600). The certificate itself is public and can be world-readable (644).

Step 5 — Configure nginx as a reverse proxy

Edit the default site configuration:

sudo nano /etc/nginx/sites-available/default

Add (or replace with) the following server block:

server {
    listen 443 ssl;
    server_name microscope.local;

    ssl_certificate     /etc/ssl/certs/microscope.local+2.pem;
    ssl_certificate_key /etc/ssl/private/microscope.local+2-key.pem;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # Required for the live camera stream / WebSocket connections
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

# Optional: redirect plain HTTP to HTTPS
server {
    listen 80;
    server_name microscope.local;
    return 301 https://$host$request_uri;
}

Test the configuration and reload nginx:

sudo nginx -t && sudo systemctl reload nginx

Expected output:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Step 6 — Verify

From the machine where you ran mkcert -install, browse to:

https://microscope.local

You should see the OpenFlexure interface with a padlock and no warnings — and no port number needed.

Step 7 — Trust the CA on other devices

mkcert -install only installs trust on the machine where it was run. Every other device needs the CA root certificate (not the microscope’s certificate) installed once:

  1. Locate the CA root: run mkcert -CAROOT on the trusted machine. The file is rootCA.pem in that directory.
  2. Install it per device:
  3. Android: copy the file to the device, then Settings → Security → Install a certificate → CA certificate.
  4. iOS: send the file (AirDrop/email), install the profile, then also enable it under Settings → General → About → Certificate Trust Settings.
  5. Firefox (Linux): if mkcert -install did not cover it, import rootCA.pem manually via Settings → Privacy & Security → Certificates → View Certificates → Authorities → Import, and tick “Trust this CA to identify websites”.
  6. Windows/macOS: double-click rootCA.pem and add it to the system trust store (Trusted Root Certification Authorities / System keychain, “Always Trust”).

Security note: never distribute rootCA-key.pem (the CA’s private key). Anyone holding it can issue certificates your devices will trust. Keep it on the trusted machine only. Sharing rootCA.pem is safe.

Optional — Password-protecting a static HTML page with encrypt.js

Transport encryption (HTTPS) protects data in transit, but anyone on the LAN can still open the microscope’s pages. For static HTML pages that should require a key to view — for example an index.html with notes or captured results — the companion script encrypt.js encrypts the page itself into a single self-contained file that prompts for a key in the browser before rendering.

How it works: the page content is encrypted with AES-256-GCM, with the key derived from your password via PBKDF2 (SHA-256, 250,000 iterations by default). The output is one .locked.html file containing only the ciphertext and the decryption logic — no plaintext, no dependencies, no server-side component. Decryption happens entirely in the browser using the Web Crypto API.

Requirements

  • Node.js on the machine where you encrypt (no npm packages needed).
  • The locked page must be opened in a secure context — served over HTTPS or localhost — because browsers only expose Web Crypto there. This is exactly what the nginx HTTPS setup above provides.

Usage

Interactive (prompts for a key, masked):

node encrypt.js index.html
# → writes index.locked.html

Explicit output name and scripted use (key from the environment):

LOCK_KEY='your-key' node encrypt.js index.html index.locked.html < /dev/null

Stronger key stretching:

node encrypt.js index.html --iterations 600000

Verifying a key without a browser

If the browser reports “Wrong key”, it can be hard to tell a mistyped key apart from an environment problem (e.g. the page opened via file:// where Web Crypto is unavailable). The --check mode answers this definitively:

LOCK_KEY='your-key' node encrypt.js --check index.locked.html
  • ✔ Key is CORRECT — the key is right; if the browser still refuses, serve the file over HTTPS/localhost instead of opening it directly.
  • ✘ Key is WRONG — the key does not match this file (exit code 2).

Deploying on the microscope

Encrypt the page, then serve the locked file through nginx like any other static file:

LOCK_KEY='your-key' node encrypt.js index.html index.locked.html < /dev/null
scp index.locked.html pi@microscope.local:/var/www/html/

Browsing to https://microscope.local/index.locked.html then shows the key prompt; the page renders only after the correct key is entered.

Notes: this protects the page content, not the OpenFlexure API itself — the microscope’s control endpoints on port 5000 remain open on the LAN. Also, keys are not recoverable: if the key is lost, the content cannot be decrypted. Keep an unencrypted copy of the source page somewhere safe.

Troubleshooting

Symptom Cause Fix
SSL_ERROR_RX_RECORD_TOO_LONG Browser speaking HTTPS to a plain-HTTP port (e.g. https://microscope.local:5000) Use https://microscope.local (port 443 via nginx), or disable the browser’s HTTPS-Only Mode for direct port-5000 access
cannot load certificate ... BIO_new_file() failed from nginx -t Certificate/key not at the path in the config (often still in ~/ after scp) ls -l ~/ \| grep pem, move files with sudo mv as in Step 4, ensure filenames in the config match exactly
Certificate warning on another device CA root not installed on that device Install rootCA.pem as in Step 7
Video stream does not load over HTTPS WebSocket upgrade headers missing Ensure the Upgrade/Connection headers from Step 5 are present, then reload nginx
Works by name but not by IP IP not included in the certificate Re-run mkcert microscope.local "*.local" <current-IP> and repeat Steps 3–5

Certificate renewal

mkcert certificates are valid for about 27 months. When the certificate expires, repeat Steps 2–5 with the same command — devices that already trust the CA will not need any changes.

Physical security

Use the Waveshare USB 3.2 Gen1 HUB Gigabit ETH HAT. https://www.waveshare.com/pcie-to-gigabit-eth-usb3.2-hat-plus.htm

Connect laptop to microscope using a crossover Ethernet cable 1Gigabit speed.

Gigabit speed is needed for the camera stream.


Last update: July 16, 2026