TLS & HTTPS
bext handles TLS natively -- no nginx, certbot cron jobs, or reverse proxy required. Configure the [tls] section in bext.config.toml and bext takes care of certificate provisioning, renewal, OCSP stapling, and HTTPS redirection.
TLS Modes
The mode field controls how bext obtains certificates:
| Mode | Behavior |
|---|---|
"auto" |
ACME certificate provisioning (Let's Encrypt by default) |
"self-signed" |
Generate a self-signed certificate on startup |
"off" |
No TLS; serve plain HTTP only (default) |
When both cert and key paths are provided, bext uses manual mode regardless of the mode setting.
Auto-TLS via ACME (Let's Encrypt)
The recommended production setup. bext requests and renews certificates automatically using the ACME protocol.
[tls]
mode = "auto"
acme_email = "ops@example.com"
On first startup, bext:
1. Generates an ACME account key and registers with the certificate authority 2. Completes the HTTP-01 challenge by serving the validation token on port 80 3. Obtains the certificate and begins serving HTTPS 4. Schedules automatic renewal before the certificate expires
Certificates are stored in the bext data directory and persist across restarts. Renewal happens in the background with zero downtime -- the new certificate is hot-swapped into the TLS acceptor without dropping connections.
Certificate Authority
By default, bext uses Let's Encrypt. You can switch to ZeroSSL or any ACME-compatible CA:
[tls]
mode = "auto"
acme_email = "ops@example.com"
acme_ca = "zerossl"
# Or use a custom ACME directory URL:
# acme_ca = "https://acme.internal.corp/directory"
Renewal Timing
The renew_before_days setting controls how many days before expiry bext triggers a renewal. The default is 30 days, which gives a comfortable buffer for Let's Encrypt's 90-day certificates.
[tls]
mode = "auto"
acme_email = "ops@example.com"
renew_before_days = 14 # Renew 2 weeks before expiry
Custom Certificates
If you obtain certificates through your own PKI, a corporate CA, or a provider like Cloudflare Origin CA, provide the PEM file paths directly:
[tls]
cert = "/etc/bext/tls/example.com.pem"
key = "/etc/bext/tls/example.com-key.pem"
When both paths are set, bext loads the certificate at startup and skips ACME entirely. It still handles OCSP stapling and HTTPS redirects. If the files change on disk (for example, an external renewal process writes new files), send SIGHUP to bext to trigger a config reload and certificate hot-swap.
For multi-domain setups, the certificate should cover all domains you serve (via SAN entries or a wildcard). In multi-app mode, each app's domains must be covered by the provided certificate.
Self-Signed Certificates (Development)
For local development and testing, bext can generate a self-signed certificate on startup:
[tls]
mode = "self-signed"
This creates an ephemeral certificate for localhost and 127.0.0.1. Browsers will show a security warning, but the connection is encrypted. The certificate is regenerated on each restart and is never persisted to disk.
This mode is useful for testing HTTPS-only features (Secure cookies, HSTS behavior, HTTP/2) during development without setting up a local CA.
OCSP Stapling
OCSP (Online Certificate Status Protocol) stapling is enabled by default when using auto or manual TLS modes. bext fetches the OCSP response from the certificate authority and staples it to the TLS handshake, so clients do not need to make a separate OCSP request.
[tls]
mode = "auto"
acme_email = "ops@example.com"
ocsp_stapling = true # Default: true
OCSP responses are cached and refreshed before they expire. If the OCSP responder is temporarily unreachable, bext continues serving with the last valid stapled response.
To disable stapling (not recommended):
[tls]
ocsp_stapling = false
SNI Routing
In multi-app mode, bext uses Server Name Indication (SNI) to determine which application should handle a connection before TLS negotiation completes. This allows serving multiple domains with different certificates from a single IP address and port.
When Auto-TLS is enabled in multi-app mode, bext provisions a separate certificate for each app's domain list. SNI selects the correct certificate during the TLS handshake based on the hostname the client requested.
HTTPS Redirect
When TLS is active (any mode except "off"), bext automatically redirects HTTP requests to HTTPS with a 301 Moved Permanently response. The redirect preserves the full request path and query string.
For the redirect to work, bext listens on both ports:
- Port 443 (or your configured listen port) for HTTPS
- Port 80 for HTTP, serving only ACME challenges and HTTPS redirects
If you are behind a load balancer that terminates TLS, set mode = "off" and let the load balancer handle HTTPS. bext will trust the X-Forwarded-Proto header to determine the original protocol for redirect logic.
Full Production Example
A complete TLS configuration for production:
[server]
listen = "0.0.0.0:443"
[tls]
mode = "auto"
acme_email = "ops@example.com"
acme_ca = "letsencrypt"
renew_before_days = 30
ocsp_stapling = true
This gives you automatic certificate management, OCSP stapling, HTTP-to-HTTPS redirect, and HTTP/2 (which is enabled by default when TLS is active) -- all with zero external dependencies.