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 ferronStandard-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:2Service-Verwaltung (systemctl)
systemctl start ferron — Startet den Ferron-Webserver.
systemctl start ferronsystemctl stop ferron — Stoppt den Ferron-Webserver.
systemctl stop ferronsystemctl restart ferron — Startet Ferron neu (unterbricht kurz bestehende Verbindungen).
systemctl restart ferronsystemctl reload ferron — Lädt die Ferron-Konfiguration ohne Ausfallzeit neu. Liest /etc/ferron.kdl erneut ein.
systemctl reload ferronsystemctl status ferron — Zeigt Status, PID und die letzten Log-Ausgaben des Ferron-Dienstes.
systemctl status ferronsystemctl enable ferron — Aktiviert den automatischen Start von Ferron beim Systemstart.
systemctl enable ferronsystemctl disable ferron — Deaktiviert den automatischen Start von Ferron beim Booten.
systemctl disable ferronCLI-Kommandos
ferron — Startet Ferron mit der ferron.kdl im aktuellen Verzeichnis.
ferronferron -c /etc/ferron.kdl — Startet Ferron mit einer bestimmten Konfigurationsdatei. Standard: ./ferron.kdl
ferron -c /etc/ferron.kdlferron --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.yamlferron --module-config — Zeigt die einkompilierte Modul-Konfiguration und die verfügbaren Module an.
ferron --module-configferron -V — Zeigt die Ferron-Version an.
ferron -Vferron serve — Liefert das aktuelle Verzeichnis schnell über HTTP auf Port 3000 aus (ohne Konfigurationsdatei).
ferron serveferron serve -p 8080 -r /var/www/html — Liefert ein bestimmtes Verzeichnis auf einem eigenen Port aus.
ferron serve -p 8080 -r /var/www/htmlferron 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/wwwferron 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 mysecretpasswordferron-precompress /var/www/assets — Komprimiert statische Assets (gzip/brotli) vorab mit 64 Threads für schnellere Auslieferung.
ferron-precompress /var/www/assetsferron-precompress -t 8 /var/www/assets — Komprimiert Assets vorab mit einer bestimmten Anzahl paralleler Threads.
ferron-precompress -t 8 /var/www/assetsferron-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.kdlKDL-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 domainsKDL-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 valuesLocation-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).
compresseddirectory_listing #true — Aktiviert das Directory-Listing (standardmäßig deaktiviert).
directory_listing #truefile_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_keepaliveproxy_http2_only — Erzwingt reines HTTP/2-Proxying zum Backend. Für gRPC-Backends erforderlich.
proxy_http2_onlyproxy_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=#truerewrite "^/(.*)$" "/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=#trueSingle-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_forRate-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 httpsLogging & 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.
Weiterführende Links
- Ferron – offizielle Website – Überblick, Features und Downloads (englisch)
- Ferron-Dokumentation – Konfigurations-Referenz und Anleitungen (englisch)
- ferronweb/ferron – GitHub – Quellcode und Issue-Tracker (englisch)