Ferron 3.0.0-rc.7 released

Published on: Category: Releases

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:

  • status rules can define a name, which is exposed as the ferron.rule_id metric label.
  • rewrite rules can define a name, exposed across rewrite metrics, spans, logs, and access-log fields.
  • Chained rewrites report each matched rule and the from and to URLs 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_duration and the upstream connection_timeout explicitly if you relied on the old defaults.
  • Add append_uri if forwarded authentication needs the original path in the redirect URL.
  • Update dashboards that filter ferron.rule_id by a bare status code.
  • Update log filters if you need rewrite_log output at INFO; it now logs at DEBUG.
  • 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

Install Ferron 3 using the installer:

sudo bash -c "$(curl -fsSL https://get.ferron.sh/v3)"