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.