Today, we’re releasing Ferron 3.0.0-rc.7, the seventh release candidate of Ferron 3.
This release improves how Ferron handles changing backends, protocol errors, cache behavior, and abuse protection. It also adds better attribution for status and rewrite rules, which should make diagnosing production traffic easier.
More resilient reverse proxying
Reverse proxies have to deal with backends that restart, reload, or close pooled connections at inconvenient moments. Ferron 3.0.0-rc.7 makes those transitions less disruptive.
The new retry_interval directive adds a delay between retries to the same upstream, with up to 25% jitter. This helps mask brief backend restarts such as Docker Compose recreates and Apache graceful reloads. The default is 200ms; set it to "0s" to disable the wait.
example.com {
proxy {
upstream http://localhost:3000
retry_interval "200ms"
}
}The default circuit-breaker open duration is now 5s, down from 30s, and the upstream connection timeout is now 2s, down from 5s. This lets Ferron recover faster from short backend interruptions. If your deployment needs the previous timing, set the values explicitly.
Ferron also no longer counts the first failure on a reused keepalive connection against the circuit breaker when it can retry against the same upstream. A dead pooled socket should not be able to open the breaker when the retry succeeds.
Better observability when things go wrong
HTTP/2 and HTTP/3 stream failures are now sent to the configured observability sinks. These records include client and server addresses, making protocol errors easier to correlate with the request that triggered them.
Connection-level HTTP/2 and HTTP/3 protocol errors are also returned as errors instead of closing silently. Ferron can therefore log a descriptive connection error rather than leaving you with an unexplained disconnect.
There are also more names and context in the metrics and logs:
statusrules can define aname, which is exposed as theferron.rule_idmetric label.rewriterules can define aname, exposed across rewrite metrics, spans, logs, and access-log fields.- Chained rewrites report each matched rule and the
fromandtoURLs for every rewrite step.
The ferron.rule_id value no longer duplicates the HTTP status code. Use http.response.status_code for the status code, or update dashboards to use the new rule names and generated identifiers.
Cache behavior that matches the deployment
Ferron now understands the LiteSpeed Cache ~s staleness suffix and can store responses using X-LiteSpeed-Vary: value=<name>.
X-LiteSpeed-Vary responses are partitioned by the corresponding set_var request variable. This allows cache variation to work with applications that use LiteSpeed-compatible cache headers, while keeping the cache behavior predictable when no variable is set.
Purge operations marked with stale now expire matching entries in place instead of deleting them. Entries continue to serve stale content while revalidating, or when a stale window applies, and miss normally when no stale content is available.
Abuse protection without flooding the logs
Ban and rate-limit rejection logging is now transition-oriented. Bans log when they are triggered and expired, and Ferron exposes an ferron.abuseban.active_bans gauge. Rate limiting logs the first rejection for a key and then at most once per minute, without including the raw key.
This prevents an abuse wave from generating a debug log for every rejected request and exhausting the observability pipeline. If you need per-request rejection logs while investigating an incident, enable log_rejections in the relevant block:
example.com {
rate_limit {
log_rejections
}
}The same option is available for abuse_protection.
Forwarded authentication compatibility
Forwarded authentication no longer appends the original URI path to the backend URL by default. Ferron now relies on the X-Forwarded-Uri header instead.
If your authentication service or existing setup depends on the previous behavior, enable append_uri:
example.com {
auth_to http://localhost:9000/auth {
append_uri
}
}Important for upgrades
This release contains breaking changes. Before upgrading, review these items:
- Set
circuit_breaker.open_durationand the upstreamconnection_timeoutexplicitly if you relied on the old defaults. - Add
append_uriif forwarded authentication needs the original path in the redirect URL. - Update dashboards that filter
ferron.rule_idby a bare status code. - Update log filters if you need
rewrite_logoutput atINFO; it now logs atDEBUG. - Review rate-limit and ban dashboards for the new transition-oriented logging and active-ban gauge.
The release also fixes HTTP/2 flow-control issues, active request metric counts, Admin API and Prometheus listener races during reloads, LSCache stale purges, ban eviction races, reverse proxy stalls, and canceled reverse proxy spans.
Ferron 3 stable coming soon
Ferron 3 is continuing to stabilize as we approach the first stable release. The release candidate phase focuses on correctness, resilience, and making Ferron easier to operate in production.
As always, I welcome feedback, bug reports, and testing results.
Full changelog
You can see the complete changelog for Ferron 3.0.0-rc.7 in the release notes.
Try it
- Documentation: https://ferron.sh/docs
- 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)"