Ferron — schneller, speichersicherer Webserver in Rust

Praxis-Guide zu Ferron, dem speichersicheren Rust-Webserver mit automatischem TLS, HTTP/2 und KDL-Konfiguration — Reverse Proxy, PHP, Security und mehr.

Ferron ist ein schneller, speichersicherer Webserver, der komplett in Rust geschrieben ist – ganze Klassen von Speicherfehlern fallen damit von vornherein weg. Statische Dateien, Reverse Proxy, PHP über FastCGI, automatisches TLS und Rate-Limiting deckt er aus einer Hand ab, ohne dass du Module nachladen musst. Konfiguriert wird alles über eine einzige, gut lesbare KDL-Datei (ferron.kdl) – deutlich kompakter als klassische Apache- oder NGINX-Configs. Dieser Guide führt dich von der Installation über die Service-Verwaltung bis zu den wichtigsten KDL-Direktiven für TLS, Proxying und Security.

Installation (Debian/Ubuntu)

Fügt das offizielle Ferron-apt-Repository hinzu und installiert Ferron unter Debian/Ubuntu.

sudo apt install curl gnupg2 ca-certificates lsb-release
curl https://deb.ferron.sh/signing.pgp | gpg --dearmor | sudo tee /usr/share/keyrings/ferron-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/ferron-keyring.gpg] https://deb.ferron.sh $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/ferron.list
sudo apt update && sudo apt install ferron

Standard-Speicherorte nach der Installation von Ferron via apt unter Debian/Ubuntu.

# Key paths after apt install:
# Binary:        /usr/sbin/ferron
# Config:        /etc/ferron.kdl
# Web root:      /var/www/ferron
# Logs:          /var/log/ferron/

Lädt und startet Ferron via Docker. Image-Varianten: '2' (distroless), '2-alpine', '2-debian'.

docker pull ferronserver/ferron:2
docker run --name ferron -d -p 80:80 --restart=always ferronserver/ferron:2

Service-Verwaltung (systemctl)

systemctl start ferron — Startet den Ferron-Webserver.

systemctl start ferron

systemctl stop ferron — Stoppt den Ferron-Webserver.

systemctl stop ferron

systemctl restart ferron — Startet Ferron neu (unterbricht kurz bestehende Verbindungen).

systemctl restart ferron

systemctl reload ferron — Lädt die Ferron-Konfiguration ohne Ausfallzeit neu. Liest /etc/ferron.kdl erneut ein.

systemctl reload ferron

systemctl status ferron — Zeigt Status, PID und die letzten Log-Ausgaben des Ferron-Dienstes.

systemctl status ferron

systemctl enable ferron — Aktiviert den automatischen Start von Ferron beim Systemstart.

systemctl enable ferron

systemctl disable ferron — Deaktiviert den automatischen Start von Ferron beim Booten.

systemctl disable ferron

CLI-Kommandos

ferron — Startet Ferron mit der ferron.kdl im aktuellen Verzeichnis.

ferron

ferron -c /etc/ferron.kdl — Startet Ferron mit einer bestimmten Konfigurationsdatei. Standard: ./ferron.kdl

ferron -c /etc/ferron.kdl

ferron --config-string 'localhost { root "/var/www" }' — Startet Ferron mit einer Inline-KDL-Konfiguration statt einer Datei.

ferron --config-string 'localhost { root "/var/www" }'

ferron --config-adapter yaml-legacy -c ferron.yaml — Startet Ferron mit einer Legacy-YAML-Konfiguration von Ferron 1.x. Adapter: kdl (Standard), yaml-legacy.

ferron --config-adapter yaml-legacy -c ferron.yaml

ferron --module-config — Zeigt die einkompilierte Modul-Konfiguration und die verfügbaren Module an.

ferron --module-config

ferron -V — Zeigt die Ferron-Version an.

ferron -V

ferron serve — Liefert das aktuelle Verzeichnis schnell über HTTP auf Port 3000 aus (ohne Konfigurationsdatei).

ferron serve

ferron serve -p 8080 -r /var/www/html — Liefert ein bestimmtes Verzeichnis auf einem eigenen Port aus.

ferron serve -p 8080 -r /var/www/html

ferron serve -l 0.0.0.0 -p 80 -r /var/www — Lauscht auf allen Schnittstellen (-l). Standard-Lauschadresse ist 127.0.0.1.

ferron serve -l 0.0.0.0 -p 80 -r /var/www

ferron serve -c "admin:$(ferron-passwd mysecret)" — Liefert mit HTTP-Basic-Auth aus. ferron-passwd erzeugt den Passwort-Hash.

ferron serve -c "admin:$(ferron-passwd mysecret)"

ferron-passwd <password> — Erzeugt einen Passwort-Hash für HTTP-Basic-Auth in ferron serve oder ferron.kdl.

