Today, we’re releasing Ferron 3.0.0-beta.3, the third beta release of Ferron 3.
This release focuses on three major areas:
- A substantially more capable HTTP cache
- A new module for request-aware variables and custom log fields
- More flexible TLS provisioning with fallback ACME providers and on-demand certificate fetching
It also completes several transitions started in beta.2, simplifies trace configuration, and ships a new developer tool.
Breaking changes first
Before diving into what’s new, a few configuration changes to be aware of. If you’re upgrading from beta.2, review these before updating:
force_tracedirective removed - trace context is now always enabled for every request. Remove anyforce_trace trueorforce_trace falsefrom your configuration. When no tracing export is configured, Ferron will skip generating span IDs to avoid unnecessary overhead, so there is no cost to always having it on.- Default text log format changed - the default access log format has been updated from Combined Log Format to Enhanced Combined Log Format, which adds the
Hostheader and trace ID fields. If you rely on the previous format, explicitly setaccess_patternin yourlogblock to the Combined Log Format pattern. - Circuit breaker
5xxbehavior - upstream5xxresponses no longer trip the circuit breaker by default. The circuit breaker is now transport-failure-based by default. To restore the previous behavior, addrecord_5xx trueto yourcircuit_breakerblock. passive_checkdirective removed - thepassive_checkdirective has been fully removed. It was deprecated in beta.2 in favor ofcircuit_breaker, which is a strict superset. If you haven’t migrated yet, now is the time.
HTTP caching, done right
The HTTP cache in beta.3 receives its most substantial upgrade yet, covering a range of RFC 9111 behaviors that were previously unhandled.
Conditional revalidation
When a client sends Cache-Control: max-age=0 or no-cache, Ferron previously bypassed the cache entirely. Now it performs conditional revalidation using stored ETag and Last-Modified validators. If the upstream returns 304 Not Modified, the cached response body is served immediately with fresh headers. If the upstream returns a new 200 OK, the cache entry is replaced. The result is that your backend sees dramatically fewer full fetches from cache-aware clients.
Stale response handling
Two new Cache-Control directives are now respected:
stale-while-revalidate - when a cached response has expired but falls within the stale window, Ferron serves the stale response immediately and revalidates in the background. Clients don’t wait out a cache miss; the next request receives a fresh entry.
stale-if-error - when upstream revalidation returns a 5xx error, Ferron serves the cached stale response instead of surfacing the error to the client. This provides a layer of resilience against transient backend failures without any additional configuration.
Responses marked with must-revalidate, proxy-revalidate, or s-maxage are never served stale, even within these windows, as required by the RFC.
Cache purge propagation
For multi-instance deployments, cache purges can now be propagated across Ferron instances via an external control-plane service. When a PURGE request or X-LiteSpeed-Purge header is received, the instance sends a webhook POST to a configured control-plane URL, which is responsible for broadcasting the purge to other registered edges. Loop prevention is handled automatically via header tracking.
Ignoring request cache control
A new ignore_request_cache_control option in the cache block causes Ferron to ignore client-side cache directives such as Cache-Control: no-cache in favor of the configured cache policy. This is useful when you want consistent cache behavior regardless of what individual clients request.
Request-aware variables and custom log fields
Beta.3 introduces a new module, http-variables, which adds two new directives.
set_var sets an interpolation variable based on regex matching against any request attribute. Variables support optional case-insensitive matching and can be negated.
log_field maps any resolvable variable (including interpolated strings) to a named field in the access log, evaluated after the response is generated. This makes it possible to add custom structured context to every log line without touching application code.
http * {
set_var request.uri.path "\.pdf$" is_pdf
set_var remote.ip "^192\.168\." network_type {
value "private"
}
location / {
log_field file_type is_pdf
log_field network network_type
proxy http://backend {
request_header X-Internal "{{is_internal}}"
}
}
}Three new variables are also available in interpolations across all directives:
auth.user- the authenticated user identity for the current requesttrace.id- the current W3C trace IDtrace.spanid- the current request span ID
Trace context always on
The force_trace directive no longer exists. Trace context is created for every request.
This removes a longstanding source of confusion: traces missing because force_trace was absent or set to false in the wrong block. With this change, tracing behavior is unconditional and predictable. When OTLP export is configured, traces flow through as expected. When it isn’t, span ID generation is skipped automatically to keep overhead zero.
Static file caching improvements
Static file responses now support the If-Modified-Since request header. Clients can cache responses locally and avoid a full re-download when the file hasn’t changed — Ferron returns 304 Not Modified instead of re-serving the body. For sites with moderate-to-heavy static assets, this reduces both bandwidth and backend CPU load for returning visitors.
TLS becomes more flexible
Fallback ACME providers
The tls-acme module now supports sequential ACME provider failover via the fallback directive. When the primary certificate authority is unavailable, Ferron automatically tries the next configured provider in order. Each provider maintains its own credential cache independently, so a failover doesn’t invalidate existing certificates.
This is particularly valuable in environments where certificate issuance is on the critical path, such as CDN-scale deployments using on-demand TLS.
example.com {
tls {
provider acme
challenge http-01
contact "admin@example.com"
directory "https://acme-v02.api.letsencrypt.org/directory"
fallback {
directory "https://acme-staging-v02.api.letsencrypt.org/directory"
contact "admin@example.com"
}
}
}On-demand certificate fetching in tls-http
The tls-http module now supports lazy certificate retrieval. When on_demand true is set in a wildcard host block, certificates are fetched on the first TLS handshake for each SNI hostname rather than at startup. Each certificate is then refreshed individually at the configured refresh_interval, so rotation for one hostname doesn’t affect others.
An optional approval endpoint (on_demand_ask) can be configured to authorize certificate fetching per hostname before it proceeds. The hostname is passed as a ?domain= query parameter. HTTP Basic Auth and TLS verification settings are configurable for the approval endpoint independently.
*.example.com {
tls {
provider http
url "https://cert-manager.internal.example.com/api/cert"
on_demand
on_demand_ask "https://internal-api.example.com/check-cert"
on_demand_ask_auth "Bearer s3cr3t"
}
}Observability keeps improving
Reload metrics
A new module, metrics-reload, emits a ferron.reloads counter metric and structured log events around every configuration reload. The metric includes a ferron.reload.successful attribute, and failures include an error.message attribute for direct correlation with the cause.
This pairs with an update to the Admin API’s /reload endpoint, which now returns an error message in the response body when a reload fails. Previously it only confirmed that the reload was initiated, giving no indication of why it didn’t take effect.
Abuse ban logging
When http-abuseban triggers a ban, Ferron now emits a WARN-level log event that includes the banned IP address and the reason for the ban. Previously, bans happened silently.
Improved error attribution
Reverse proxy, HTTP, TCP, and QUIC error logs now include an error.message attribute when one is available. Some previously verbose reverse proxy error log summaries have also been shortened.
OCSP response caching events are now emitted at info level (previously debug), and include the ferron.ocsp.cert.primary_san attribute when available.
Reduced ACME log noise
ACME provisioning logs are now no longer emitted at debug level when a certificate is still valid or loaded from cache, as these messages were redundant with the ferron.acme.domains attribute already included in the main log message.
Custom abuse events
The http-abuseban module can now register custom abuse events from configuration, making it possible to integrate honeypot endpoints or application-level signals into Ferron’s ban logic directly — without relying on external tooling to post events back.
ferron-fmt: a configuration formatter
Beta.3 ships a new command-line utility, ferron-fmt, for formatting Ferron configuration files. Running it against a configuration file normalizes formatting and reports syntax issues before a reload or deployment. It’s a small addition, but a useful one for teams with multiple contributors to a shared configuration.
Reliability fixes
Several edge cases addressed in this release:
- HTTP/2 and HTTP/3 requests now correctly log the
Hostheader. - OCSP stapling with ECDSA certificates now correctly extracts the issuer OID, fixing incorrect OCSP validation results.
- TLS certificate provider inheritance from parent configuration blocks has been corrected.
- Error routing for wildcard hostname configurations was resolving against the wrong configuration in some cases. This has been fixed.
- A critical Unix daemon initialization fix: the forked process was previously exiting due to a logging re-initialization issue. The parent process now continues running correctly in all cases. (GitHub issue)
Looking toward stable
Beta.3 advances Ferron 3 further toward a production-ready release. The HTTP cache now handles the full range of RFC 9111 stale and revalidation behaviors. The new variables module opens up custom observability and conditional routing without adding configuration complexity. TLS provisioning is more resilient to CA outages. And removing force_trace eliminates an entire category of configuration foot-guns.
As always, feedback, bug reports, and testing results are welcome — they directly shape what goes into the stable release.
Full changelog
The complete changelog for Ferron 3.0.0-beta.3 is available in the release notes.
Try it
- Documentation: https://ferron.sh/docs/v3
- GitHub repo: https://github.com/ferronweb/ferron/tree/develop-3.x
Install Ferron 3 using the installer:
sudo bash -c "$(curl -fsSL https://get.ferron.sh/v3)"