Running a multi‑site reverse proxy on a Raspberry Pi is a classic hobby‑project. The Pi’s low power draw and cheap price make it a go‑to for hosting tiny web services, internal dashboards, or a personal API gateway. Throw TLS in there and you get confidentiality and integrity, but one small mis‑configuration can make every HTTPS request hang until the client gives up.
The usual culprit is the dance between SNI, OpenSSL’s default cipher selection, and the Pi’s limited CPU. Below I’ll walk through why this happens, how to spot it, and the practical tweaks that keep the Pi from blowing up the handshake.
Why the Pi struggles with SNI‑based TLS handshakes
SNI lets a client tell the server which hostname it wants during the TLS handshake. Nginx uses that to pick the right certificate from a list of server blocks. On a desktop or cloud VM the handshake is a handful of microseconds. On a Pi the same handshake can take 200 ms–400 ms when the server must do a full RSA or ECDHE key exchange for each hostname.
The slowdown comes from a few things:
| Factor | What it does | Typical impact on a Pi |
|---|---|---|
| RSA key exchange | 2048‑bit or 4096‑bit modular exponentiation | ~100 ms per handshake |
| ECDHE with curve P‑256 | Elliptic‑curve scalar multiplication | ~50 ms per handshake |
| Cipher suite negotiation | OpenSSL may try several suites | Adds 10–20 ms per attempt |
| CPU throttling | ARM Cortex‑A72 runs at 1.5 GHz but throttles under load | Latency can double |
When you add a second site, Nginx must evaluate ssl_certificate and ssl_certificate_key for every server block. If the certificates are large (e.g., 4096‑bit RSA) or the server falls back to a slower cipher, the cumulative cost pushes the handshake past the client’s default timeout (often 30 s). Browsers will show “Connection timed out” or “The connection was reset”.
Detecting the symptom
-
Client‑side logs
Chrome’s DevTools → Network → click the failed request → look at the “Status” column. A408 Request TimeoutorERR_CONNECTION_TIMED_OUTmeans the TLS layer never finished. -
Server‑side timing
Useopenssl s_clientto time the handshake:time openssl s_client -connect example.com:443 -servername example.comIf the
timeoutput shows > 30 s, you’ve hit the timeout. -
Nginx access logs
Add$ssl_handshake_timeto your log format:log_format ssl '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '$ssl_handshake_time';A
-means the handshake never finished.
Common mis‑configurations that trigger timeouts
| Mis‑config | Why it hurts | Fix |
|---|---|---|
Single ssl_certificate block for all sites |
Nginx still performs a full SNI lookup, but the same key is read for every request, causing extra disk I/O | Separate ssl_certificate directives per server block |
| Storing certificates on a slow SD card | Each handshake reads the key file from flash, adding ~10 ms per request | Move certificates to RAM (/dev/shm) or use a faster SD card (UHS‑I) |
Leaving ssl_ciphers at the default |
OpenSSL may try many ciphers before finding a match | Explicitly set a short, secure list: ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'; |
Enabling ssl_prefer_server_ciphers on; on a Pi |
Forces the server to try its cipher list first, which may include slower suites | Disable or reorder the list to favor fast ECDHE suites |
| Using 4096‑bit RSA keys | RSA key exchange is CPU‑heavy | Switch to 2048‑bit RSA or, better, ECDSA/ECDHE |
Practical mitigation steps
1. Switch to ECDHE‑RSA or ECDSA
ECDHE with a 256‑bit curve is roughly 5× faster than RSA key exchange on ARM. Generate a new key pair:
openssl ecparam -name prime256v1 -genkey -noout -out ecdsa.key
openssl req -new -x509 -key ecdsa.key -out ecdsa.crt -days 365
Configure Nginx:
ssl_certificate /etc/nginx/ssl/ecdsa.crt;
ssl_certificate_key /etc/nginx/ssl/ecdsa.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers off;
2. Pin the cipher list to fast suites
A minimal, secure list eliminates the “try‑and‑fail” overhead:
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
Add ssl_session_cache shared:SSL:10m; to enable session resumption, which skips the full handshake on subsequent connections.
3. Use ssl_session_tickets wisely
Session tickets are encrypted on the fly. On a Pi, generating the ticket key is cheap, but decrypting it can be expensive if the key is large. Keep the key small (e.g., 256 bits) and rotate it nightly:
openssl rand -hex 32 > /etc/nginx/ssl/ticket.key
Then in Nginx:
ssl_session_tickets on;
ssl_session_ticket_key_file /etc/nginx/ssl/ticket.key;
4. Move certificates to RAM
If your SD card is slow, copy certificates to /dev/shm at boot:
mkdir -p /dev/shm/ssl
cp /etc/nginx/ssl/*.crt /dev/shm/ssl/
cp /etc/nginx/ssl/*.key /dev/shm/ssl/
Update the ssl_certificate paths accordingly.
5. Tune the Pi’s CPU governor
The default ondemand governor throttles the CPU under load. Switch to performance for short bursts:
echo performance | sudo tee /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
Add this to /etc/rc.local to persist across reboots.
6. Verify with openssl s_client
After each change, run:
openssl s_client -connect example.com:443 -servername example.com -tls1_3
Check that the handshake completes within 2 s. Use sshd -T to confirm that Nginx is listening on the correct ports.
Trade‑offs to consider
| Change | Benefit | Risk / Caveat |
|---|---|---|
| ECDHE‑RSA over RSA | Faster handshakes, forward secrecy | Requires clients that support ECDHE (most modern browsers). |
| Short cipher list | Eliminates negotiation overhead | May exclude older clients; test with sslscan. |
| RAM‑based certs | Removes disk latency | Certificates are lost on reboot unless backed up. |
| Performance governor | Faster CPU | Higher power draw; may trigger thermal throttling on older Pi models. |
Always balance performance against the attack surface. For example, disabling ssl_prefer_server_ciphers may expose the server to weaker client‑chosen ciphers if the client is compromised. Keep the cipher list tight but inclusive of all clients you expect to serve.
Real‑world example: a Pi‑based API gateway
A security engineer at a small fintech firm runs an Nginx gateway on a Pi Zero W to expose two internal services:
api.internal.company.com– a Node.js microservicedocs.internal.company.com– a static S3‑like bucket
Both services require TLS. After adding SNI, all API calls from the corporate VPN timed out after 30 s. The engineer applied the steps above:
- Generated ECDSA certificates for both hosts.
- Configured a minimal cipher list and enabled session resumption.
- Moved certs to
/dev/shm. - Switched the governor to
performance.
Post‑fix, the average handshake dropped from 350 ms to 45 ms, and API latency improved from 1.2 s to 0.3 s. No security regressions were observed, and the gateway remained compliant with the firm’s internal policy (see NIST SP 800‑53 Rev.5).
Quick checklist for production
- Use ECDHE‑RSA or ECDSA certificates (2048‑bit RSA is acceptable if ECDHE is unavailable).
- Pin the cipher list to fast, secure suites.
- Enable
ssl_session_cacheandssl_session_tickets. - Store certificates in RAM or a fast SD card.
- Set the CPU governor to
performancefor short bursts. - Test with
openssl s_clientand monitor$ssl_handshake_timein logs. - Verify that all clients (browsers, mobile apps, internal tools) support the chosen ciphers.
Final thoughts
SSL/TLS handshakes on a Raspberry Pi are a classic example of how cryptographic choices interact with hardware limits. By understanding the cost of each step in the handshake and applying targeted optimizations, you can keep your multi‑site Nginx deployment responsive without compromising security.
When you add SNI, you’re not just telling Nginx which certificate to use; you’re also triggering a full cryptographic handshake for each hostname. On a low‑power device, that handshake can become the bottleneck. The fixes above are proven in the field and align with best practices from NIST and OWASP.
See also
- The WAF Is Learning: How ML and Behavioral Analysis Are Reshaping Application Protection
- Cleaning up orphaned swap files that bloat disk space on a 2 GB VPS.
- How to Detect and Fix a Silent NFS Export Leak on a Home NAS
- Configuring systemd‑resolved to Use a Custom DNS Server Only on the VPN Interface
- How to Give the Backup User Read‑Only Access to /var/log via ACLs Without Granting sudo