ferron-passwd mysecretpassword

ferron-precompress /var/www/assets — Komprimiert statische Assets (gzip/brotli) vorab mit 64 Threads für schnellere Auslieferung.

ferron-precompress /var/www/assets

ferron-precompress -t 8 /var/www/assets — Komprimiert Assets vorab mit einer bestimmten Anzahl paralleler Threads.

ferron-precompress -t 8 /var/www/assets

ferron-yaml2kdl ferron.yaml ferron.kdl — Migriert eine Legacy-YAML-Konfiguration von Ferron 1.x in das moderne KDL-Format.

ferron-yaml2kdl old-config.yaml ferron.kdl

KDL-Konfiguration: Struktur & Syntax

KDL-Block-Ziele legen fest, für welche Hosts/Ports ein Konfigurationsblock gilt. globals{} wird zuerst ausgewertet.

# Block targets (virtual hosts):
globals { }            # Global settings (applied everywhere)
* { }                  # All hosts/ports
*:80 { }               # All hosts on port 80 only
example.com { }        # Specific domain
"192.168.1.1" { }      # Specific IP address
example.com:8080 { }   # Domain + port
example.com,example.org { }  # Multiple domains

KDL-Direktiven-Syntax. Flags sind nackte Schlüsselwörter; Booleans nutzen #true/#false; Strings stehen in doppelten Anführungszeichen.

# Directive value types:
root "/var/www/html"      # String
timeout 30000             # Integer (ms)
ocsp_stapling             # Boolean flag (presence = true)
directory_listing #false  # Explicit boolean
protocols "h1" "h2"      # Multiple values

Location-Blöcke matchen URL-Präfixe. remove_base=#true entfernt das Präfix, bevor an das Backend weitergeleitet wird.

location "/api" remove_base=#true {
    proxy "http://localhost:3000"
}
location "/static" {
    root "/var/www/static"
}

Snippets definieren wiederverwendbare Konfigurationsblöcke. 'use' bindet ein Snippet in einen beliebigen Block ein.

snippet "COMMON_HEADERS" {
    header "X-Frame-Options" "DENY"
    header "X-Content-Type-Options" "nosniff"
}

example.com {
    use "COMMON_HEADERS"
    root "/var/www/html"
}

include "/etc/ferron.d/**/*.kdl" — Bindet weitere KDL-Konfigurationsdateien per Glob-Muster ein. Sie werden in die Hauptkonfiguration eingemischt.

include "/etc/ferron.d/**/*.kdl"

Statische Dateien ausliefern

Minimaler Dateiserver: liefert Dateien aus dem angegebenen Verzeichnis für eine Domain aus.

example.com {
    root "/var/www/example.com"
}

Statische Site mit ETag-Unterstützung, Auslieferung vorkomprimierter Dateien und aggressivem Browser-Caching.

example.com {
    root "/var/www/html"
    etag
    compressed
    directory_listing #false
    file_cache_control "public, max-age=31536000"
}

compressed — Liefert automatisch vorkomprimierte .br- und .gz-Dateien aus, wenn der Client sie unterstützt (mit ferron-precompress erzeugen).

compressed

directory_listing #true — Aktiviert das Directory-Listing (standardmäßig deaktiviert).

directory_listing #true

file_cache_control "public, max-age=3600" — Setzt den Cache-Control-Header für alle Antworten mit statischen Dateien.

file_cache_control "public, max-age=86400"

TLS / HTTPS

HTTPS mit manuellen Zertifikatsdateien: Pfad zu Zertifikat und privatem Schlüssel.

example.com {
    tls "/etc/ssl/certs/example.com.crt" "/etc/ssl/private/example.com.key"
    root "/var/www/html"
}

Automatisches TLS über Let's Encrypt (HTTP-01-Challenge). Ferron übernimmt Bezug und Erneuerung der Zertifikate.

example.com {
    auto_tls
    auto_tls_contact "admin@example.com"
    root "/var/www/html"
}

On-Demand-TLS: bezieht Zertifikate für jede Domain beim ersten Request (praktisch für Wildcard-/Multi-Tenant-Setups).

globals {
    auto_tls_on_demand #true
    auto_tls_contact "admin@example.com"
}

Globale TLS-Härtung: Mindestversion, Cipher-Suite und OCSP-Stapling.

globals {
    tls_min_version "TLSv1.2"
    tls_cipher_suite "TLS_AES_256_GCM_SHA384"
    ocsp_stapling
}

Aktiviert HTTP/1.1 und HTTP/2. "h3" ergänzen für experimentelle HTTP/3-Unterstützung.

globals {
    protocols "h1" "h2"
}

Reverse Proxy & Load-Balancing

