Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Simply add `VIRTUAL_HOST=myapp.local` to any container or use native Traefik lab
- [HTTPS Support](#https-support)
- [Automatic HTTP and HTTPS Routes](#automatic-http-and-https-routes)
- [Self-Signed Certificates](#self-signed-certificates)
- [HSTS Headers Disabled for Development](#hsts-headers-disabled-for-development)
- [Disabling HTTP/2 (workaround for stuck HTTPS sessions on macOS)](#disabling-http2-workaround-for-stuck-https-sessions-on-macos)
- [Trusted Local Certificates with mkcert](#trusted-local-certificates-with-mkcert)
- [Manual Certificate Generation (Alternative)](#manual-certificate-generation-alternative)
- [Start the proxy](#start-the-proxy)
Expand Down Expand Up @@ -420,6 +422,27 @@ Traefik automatically generates self-signed certificates for HTTPS routes. For t

This is implemented using Traefik's `disable-hsts` middleware applied to the HTTPS entrypoint, ensuring **all HTTPS traffic** (both dinghy-layer and native Traefik routes) benefits from this development-friendly configuration. This is essential for development environments where certificates may frequently change, expire, or be regenerated.

### Disabling HTTP/2 (workaround for stuck HTTPS sessions on macOS)

On macOS with Docker Desktop, long-lived HTTP/2 sessions between browsers and the proxy can become "zombies": the host-side TCP socket through vpnkit stays alive while the container-side socket has already closed, and no FIN/RST propagates across the loopback shim. The browser keeps multiplexing new requests into a dead session, and **all** `.loc` hosts time out (`ERR_TIMED_OUT` in Chrome, equivalent in Firefox) until the browser is fully restarted. Command-line clients (curl, wget) are unaffected because they open fresh connections per request.

Setting `HTTP_PROXY_DISABLE_HTTP2=true` on the `traefik` service restricts TLS ALPN negotiation to `http/1.1`. Browsers fall back to HTTP/1.1 over TLS, which detects dead connections aggressively at the socket layer and avoids the zombie-session class of bugs. HTTPS keeps working normally; only the wire protocol version changes.

```yaml
services:
traefik:
environment:
- HTTP_PROXY_DISABLE_HTTP2=true
```

Or via a top-level `.env`:

```
HTTP_PROXY_DISABLE_HTTP2=true
```

The default is `false`, so existing installations are unaffected. To verify the option is active, load any HTTPS site through the proxy and check the protocol column in your browser's network panel — it should report `HTTP/1.1` instead of `h2`.

### Trusted Local Certificates with mkcert

For browser-trusted certificates without warnings, use the `spark-http-proxy generate-mkcert` command. This command automatically handles the entire certificate generation process:
Expand Down
29 changes: 29 additions & 0 deletions build/traefik/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
CERTS_DIR="/traefik/certs"
DYNAMIC_DIR="/traefik/dynamic"
TLS_CONFIG_FILE="${DYNAMIC_DIR}/auto-tls.yml"
DISABLE_HTTP2_FILE="${DYNAMIC_DIR}/disable-http2.yml"

generate_tls_config() {
echo "Scanning for certificates in ${CERTS_DIR}..."
Expand Down Expand Up @@ -88,6 +89,34 @@ EOF
# Generate TLS configuration from user certificates
generate_tls_config

# Optionally restrict TLS ALPN to HTTP/1.1 only. Useful on macOS with Docker
# Desktop, where long-lived HTTP/2 sessions across vpnkit can become stuck
# (kernel-side socket alive, container-side closed, no FIN/RST propagated)
# and cause browser timeouts on all .loc hosts until the browser is killed.
# When set, clients fall back to HTTP/1.1 over TLS, which detects dead
# connections aggressively at the socket layer.
generate_disable_http2_config() {
if [ "${HTTP_PROXY_DISABLE_HTTP2:-false}" = "true" ]; then
echo "Disabling HTTP/2 (HTTP_PROXY_DISABLE_HTTP2=true): forcing ALPN to http/1.1"
cat > "${DISABLE_HTTP2_FILE}" << 'EOF'
# Auto-generated by entrypoint.sh when HTTP_PROXY_DISABLE_HTTP2=true.
# Restricts the TLS ALPN list to http/1.1, preventing HTTP/2 negotiation
# on TLS entrypoints.
tls:
options:
default:
alpnProtocols:
- http/1.1
EOF
Comment on lines +98 to +110
else
# Make sure we don't keep a stale file from a previous run with the
# flag enabled.
rm -f "${DISABLE_HTTP2_FILE}"
fi
}

generate_disable_http2_config

# Start Traefik with the original arguments
echo "Starting Traefik..."
exec traefik "$@"
1 change: 1 addition & 0 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ services:
- "${LOCAL_HOME:-$HOME}/.local/spark/http-proxy/certs:/traefik/certs:ro"
environment:
- LOG_LEVEL=${LOG_LEVEL:-info}
- HTTP_PROXY_DISABLE_HTTP2=${HTTP_PROXY_DISABLE_HTTP2:-false}
labels:
- "traefik.enable=false"
restart: unless-stopped
Expand Down
Loading