Charger control needs TLS, and the stack speaks plain HTTP, so the README said "terminate TLS in a reverse proxy" and left the operator to work out which four settings have to agree. This adds the proxy: a Caddy overlay that fronts the Web App BFF — which already carries /api/ and /ocpp/ — so browsers and chargers arrive at the same name and the certificate is issued on first boot. The four settings are derived from DV_DOMAIN, since one value getting typed right is better odds than four: OCPP_PUBLIC_URL, OCPP_REQUIRE_TLS back on, CORS, and TRUST_FORWARDED_PROTO on the Web App. That last one is the non-obvious one — without it the BFF overwrites Caddy's X-Forwarded-Proto with its own plaintext hop and the API Server rejects the charger it just told to connect over wss. The README's OCPP section was stale besides: it still sent chargers to the API Server port alone, from before the BFF carried that path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
25 lines
943 B
Caddyfile
25 lines
943 B
Caddyfile
# Caddy in front of the stack: one hostname, TLS from Let's Encrypt, everything
|
|
# behind it spoken to over the compose network in plain HTTP.
|
|
#
|
|
# Both doors are the same door here. Browsers get the Web App; chargers dial
|
|
# /ocpp/{serial} on the same hostname, and the BFF carries that through to the
|
|
# API Server. Caddy proxies WebSocket upgrades without being told to, so there
|
|
# is nothing to configure for the charger case.
|
|
#
|
|
# DV_DOMAIN and DV_ACME_EMAIL come from .env via docker-compose.tls.yml.
|
|
{
|
|
email {$DV_ACME_EMAIL}
|
|
}
|
|
|
|
{$DV_DOMAIN} {
|
|
encode zstd gzip
|
|
|
|
# X-Forwarded-Proto is set by Caddy to the scheme the client used. The API
|
|
# Server reads it to decide a charger arrived over TLS, which is why the
|
|
# web-app service is given TRUST_FORWARDED_PROTO=true — without that the BFF
|
|
# would overwrite this header with its own plaintext hop and a charger would
|
|
# be rejected under OCPP_REQUIRE_TLS.
|
|
reverse_proxy web-app:8090
|
|
}
|