Einfacher Reverse Proxy: leitet alle Requests an ein Backend weiter. WebSocket-Unterstützung ist automatisch enthalten.

example.com {
    proxy "http://localhost:3000"
}

Reverse Proxy zu einem Unix-Socket-Backend. Die URL setzt den Host-Header; unix= setzt das Verbindungsziel.

example.com {
    proxy "http://localhost:3000" unix="/run/app/web.sock"
}

Verteilt die Last auf mehrere Backends (standardmäßig Round-Robin). lb_health_check aktiviert passive Health-Checks.

example.com {
    proxy "http://backend1:8080"
    proxy "http://backend2:8080"
    proxy "http://backend3:8080"
    lb_health_check
}

lb_algorithm "round_robin" — Setzt den Load-Balancing-Algorithmus. Optionen: round_robin (Standard), random, ip_hash.

lb_algorithm "ip_hash"

proxy_keepalive — Aktiviert Keep-Alive-Verbindungen zum Backend für bessere Performance.

proxy_keepalive

proxy_http2_only — Erzwingt reines HTTP/2-Proxying zum Backend. Für gRPC-Backends erforderlich.

proxy_http2_only

proxy_request_header_replace "Host" "{header:Host}" — Reicht den ursprünglichen Host-Header des Clients an das Backend weiter (Ferron ersetzt ihn standardmäßig).

proxy_request_header_replace "Host" "{header:Host}"

Gemischtes Setup: /api an ein Backend proxen, alles andere als statische Dateien ausliefern.

example.com {
    location "/api" remove_base=#true {
        proxy "http://localhost:3000"
    }
    location "/" {
        root "/var/www/html"
    }
}

Aktiviert In-Memory-Response-Caching für geproxyte Requests. cache_vary fügt eine Cache-Variation pro Header hinzu.

cache
cache_max_entries 1024
cache_vary "Accept-Encoding"

PHP / FastCGI

Liefert PHP über PHP-FPM via Unix-Socket aus. Erfordert das fcgi-Modul.

example.com {
    root "/var/www/html"
    fcgi_responder "unix:/run/php/php8.3-fpm.sock"
}

Liefert PHP über PHP-FPM via TCP aus. TCP nutzen, wenn FPM auf einem anderen Host läuft.

example.com {
    root "/var/www/html"
    fcgi_responder "127.0.0.1:9000"
}

Front-Controller-Muster für PHP-Frameworks (Laravel, Symfony, WordPress): leitet alle Requests über index.php.

example.com {
    root "/var/www/html"
    rewrite "^/index\.php/?(.*)$" "/$1" last=#true
    location "/" {
        rewrite "^/(.*)$" "/index.php/$1" file=#false directory=#false last=#true
    }
    fcgi_responder "unix:/run/php/php8.3-fpm.sock"
}

Weiterleitungen & Rewrites

Leitet allen HTTP-Verkehr per dauerhaftem 301-Redirect auf HTTPS um.

*:80 {
    return 301 "https://{header:Host}{path_and_query}"
}

Leitet www auf non-www um (oder umgekehrt) mit einem dauerhaften Redirect.

www.example.com {
    return 301 "https://example.com{path_and_query}"
}

return 302 "/maintenance" — Leitet alle Requests temporär auf eine Wartungsseite um.

return 302 "/maintenance"

rewrite "^/old/(.*)$" "/new/$1" last=#true — Rewrite-Regel mit Regex-Capture-Group. last=#true stoppt die weitere Rewrite-Verarbeitung.

rewrite "^/old/(.*)$" "/new/$1" last=#true

rewrite "^/(.*)$" "/index.php" file=#false directory=#false last=#true — Leitet alle Requests, die keiner Datei oder keinem Verzeichnis entsprechen, an index.php (Front Controller).

rewrite "^/(.*)$" "/index.php" file=#false directory=#false last=#true

Single-Page-Application-Setup: fällt für alle clientseitigen Routen auf / (index.html) zurück.

# SPA: serve index.html for all non-file requests
location "/" {
    root "/var/www/html"
    rewrite "^/.*" "/" directory=#false file=#false last=#true
}

Sicherheit & Header

Essenzielle Security-Header. In einen Server-Block oder ein Snippet einfügen.

header "X-Frame-Options" "DENY"
header "X-Content-Type-Options" "nosniff"
header "Referrer-Policy" "strict-origin-when-cross-origin"
header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"

header "Content-Security-Policy" "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'" — Content-Security-Policy-Header. Quellen an die eigene Anwendung anpassen.

header "Content-Security-Policy" "default-src 'self'"

Entfernt Header aus Antworten (verbirgt Server-Informationen).

header_remove "X-Powered-By"
header_remove "Server"

Blockiert bestimmte IP-Adressen oder CIDR-Bereiche. Blockierte Clients erhalten eine 403-Antwort.

