# 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.

Source: https://www.jpkc.com/db/cheatsheets/web-servers/ferron/

<!-- PROSE:intro -->
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.
<!-- PROSE:intro:end -->

## Installation (Debian/Ubuntu)

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

```bash
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.

```bash
# 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'.

```bash
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.

```bash
systemctl start ferron
```

`systemctl stop ferron` — Stoppt den Ferron-Webserver.

```bash
systemctl stop ferron
```

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

```bash
systemctl restart ferron
```

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

```bash
systemctl reload ferron
```

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

```bash
systemctl status ferron
```

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

```bash
systemctl enable ferron
```

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

```bash
systemctl disable ferron
```

## CLI-Kommandos

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

```bash
ferron
```

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

```bash
ferron -c /etc/ferron.kdl
```

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

```bash
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.

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

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

```bash
ferron --module-config
```

`ferron -V` — Zeigt die Ferron-Version an.

```bash
ferron -V
```

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

```bash
ferron serve
```

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

```bash
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.

```bash
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.

```bash
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.

```bash
ferron-passwd mysecretpassword
```

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

```bash
ferron-precompress /var/www/assets
```

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

```bash
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.

```bash
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.

```bash
# 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.

```bash
# 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.

```bash
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.

```bash
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.

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

## Statische Dateien ausliefern

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

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

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

```bash
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).

```bash
compressed
```

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

```bash
directory_listing #true
```

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

```bash
file_cache_control "public, max-age=86400"
```

## TLS / HTTPS

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

```bash
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.

```bash
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).

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

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

```bash
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.

```bash
globals {
    protocols "h1" "h2"
}
```

## Reverse Proxy & Load-Balancing

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

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

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

```bash
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.

```bash
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.

```bash
lb_algorithm "ip_hash"
```

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

```bash
proxy_keepalive
```

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

```bash
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).

```bash
proxy_request_header_replace "Host" "{header:Host}"
```

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

```bash
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.

```bash
cache
cache_max_entries 1024
cache_vary "Accept-Encoding"
```

## PHP / FastCGI

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

```bash
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.

```bash
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.

```bash
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.

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

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

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

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

```bash
return 302 "/maintenance"
```

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

```bash
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).

```bash
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.

```bash
# 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.

```bash
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.

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

Entfernt Header aus Antworten (verbirgt Server-Informationen).

```bash
header_remove "X-Powered-By"
header_remove "Server"
```

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

```bash
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.

```bash
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.

```bash
trust_x_forwarded_for
```

## Rate-Limiting

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

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

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

```bash
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.

```bash
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.

```bash
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.

```bash
condition "LOCAL" {
    is_remote_ip "127.0.0.1"
}
```

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

```bash
# 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.

```bash
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.

```bash
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.

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

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

```bash
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.

```bash
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
    }
}
```

<!-- PROSE:outro -->
## 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](https://ferron.sh/) – Überblick, Features und Downloads (englisch)
- [Ferron-Dokumentation](https://ferron.sh/docs) – Konfigurations-Referenz und Anleitungen (englisch)
- [ferronweb/ferron – GitHub](https://github.com/ferronweb/ferron) – Quellcode und Issue-Tracker (englisch)
<!-- PROSE:outro:end -->

## Verwandte Kommandos

- [apache](https://www.jpkc.com/db/cheatsheets/web-servers/apache/) – etablierter, modularer Webserver mit großem Ökosystem
- [caddy](https://www.jpkc.com/db/cheatsheets/web-servers/caddy/) – Webserver in Go mit automatischem TLS und einfacher Konfiguration
- [certbot](https://www.jpkc.com/db/cheatsheets/web-servers/certbot/) – Let's-Encrypt-Zertifikate manuell ausstellen und erneuern

