One IP, Multiple HTTPS Domains on Nginx — SNI Solves It

In the HTTP era, virtual hosting was trivial: one IP serves many domains, the Host header tells them apart. HTTPS breaks this because TLS handshake happens before any HTTP header — the server must pick a certificate before knowing which domain the client wants.

SNI — the standard fix

Server Name Indication is a TLS extension where the client sends the target hostname during handshake. All modern clients support it. Nginx uses it automatically — just define separate server blocks:

server {
    listen 443 ssl;
    server_name a.com;
    ssl_certificate /etc/ssl/a.com.crt;
    ssl_certificate_key /etc/ssl/a.com.key;
}

server {
    listen 443 ssl;
    server_name b.com;
    ssl_certificate /etc/ssl/b.com.crt;
    ssl_certificate_key /etc/ssl/b.com.key;
}

Each domain gets its own certificate. Nginx routes based on SNI. Zero extra config needed.

When you'd rather use one certificate

For a handful of related domains, a SAN certificate (Subject Alternative Name) covers multiple names in one cert:

server {
    listen 443 ssl;
    server_name a.com b.com c.com;
    ssl_certificate /etc/ssl/multi.crt;
    ssl_certificate_key /etc/ssl/multi.key;
}

Let's Encrypt makes this painless:

certbot --nginx -d a.com -d b.com -d c.com

For lots of subdomains (*.example.com), a wildcard certificate is the way to go.

SNI + Let's Encrypt covers 99% of real-world setups — free and automatic.

Git alias could run in dash, not bash — even if `/bin/bash` exists

I Wrote a git alias using [[ ... ]] and =~. Works fine in my interactive bash. Run the alias and it explodes:

$ git co master
... Syntax error: "(" unexpected (expecting "then")

First instinct: "but I have bash installed":

$ ls -l /bin/bash
-rwxr-xr-x 1 root root 1298416 ... /bin/bash
$ /bin/bash --version
GNU bash, version 5.2.37(1)-release ...

Doesn't matter. A git alias starting with ! is hardcoded to run under /bin/sh — it doesn't read $SHELL, doesn't care what your login shell is. On Debian/Ubuntu /bin/sh -> dash, and dash doesn't understand [[, =~, == or other bash extensions:

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 ... /bin/sh -> dash
$ echo '[[ "a" == a* ]]' | /bin/sh
/bin/sh: 1: [[: not found

The source is run-command.c::prepare_shell_cmd() in git itself — it literally calls sh -c.

Two fixes:

Wrap in bash -c explicitly (minimal change, but the nested quoting inside an alias gets ugly fast):

co = "!bash -c 'f() { ...bash syntax... }; f \"$@\"' _"

Rewrite as POSIX sh (preferred). Common substitutions:

  • [[ "$x" == -* ]]case "$x" in -*) ... ;; esac
  • [[ "$x" =~ ^HEAD~ ]]case "$x" in HEAD~*) ... ;; esac
  • [[ "$a" == "$b" ]][ "$a" = "$b" ]
  • [[ -f foo ]][ -f foo ] (already POSIX, no reason to use [[)

Lesson: write git aliases as if dash is the only shell on the planet. Don't reach for [[ because it feels nicer — either wrap with bash -c or just use case.

Run Multiple Claude Code Instances with Separate Configs

Use CLAUDE_CONFIG_DIR to point Claude Code at a different config directory — credentials, settings, sessions all go there instead of ~/.claude.

mkdir -p ~/.claude-work
CLAUDE_CONFIG_DIR=$HOME/.claude-work claude

Set up aliases in your shell config for quick switching:

alias claude-work='CLAUDE_CONFIG_DIR=$HOME/.claude-work claude'

First launch in the new directory triggers the login flow — that's how you get account isolation. Both instances can run simultaneously without interference.

Share session history between instances

CLAUDE_CONFIG_DIR is all-or-nothing — no built-in way to share just sessions. Symlink the projects/ directory back to the original to get shared session history with separate settings:

mkdir -p ~/.claude-work
cp ~/.claude/settings.json ~/.claude-work/settings.json
ln -s ~/.claude/projects ~/.claude-work/projects

This gives you independent config/credentials but a unified session list. New sessions written by either instance appear in both (bidirectional). No read-only option exists — if you need isolation, don't symlink.

WSL SSL Certificate Errors on Corporate Networks

If curl throws SSL certificate problem: unable to get local issuer certificate every time in WSL, it's usually a stale CA bundle — especially common on corporate networks running SSL inspection (Zscaler, etc.).

First, refresh the bundle:

sudo apt-get update && sudo apt-get install -y ca-certificates
sudo update-ca-certificates

That fixes most cases. If not, try a full reinstall:

sudo apt-get install --reinstall ca-certificates
sudo update-ca-certificates --fresh

On corporate networks, you likely need your company's root CA certs. You probably already have them somewhere in your Windows filesystem (.crt or .cer files). Copy them all into the trusted store:

sudo cp /c/Certificates/*.crt /usr/local/share/ca-certificates/
sudo cp /c/Certificates/*.cer /usr/local/share/ca-certificates/
sudo update-ca-certificates

Skip .pfx files — those contain private keys and are a different format.

If you don't have the certs handy, export them from Windows:

# PowerShell — list all root certs, look for your company name
Get-ChildItem Cert:\LocalMachine\Root | Select-Object Subject, Issuer | Sort-Object Subject

Or use certmgr.msc (Win+R → certmgr.msc) → Trusted Root Certification Authorities → find your company's cert → right-click → Export → Base-64 encoded X.509.

Test with curl https://google.com after each step.

whoseport: Find What's Listening on a Port (With Working Directory)

ss -tlnp and lsof -i :PORT tell you the PID and command name, but for Node.js or Python processes, "node" or "python" alone doesn't tell you which project is running. The working directory is what you actually need — and it's sitting right there in /proc/$pid/cwd.

Put this in ~/.bashrc:

whoseport() {
  if [ -z "$1" ]; then
    sudo ss -tlnp | tail -n +2 | while read -r line; do
      port=$(echo "$line" | grep -oP ':\K[0-9]+(?=\s)')
      pid=$(echo "$line" | grep -oP 'pid=\K[0-9]+' | head -1 | tr -dc '0-9')
      [ -n "$pid" ] && echo "PORT: $port | PID: $pid | CMD: $(ps -p $pid -o comm=) | CWD: $(readlink /proc/$pid/cwd)"
    done
  else
    whoseport | grep 'PORT: '$1
  fi
}

Usage:

$ whoseport 6173
PORT: 6173 | PID: 1326886 | CMD: node | CWD: /home/davidw/Projects/ccode_viewer/server

$ whoseport          # list all listening ports
PORT: 61217 | PID: 1356 | CMD: tailscaled | CWD: /
PORT: 22     | PID: 1385  | CMD: sshd      | CWD: /

A few things that went wrong before arriving at this version:

Don't use an alias for this — $1 in a single-quoted alias gets swallowed by inner sh -c calls, and local variables don't survive xargs boundaries. A function avoids both problems. The tr -dc '0-9' on the PID is not cosmetic — ss output can carry trailing whitespace or newlines that break ps -p with a "process ID list syntax error".