Ferron 3.0.0-beta.11 released

Published on:

Today, we’re releasing Ferron 3.0.0-beta.11, the eleventh beta version of what it would be Ferron 3.

This release involves significant performance improvements (especially in static file serving), but also multi-IP binding, improved cache observability, various bugfixes and more.

Benchmark spotlight: Ferron 3 performance gains

In 3.0.0-beta.11, we shifted the default I/O backend in zincio back to epoll for standard Linux workloads after extensive profiling revealed that epoll delivers lower event-loop overhead and tighter latency characteristics for page-cached static file delivery and standard reverse proxying.

To measure these improvements, we ran synthetic HTTPS benchmark suites using wrk across popular web servers under heavy concurrency.

Benchmark environment

  • Hardware: AMD Ryzen 7 8700G
  • OS / kernel: Linux 7.1 (Omarchy, based on Arch Linux)
  • Workload: Static ~2.6 KB index.html file over HTTPS (HTTP/1.1)
  • Command: wrk -c 2500 -d 10 -t 16 https://localhost/

Results

Web serverRequests/secAvg latencyMax latencyTransfer/secSocket errors
NGINX348,801.405.94 ms116.49 ms0.93 GB/s0
Ferron 3 (dev / beta.11, epoll)333,299.717.04 ms128.78 ms0.92 GB/s0
Ferron 2 (epoll)302,917.529.04 ms233.13 ms818.99 MB/s0
Ferron 3 (beta.8, epoll)293,941.508.67 ms193.63 ms831.72 MB/s0
Caddy124,584.3097.21 ms2,000.00 ms343.61 MB/s22 timeouts

Key takeaways

  1. +13.4% speedup over 3.0.0-beta.8: internal runtime refinements in zincio pushed throughput from 293.9k req/sec up to 333.3k req/sec.
  2. Reclaiming the lead over Ferron 2: 3.0.0-beta.11 officially leapfrogs Ferron 2’s baseline (302.9k req/sec) with lower average latency (7.04 ms vs 9.04 ms) and tighter tail latency.
  3. Closing the NGINX gap: Ferron 3 sits within 4.4% of NGINX’s C-based transport core while delivering memory safety, built-in LSCache support, and automated TLS out of the box.
View Tweet

Your requested features

Multi-interface binding

After we saw someone replying on Threads asking for new feature, we implemented a feature where the web server can listen on multiple IP addresses (from different network interfaces). This allows separating management traffic from public-facing traffic on a single machine.

You can use this feature like this:

{
    tcp {
        listen_ip 10.0.0.1 192.168.1.1
    }
}

Forwarded auth configuration

After seeing a GitHub issue related to OAuth2 Proxy integration, we found out that Ferron 3 didn’t support that, because forwarded authentication response interception wasn’t supported.

So we added several forwarded authentication configuration subdirectives to support applications like OAuth2 Proxy:

  • intercept_errors, which intercepts error responses for error handlers, similarly to what reverse proxy is doing
  • request_header for modifying request headers, also similar to reverse proxy

Also, X-Real-IP request header is now included by default for forward auth requests, just like the reverse proxy.

Improved cache observability

We truncated cache key (up to 48 characters) with the query string removed, useful for diagnosing why a specific request missed. Optionally with a short non-reversible tag (q=<16 hex chars>), and if the base itself had to be truncated, a second tag (h=<16 hex chars>).

Also, cache decision reasons are now attached to access log entries and cache metrics.

UX improvements

  • Empty configuration validation: added a check for empty configurations (no ports, no global directives/matchers) to return an error instead of silently starting.
  • Reduced startup logs: removed multiple startup logs for various features to reduce noise in the logs.
  • Certificate and private key mismatch fix: previously, when a certificate and private key did not match for a given host, this was silently allowed. This has been changed to error out on the startup of the server.

Bug fixes

  • Protocol + IP + port host block split fix: previously, the protocol+IP+port combination was not properly split when used as a host block (for example, http 127.0.0.1:18081 lead to two host blocks: http 127 and 0.0.1:18081).
  • request.uri interpolation fix: previously, request.uri interpolation included the full request URL, including the http/https scheme and hostname. This had been changed to only include the path and query string.
  • HTTP status code for overlong paths: previously, overlong file paths led to 500 Internal Server Error status code to be returned. This has been changed to return 400 Bad Request instead.
  • Static file serving symlink settings fix: previously, symlink mode (follow or not) was not properly respected when serving precompressed static files.

Ferron 3 is approaching stabilization

With the core engine now demonstrating high performance and stability, the focus of the remaining beta cycle is shifting. We are freezing new features and performance optimizations for now, allowing us to focus on hardening the existing codebase and preparing for our first Release Candidate (or at least getting closer to it).

The next major phase of development will focus on building out the ecosystem, specifically focusing on custom modules, to provide the extensibility and flexibility you need to build complex, modular systems on top of Ferron.

As always, feedback, bug reports, and testing results are welcome.

Full changelog

You can see the complete changelog for Ferron 3.0.0-beta.11 in the release notes.

Try it

Install Ferron 3 using the installer:

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