block "192.168.1.100"
block "10.0.0.0/8"

Erlaubt IP-Bereiche per Allowlist. In Kombination mit block nutzen, um den Zugriff auf bestimmte Netze zu beschränken.

allow "10.0.0.0/8"
allow "192.168.1.0/24"

trust_x_forwarded_for — Vertraut dem X-Forwarded-For-Header vorgelagerter Proxys/Load-Balancer für korrektes Client-IP-Logging.

trust_x_forwarded_for

Rate-Limiting

Begrenzt auf 100 Requests/Sekunde pro Client-IP, mit Bursts bis 200. Überzählige Requests erhalten 429.

example.com {
    limit rate=100 burst=200
}

Wendet strengeres Rate-Limiting auf einen bestimmten Pfad an (z. B. API-Endpunkte).

location "/api" {
    limit rate=10 burst=20
    proxy "http://localhost:3000"
}

Bedingungen & Conditionals

Benannter Bedingungsblock mit is_regex. Mit 'if' oder 'if_not' Direktiven bedingt anwenden.

condition "IS_API" {
    is_regex "{path}" "^/api/"
}

example.com {
    if "IS_API" {
        proxy "http://localhost:3000"
    }
}

is_equal prüft eine exakte Übereinstimmung eines Platzhalter-Werts. Mit if_not negieren.

condition "NO_BOT" {
    is_equal "{header:User-Agent}" "curl"
}
example.com {
    if_not "NO_BOT" {
        root "/var/www/html"
    }
}

is_remote_ip matcht die Client-IP-Adresse.

condition "LOCAL" {
    is_remote_ip "127.0.0.1"
}

Platzhalter für Bedingungen, header-Direktiven, return und Rewrite-Ziele.

# Available placeholders for conditions and headers:
# {path}             - Request URI path
# {path_and_query}   - Path including query string
# {method}           - HTTP method (GET, POST, ...)
# {header:<name>}    - Request header value
# {client_ip}        - Client IP address
# {scheme}           - http or https

Logging & Observability

Setzt globale Pfade für Access- und Error-Log.

globals {
    log "/var/log/ferron/access.log"
    error_log "/var/log/ferron/error.log"
}

log "/var/log/ferron/example-access.log" — Überschreibt die Log-Datei pro virtuellem Host.

log "/var/log/ferron/example-access.log"

log_format "{client_ip} - [{timestamp}] \"{method} {path}\" {status} {bytes_sent}" — Passt das Access-Log-Format über Platzhalter an.

log_format "{client_ip} {method} {path} {status}"

Loggt nach stdout/stderr statt in Dateien. Praktisch für Docker und containerisierte Umgebungen.

globals {
    log "stdout"
    error_log "stderr"
}

Vollständiges Konfigurationsbeispiel

Produktionsreifes Beispiel: globals, Security-Snippet, HTTP→HTTPS-Redirect, HTTPS-vHost mit statischen Dateien + API-Proxy, Rate-Limiting.

globals {
    protocols "h1" "h2"
    tls_min_version "TLSv1.2"
    ocsp_stapling
    log "/var/log/ferron/access.log"
    error_log "/var/log/ferron/error.log"
}

snippet "SECURITY_HEADERS" {
    header "X-Frame-Options" "DENY"
    header "X-Content-Type-Options" "nosniff"
    header "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
    header_remove "Server"
}

*:80 {
    return 301 "https://{header:Host}{path_and_query}"
}

example.com {
    tls "/etc/ssl/certs/example.com.crt" "/etc/ssl/private/example.com.key"
    root "/var/www/example.com"
    use "SECURITY_HEADERS"
    etag
    compressed
    limit rate=200 burst=400

    location "/api" remove_base=#true {
        proxy "http://localhost:3000"
        limit rate=50 burst=100
    }
}

Fazit

Ferron zeigt, dass ein Webserver nicht zwischen Performance, Sicherheit und Lesbarkeit wählen muss: Die Rust-Basis liefert Speichersicherheit ohne Garbage Collector, und die KDL-Konfiguration bleibt auch bei TLS, Reverse Proxy und PHP übersichtlich. Weil automatisches TLS, Load-Balancing und Rate-Limiting bereits eingebaut sind, kommst du für viele Setups ohne zusätzliche Tools aus. Da das Projekt noch jung ist und sich aktiv weiterentwickelt, lohnt vor dem Produktiveinsatz ein Blick in die offizielle Dokumentation.

Verwandte Kommandos

  • apache – etablierter, modularer Webserver mit großem Ökosystem
  • caddy – Webserver in Go mit automatischem TLS und einfacher Konfiguration
  • certbot – Let's-Encrypt-Zertifikate manuell ausstellen und erneuern