---
title: Caching und CDN für die OTT-Auslieferung
url: https://doc2.pstreamer.tv/de/manual/extras/ott_caching.html
lang: de
product: Perfect Streamer
version: 2.0.2.362
---

# Caching und CDN für die OTT-Auslieferung

Anhang für den Betrieb großer Installationen: wie das Caching der OTT-Antworten von Perfect Streamer aufgebaut ist und wie einem Knoten ein cachender Reverse Proxy oder ein CDN vorgeschaltet wird. Auslieferungsmodi, URL-Formate und Autorisierung sind im Hauptabschnitt [OTT und DVR](../streamer/ott_dvr.md#streamer-ott-dvr) beschrieben.

Der Server erzeugt Antworten dreier Kategorien, die sich in der Lebensdauer des Inhalts und in der Eignung für das Caching durch zwischengeschaltete Knoten (Reverse Proxy, CDN, Client-Cache) unterscheiden.

## 1. Caching-Modell

### 1.1. Ressourcen und HTTP-Header

| Ressource | URL | Content-Type | Cache-Control |
| --- | --- | --- | --- |
| TS-Segment (HLS) | `/h<sess>/<keyHex>.ts` | `video/mp2t` | `public, max-age=60, immutable` |
| VTT-Segment (Untertitel) | `/h<sess>/sub/<pid>/<keyHex>.vtt` | `text/vtt; charset=utf-8` | `public, max-age=60, immutable` |
| fMP4-Segment (DASH / LL-HLS) | `/h<sess>/init.mp4`, `/h<sess>/<key\|N>.m4s` | `video/mp4` | `public, max-age=60, immutable` |
| DASH MPD | `/h<sess>/index.mpd` | `application/dash+xml; charset=utf-8` | `public, max-age=1` |
| HLS master | `/hls/<stream>/<login>/<pass>/index.m3u8` | `application/vnd.apple.mpegurl` | `public, max-age=1` |
| HLS media | `/h<sess>/index.m3u8`, `/h<sess>/sub/<pid>/index.m3u8` | `application/vnd.apple.mpegurl` | `public, max-age=1` |
| 302 Redirect | `/dash/<stream>/<login>/<pass>/index.mpd` | — | `no-cache, no-store` |
| Raw TS | `/http/<stream>/<login>/<pass>` | `video/mp2t` | nicht gesetzt; wird nicht gecacht |

### 1.2. Eigenschaften der Segmente

Der hexadezimale Segmentbezeichner in der URL (`<keyHex>` in den Pfaden `/h<sess>/<keyHex>.ts`) wird als CRC64 aus der Startzeit des Segments und der Stream-ID gebildet und ist global eindeutig. Die URL des Segments adressiert unveränderlichen Inhalt — bei wiederholten Anfragen derselben URL wird ein identischer Bytestrom zurückgegeben (solange das Segment innerhalb des gleitenden Fensters bleibt).

Die Direktive `immutable` unterdrückt die bedingte Revalidierung durch den Client (`If-None-Match`, `If-Modified-Since`). Der Wert `max-age=60` gewährleistet die Kompatibilität mit einem typischen `timeShiftBufferDepth=40s`.

CMAF-fMP4-Segmente (`.m4s`) und die gemeinsame `init.mp4` für DASH / Low-Latency HLS werden analog adressiert und nach demselben Modell gecacht (`immutable`, `max-age=60`). Teilsegmente (Parts) von LL-HLS sind Byte-Range-Anfragen innerhalb derselben `.m4s` und bilden daher keinen eigenen Cache-Eintrag.

### 1.3. Eigenschaften der Manifeste

`max-age=1` begrenzt die obere Schranke der Veralterung des Inhalts im Cache auf eine Sekunde. Zusammen mit `proxy_cache_lock on` (nginx) werden Anfragespitzen auf das Manifest zu einer Anfrage pro Sekunde an den Origin zusammengefasst.

### 1.4. Variabilität des Inhalts

Bei `absPath=0` (Standardwert, der URL-Parameter `a` fehlt) enthalten die HLS-Media-Manifeste und das DASH-MPD keinen Sitzungsbezeichner im Rumpf. Der Inhalt des Manifests ist zwischen Sitzungen identisch, die zu derselben (stream, param)-Kombination gehören. Das erlaubt einem Reverse-Proxy-Cache, den Eintrag bei normalisiertem Cache-Schlüssel sitzungsübergreifend wiederzuverwenden.

Bei `absPath=1` (URL-Parameter `a=1`) enthält der Rumpf des Manifests absolute URLs einschließlich Schema, Host und Sitzungsbezeichner. Der Inhalt wird sitzungsspezifisch, eine sitzungsübergreifende Wiederverwendung des Caches ist nicht möglich.

## 2. Verhalten der Clients

| Client | URL der Manifest-Aktualisierung | Auswirkung auf die Anzahl der Sitzungen |
| --- | --- | --- |
| HLS-Player über die Media-Playlist | `/h<sess>/index.m3u8` | Eine Sitzung je Wiedergabevorgang |
| DASH-Player über die Root-URL | `/dash/<stream>/.../index.mpd` (Wiederholungszyklus) | Wird durch Session Reuse behandelt (siehe 3.2) |
| Browser-Player (MSE) | `/h<sess>/...` über `<Location>` / Session-URL | Eine Sitzung je Wiedergabevorgang |

## 3. Spezielle Mechanismen

### 3.1. HTTP 302 Redirect für DASH

Eine Anfrage der Form `/dash/<stream>/<login>/<pass>/index.mpd` liefert die Antwort `302 Found` mit dem Header `Location: /h<sess>/index.mpd`. Der Antwortrumpf ist leer. Autorisierung und Zuweisung der Sitzung erfolgen bei der Verarbeitung des Redirects.

Clients, die das Caching des Redirects unterstützen, greifen in den folgenden Anfragen direkt auf die Session-URL zu. Clients, die es nicht unterstützen, wiederholen die Redirect-Anfrage. Der Aufwand für die erneute Verarbeitung des Redirects beschränkt sich auf die Authentifizierungsprüfung und die Operationen des Session Reuse.

### 3.2. Session Reuse für DASH

Bei der Verarbeitung einer Anfrage `/dash/.../index.mpd` desselben Logins an denselben Stream (mit demselben Adaptive-Merkmal) findet der Server eine bereits bestehende DASH-Sitzung und gibt deren Bezeichner erneut zurück. Es wird keine neue Sitzung angelegt und kein Slot des Limits gleichzeitiger Verbindungen verbraucht. Wiederverwendet werden nur Sitzungen im gleichen Wiedergabemodus: eine Live-Sitzung und eine VOD-Sitzung des Archivs (Parameter `t`/`d`/`epg`) werden nicht zusammengeführt.

Gilt nur für DASH. Für HLS ist kein eigener Reuse-Mechanismus erforderlich: HLS-Clients aktualisieren die Media-Playlist über die Session-URL und legen nicht bei jeder Aktualisierung eine neue Sitzung an.

### 3.3. Wiederverwendung von Segmenten zwischen Sitzungen

Der Pfad `/h<sess>/<keyHex>.ts` hängt bei der Auflösung von `<keyHex>` zum Inhalt nicht von `<sess>` ab: `<keyHex>` identifiziert das TS-Segment innerhalb des Streams global eindeutig. Nginx mit normalisiertem Cache Key (der das Präfix `/h<sess>/` abschneidet) bedient alle Anfragen desselben `<keyHex>` aus einem einzigen Cache-Eintrag, unabhängig davon, welche Clients sie gestellt haben.

Die Deduplizierung ist nur auf content-addressed Namen anwendbar — auf Segmente mit einem 16-stelligen hexadezimalen Bezeichner (`.ts`, `.m4s` mit `<keyHex>`, `.vtt`). Nummerierte Segmente von Live-DASH (`<Nummer>.m4s`), `init.mp4` und die Manifeste sind nur innerhalb ihrer Sitzung eindeutig — ihr Cache-Schlüssel muss das Präfix `/h<sess>/` beibehalten, sonst liefert der Cache den Inhalt eines Streams an die Zuschauer eines anderen. Für DASH sorgt Session Reuse für die Deduplizierung der Manifeste zwischen Clients desselben Logins (siehe 3.2).

## 4. Anfrageparameter

| Parameter | Standardwert | Wirkung |
| --- | --- | --- |
| `a` | `0` | `1` — absolute URLs in den Manifesten; `0` — relative |
| `s` | `40` | `timeShiftBufferDepth` in Sekunden |
| `m` | `40` | Minimale Fensterlänge für die Ausgabe des Manifests |
| `v` | `6` für die OTT-Modi, `3` für Peering / HLS | `#EXT-X-VERSION` in HLS (wird von DASH ignoriert); ein explizit in der URL angegebener Wert überschreibt den Standardwert |
| `h3` | nicht vorhanden (off) | Opt-in für HTTP/3 (QUIC): veranlasst den Server, `Alt-Svc` in der Antwort auszugeben; sticky auf der Session-URL. Siehe [HTTP/3 (QUIC)](../streamer/ott_dvr.md#streamer-ott-http3) |

Die Änderung eines Parameters über den Query String aktualisiert die in der Sitzung gespeicherten Werte beim nächsten Wiederöffnen der Sitzung. Die Parameter der Archivwiedergabe `t`, `d` und `epg` sind in [Archivwiedergabe (VOD)](../streamer/ott_dvr.md#streamer-dvr-vod) beschrieben.

## 5. Lastcharakteristika

Die Last auf dem Origin skaliert mit der Anzahl gleichzeitig betrachteter unterschiedlicher Streams. Eine steigende Anzahl von Clients, die denselben Stream betrachten, erhöht die Anzahl der Anfragen an den Origin nicht, sofern ein Reverse-Proxy-Cache und ein normalisierter Cache Key vorhanden sind.

| Szenario | Frequenz der Origin-Anfragen (Richtwert) |
| --- | --- |
| 1 Client auf Stream X | MPD: 0.4 req/s, segment: 0.2 req/s |
| N Clients auf denselben Stream X (Cache aktiviert) | MPD: 1 req/s, segment: 0.2 req/s |
| N DASH-Clients im Wiederholungsmodus auf einem Stream | MPD: 1 req/s (mit `proxy_cache_lock`) |
| N Clients auf N unterschiedliche Streams | MPD: 0.4·N req/s, segment: 0.2·N req/s |

Segmente werden global über content-addressed Namen dedupliziert; für die Deduplizierung der Manifeste zwischen Clients sorgen Session Reuse (DASH) und der Cache innerhalb der Sitzung.

## 6. Nginx als cachender Reverse Proxy

### 6.1. Basiskonfiguration

```nginx
proxy_cache_path /var/cache/nginx/pss_segments
    levels=1:2 keys_zone=pss_segments:100m
    max_size=20g inactive=30m use_temp_path=off;

proxy_cache_path /var/cache/nginx/pss_manifests
    levels=1:2 keys_zone=pss_manifests:10m
    max_size=256m inactive=5m use_temp_path=off;

upstream pss_backend {
    server 127.0.0.1:43972;
    keepalive 64;
}

map $uri $pss_cache_key {
    "~^/h[0-9a-f]{16}(?<tail>(/[0-9]+)?/[0-9a-f]{16}\.(ts|m4s)|/sub/[0-9]+/[0-9a-f]{16}\.vtt)$"  "stream:$tail";
    default  $uri;
}

server {
    listen 80;
    server_name stream.example.com;

    location ~* "^/h[0-9a-f]{16}(/[0-9]+|/sub/[0-9]+)?/([0-9a-f]+\.(ts|m4s|vtt)|init\.mp4)$" {
        proxy_cache               pss_segments;
        proxy_cache_key           $pss_cache_key;
        proxy_cache_valid         200 60s;
        proxy_cache_valid         404 403 0s;
        proxy_cache_lock          on;
        proxy_cache_use_stale     updating error timeout;
        proxy_cache_revalidate    on;
        proxy_ignore_headers      Vary;
        add_header                X-Cache-Status $upstream_cache_status;

        proxy_pass                http://pss_backend;
        proxy_http_version        1.1;
        proxy_set_header          Connection "";
        proxy_buffering           on;
    }

    location ~* "(^/h[0-9a-f]{16}(/[0-9]+|/sub/[0-9]+)?/index\.(m3u8|mpd)$|^/(hls|dash|llhls)/.*\.(m3u8|mpd)$)" {
        proxy_cache               pss_manifests;
        proxy_cache_key           $pss_cache_key;
        proxy_cache_valid         200 1s;
        proxy_cache_valid         404 403 0s;
        proxy_cache_lock          on;
        proxy_cache_lock_timeout  2s;
        proxy_cache_use_stale     updating;
        add_header                X-Cache-Status $upstream_cache_status;

        proxy_pass                http://pss_backend;
        proxy_http_version        1.1;
        proxy_set_header          Connection "";
    }

    location / {
        proxy_pass                http://pss_backend;
        proxy_http_version        1.1;
        proxy_set_header          Connection "";
        proxy_set_header          X-Forwarded-Proto $scheme;
        proxy_set_header          X-Forwarded-Host  $host;
        proxy_buffering           off;
        proxy_read_timeout        3600s;
    }
}
```

Der Origin-Port im Beispiel ist der Standardport des Streaming-HTTP-Servers (43972; HTTPS — 43982). Normalisiert werden nur die content-addressed Namen der Segmente (siehe 3.3); Manifeste, `init.mp4` und nummerierte Segmente werden unter der vollständigen URI ihrer Sitzung gecacht. Die Direktive `proxy_ignore_headers Vary` in der Location der Segmente ist gerechtfertigt: Der Server versieht OTT-Antworten mit dem Header `Vary: Origin`, und ohne sie würde der Cache nach dem Origin-Wert des Clients in Varianten aufgeteilt (siehe 7).

### 6.2. Zweck der Direktiven

| Direktive | Zweck |
| --- | --- |
| `proxy_cache_lock on` | Serialisiert die Ausführung der Upstream-Anfragen bei gleichzeitigen Cache Misses auf denselben Schlüssel |
| `proxy_cache_use_stale updating` | Gibt parallelen Anfragen während der Cache-Aktualisierung die veraltete Kopie zurück |
| `proxy_cache_revalidate on` | Verwendet `If-Modified-Since` bei einem Cache Miss mit gespeicherter Kopie |
| `proxy_cache_valid 404 403 0s` | Verbietet das Caching von Autorisierungsfehlern und 404 |
| `keepalive 64` im Upstream | Unterhält einen Pool persistenter Verbindungen zum Origin |
| `proxy_buffering on` | Für Segmente; aktiviert die Pufferung der Antwort in nginx |
| `proxy_buffering off` | Für den Abschnitt `/`; deaktiviert die Pufferung (Raw Streaming) |

### 6.3. Berechnung von `max_size` des Segment-Caches

Richtwert: `bitrate × timeShiftBufferDepth × distinct_streams × 2`

Beispiel: 10 Streams × 8 Mbit/s × 40 s × 2 ≈ 800 MB. Es wird empfohlen, eine Reserve von 10x für die Schwankungen der Bitrate vorzusehen.

### 6.4. TLS-Terminierung

Der Server Perfect Streamer nimmt Verbindungen auf dem HTTP- und dem HTTPS-Port entgegen. Bei TLS-Terminierung auf nginx verwendet der Upstream den HTTP-Port. Die Weitergabe der Header `X-Forwarded-Proto` und `X-Forwarded-Host` ist für die korrekte Bildung absoluter URLs bei `absPath=1` zwingend erforderlich.

```nginx
server {
    listen 443 ssl http2;
    server_name stream.example.com;

    ssl_certificate           /etc/letsencrypt/live/stream.example.com/fullchain.pem;
    ssl_certificate_key       /etc/letsencrypt/live/stream.example.com/privkey.pem;
    ssl_protocols             TLSv1.2 TLSv1.3;
    ssl_session_cache         shared:SSL:10m;
    ssl_session_timeout       1d;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location ... {
        proxy_pass                http://pss_backend;
        proxy_set_header          X-Forwarded-Proto https;
        proxy_set_header          X-Forwarded-Host  $host;
        proxy_set_header          Host              $host;
        # + caching directives from 6.1
    }
}

server {
    listen 80;
    server_name stream.example.com;
    return 301 https://$host$request_uri;
}
```

Bei HTTPS zwischen nginx und Origin kommen die Direktiven `proxy_ssl_verify` und `proxy_ssl_trusted_certificate` zum Einsatz. Für Loopback-Verbindungen ist eine Verschlüsselung überflüssig.

### 6.5. Multihost

Werden mehrere `server_name` aus einem nginx-Prozess bedient, wird `$host` zur Isolierung der Inhalte in den Cache Key aufgenommen:

```nginx
map $uri $pss_cache_key {
    "~^/h[0-9a-f]{16}(?<tail>(/[0-9]+)?/[0-9a-f]{16}\.(ts|m4s)|/sub/[0-9]+/[0-9a-f]{16}\.vtt)$"  "$host:stream:$tail";
    default  "$host:$uri";
}
```

Die Größe von `keys_zone` wird mit 8000 Schlüsseln/MB berechnet. Für Multihost-Installationen mit Tausenden von Streams wird `keys_zone=...:300m` oder höher empfohlen.

## 7. Client-seitiges Caching

`Cache-Control: immutable` wird von aktuellen Browsern verarbeitet. Der Client-Cache liefert das Segment bei erneutem Zugriff ohne bedingte Anfrage (auch beim Rückwärtssprung innerhalb des Player-Puffers).

Service Workers können anhand des Inhalts von Cache-Control die Strategie `cache-first` anwenden. DASH-Player nutzen MSE über `SourceBuffer`; ein in den Puffer eingefügtes Segment bleibt ohne erneute HTTP-Anfrage verfügbar, bis es aus dem gleitenden Fenster herausfällt.

Für Cross-Domain-Anfragen gibt der Server `Access-Control-Allow-Origin: *` zusammen mit dem Header `Vary: Origin, Access-Control-Request-Headers` aus. Ein Caching-Proxy, der Vary beachtet, teilt die Einträge nach dem Origin-Wert des Clients in Varianten auf (Browser-Player senden Origin, Konsolen-Clients nicht), was die HIT rate senkt. Da ACAO das universelle „*“ ist, kann in der Location der Segmente gefahrlos `proxy_ignore_headers Vary` ergänzt werden (siehe 6.1).

## 8. Bereitstellung über ein CDN

Perfect Streamer ist mit einem CDN im Modus Pull-from-Origin kompatibel.

**Origin shield.** Es wird empfohlen, einen oder mehrere Shield-Knoten zwischen CDN-Edge und Origin zu platzieren, um die Anfragefrequenz am Origin bei globaler Verteilung der Clients zu senken.

**Purge.** Content-addressed Segmente erfordern keinen Purge. Bei Änderung der Metadaten des Streams (Codec, Auflösung) werden die Manifeste innerhalb von `max-age=1` ohne expliziten Purge aktualisiert.

**Cache warming.** Bei erwartetem Lastanstieg auf einen bestimmten Stream ist ein Vorwärmen des CDN von mehreren geografischen Standorten aus vor Beginn der Übertragung zulässig.

**Geografische Verteilung.** Segmente (`max-age=60`) eignen sich gut für geografisch verteiltes Caching. Manifeste (`max-age=1`) lassen eine Auslieferungsverzögerung von bis zu einer Sekunde zu — für Live-Übertragungen ohne niedrige Latenz akzeptabel.

## 9. Überwachung

### 9.1. `X-Cache-Status`

Hinzufügen von `add_header X-Cache-Status $upstream_cache_status;` in jeder Location mit Caching. Werte:

| Wert | Beschreibung |
| --- | --- |
| `HIT` | Antwort aus dem Cache |
| `MISS` | War nicht im Cache vorhanden, vom Origin bezogen und gespeichert |
| `EXPIRED` | Abgelaufen, aktualisiert |
| `UPDATING` | Während der Aktualisierung wurde einer parallelen Anfrage die Stale-Kopie geliefert |
| `STALE` | `use_stale` hat eine abgelaufene Kopie geliefert (Origin nicht erreichbar) |
| `REVALIDATED` | Der Origin hat 304 Not Modified zurückgegeben |
| `BYPASS` | `proxy_cache_bypass` hat ausgelöst |

### 9.2. Format des Access-Logs

```nginx
log_format pss_cache '$remote_addr $status $request_method "$request" '
                     '$body_bytes_sent rt=$request_time ut=$upstream_response_time '
                     'cache=$upstream_cache_status key=$pss_cache_key';

server {
    access_log /var/log/nginx/pss.log pss_cache;
}
```

### 9.3. Metriken

Das Modul `nginx-vts` exportiert Metriken pro Zone im Prometheus-Format:

```
GET /status/format/prometheus
```

Empfohlene Schwellenwerte für Alarme:

| Metrik | Schwellenwert | Mögliche Ursache |
| --- | --- | --- |
| Segment HIT rate | < 90% über 5 Minuten | Die Normalisierung des Cache Key ist verletzt; zu kleiner `max_size` |
| Manifest MISS rate | > 50% über 1 Minute | `proxy_cache_lock` serialisiert die Anfragen nicht |
| Upstream response time p95 | > 500 ms über 1 Minute | Überlastung des Origin |
| Cache zone fill | > 90% über 10 Minuten | Annäherung an `max_size`, LRU-Eviction steht bevor |

Die Auslieferungsqualität auf Knotenseite ist an den Sitzungsmetriken auf dem Bildschirm „Clients“ ([Clients](../webui/monitor.md#webui-clients)) ersichtlich.

## 10. Diagnose

| Symptom | Wahrscheinliche Ursache | Lösung |
| --- | --- | --- |
| Segment HIT rate niedrig | `Vary: Origin` teilt den Cache in Varianten auf; verletzte Normalisierung in `map` | `proxy_ignore_headers Vary` in der Location der Segmente ergänzen; Regex in der Direktive `map` prüfen |
| 404 auf Segmenten nach dem Verlassen des Fensters | Gecachte 404 bei einem Segment, das aus dem Sliding Window herausgefallen ist | `proxy_cache_valid 404 0s` in der Location segments ergänzen |
| Verzögerung des Playback-Starts 2–5 s | `proxy_cache_lock_timeout` überschreitet die Target Latency | Auf 1–2 s senken; `proxy_cache_use_stale updating` aktivieren |
| Manifest wird nicht aktualisiert | `proxy_ignore_headers Cache-Control` ist gesetzt, und `proxy_cache_valid` mit großem TTL ist wirksam | `proxy_cache_valid 200 1s` explizit angeben oder das Ignorieren der Header entfernen |
| Anstieg von TIME_WAIT am Upstream | `keepalive` fehlt im Upstream-Block | `keepalive 64`, `proxy_http_version 1.1`, `proxy_set_header Connection ""` ergänzen |
| 403 auf `/dash/.../<segment>.m4s` von Konsolen-Clients | Der Client löst relative URLs gegen die Pre-Redirect-URL auf | Der Server gibt `<BaseURL>` mit absolutem Pfad aus; im aktuellen Build kompatibel |
| Ruckler, häufiges Rebuffering bei entfernten Clients | Niedriger effektiver TCP-Durchsatz durch Slow Start und Idle Restart bei großen RTT (300 ms und mehr) | Tuning des Linux-Netzwerkstacks auf dem Origin: siehe 10.1 |

### 10.1. TCP-Tuning des Origin für High-RTT-Clients

Das Problem tritt bei Clients mit großem RTT zum Origin auf (zum Beispiel 300 ms und mehr), wenn die Bitrate des Streams nahe an der Kapazität des Kanals liegt. Symptome im Player sind häufiges Rebuffering und Abbrüche; auf dem Server wirkt der Client dabei normal, Fehler treten nicht auf.

Ursache:

- **TCP slow start.** Jede neue TCP-Verbindung beginnt mit einem Congestion Window von etwa 14 KB und vergrößert es über mehrere RTT. Bei einem RTT von 300 ms dauert das Erreichen des vollen Fensters 2–3 Sekunden. In dieser Zeit wird ein HLS/DASH-Segment von 5 s Dauer (4–6 MB) merklich langsamer als in Echtzeit heruntergeladen.
- **TCP idle restart.** Zwischen den Segmentanfragen legt der Client im HLS-Pull-Modell eine Pause von 4–5 s ein. Standardmäßig setzt der Linux-Kernel nach einer solchen Pause das Congestion Window der Verbindung auf das Initial cwnd zurück (Verhalten `net.ipv4.tcp_slow_start_after_idle=1`). Dadurch beginnt die Keep-alive-Verbindung beim nächsten GET die Übertragung erneut mit Slow Start — selbst in einer bereits eingelaufenen Sitzung.

Erschwerend kommt hinzu, dass das standardmäßige Congestion Control CUBIC lange RTT und Paketverluste auf zwischengeschalteten Netzabschnitten schlecht verkraftet.

Die Lösung sind zwei sysctl-Parameter auf dem Origin:

```bash
# Keep congestion window across idle pauses inside keep-alive sessions.
sysctl -w net.ipv4.tcp_slow_start_after_idle=0

# Use BBR instead of CUBIC: better behaviour on long-RTT paths
# with mild packet loss; paces sending instead of bursting.
modprobe tcp_bbr
sysctl -w net.ipv4.tcp_congestion_control=bbr
```

Für die dauerhafte Anwendung:

```bash
cat > /etc/sysctl.d/99-pss-net.conf <<EOF
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_congestion_control    = bbr
EOF
sysctl --system
```

Die Hauptwirkung erzielt der erste Parameter (`tcp_slow_start_after_idle=0`) — er beseitigt unmittelbar den erneuten Slow Start zwischen den Segmentanfragen innerhalb derselben Keep-alive-Verbindung. Der zweite (BBR) bringt zusätzliche Stabilität und gilt für alle neuen Verbindungen. Das Tuning erfordert keinen Neustart von Perfect Streamer. Für HTTP/3-Clients (QUIC) gelten diese Parameter nicht: QUIC arbeitet über UDP mit eigener Überlastungssteuerung.

## 11. Sicherheit

### 11.1. Session URL

Eine URL im Format `/h<sess>/...` erfüllt die Funktion eines Sitzungstokens — sie erfordert keine erneute Authentifizierung. Die Lebensdauer ist durch ein Inaktivitäts-Timeout von 60 Sekunden begrenzt; bleibt Aktivität aus, wird die Sitzung automatisch gelöscht.

Anforderungen:

- HTTPS für alle OTT-Pfade (`/hls/`, `/dash/`, `/h<sess>/`) im Produktivbetrieb;
- Die Session ID im Header `Location` der 302-Antwort wird nicht gecacht (`no-cache, no-store`).

### 11.2. Rate limiting

```nginx
limit_req_zone $binary_remote_addr zone=dash_top:10m  rate=5r/s;
limit_req_zone $binary_remote_addr zone=hls_top:10m   rate=5r/s;
limit_req_zone $binary_remote_addr zone=llhls_top:10m rate=5r/s;

server {
    location /dash/ {
        limit_req zone=dash_top burst=20 nodelay;
        proxy_pass http://pss_backend;
    }
    location /hls/ {
        limit_req zone=hls_top burst=20 nodelay;
        proxy_pass http://pss_backend;
    }
    location /llhls/ {
        limit_req zone=llhls_top burst=20 nodelay;
        proxy_pass http://pss_backend;
    }
}
```

Die Session-URL (`/h<sess>/`) erfordert kein Rate Limiting — die Verarbeitung ist günstig, die Antworten werden gecacht.

### 11.3. Caching von Fehlerantworten

```nginx
proxy_cache_valid 200 60s;
proxy_cache_valid 301 302 0s;
proxy_cache_valid 404 403 0s;
proxy_cache_valid any 1s;
```

Verbietet das Caching von Redirects (eindeutige sess in Location) sowie von Antworten mit Autorisierungsfehlern oder fehlender Ressource.

### 11.4. Beschränkung des Netzwerkzugriffs auf den Origin

Der Port des Streaming-HTTP-Servers (standardmäßig 43972; 43982 für HTTPS) muss für externen Verkehr gesperrt sein. Zulässige Konfigurationen:

1. Perfect Streamer an `127.0.0.1` binden (bei lokalem nginx)
2. Firewall-Regel:

```
iptables -A INPUT -p tcp -m multiport --dports 43972,43982 ! -s 10.0.0.0/8 -j DROP
```

## 12. Integration mit Middleware

### 12.1. Modell prefix-login

Perfect Streamer unterstützt die Delegierung der Benutzeridentifikation an ein Middleware-System über den Mechanismus prefix-login — eine eingebaute Alternative zur vollständigen Integration mit einem externen Billing-System (Konten, die von einem externen System geprüft werden, werden auf dem Bildschirm „Benutzer / Logins“ auf der Registerkarte „Extern (Billing)“ konfiguriert, [Benutzer / Logins](../webui/configure.md#webui-users)).

Beim lokalen Login wird das Präfix-Merkmal aktiviert, woraufhin der Server URLs mit einem Login der Form `<prefix><Abonnenten-ID>` akzeptiert:

```
/dash/test1/sub42/xxx/index.mpd
/hls/test1/sub43/xxx/index.m3u8
```

Hier ist `sub` das konfigurierte Login-Präfix, und `42`/`43` sind die Abonnentenbezeichner der Middleware; das Passwort ist gemeinsam.

### 12.2. Statistik nach Abonnenten

In der Liste der Clients trägt jede Sitzung sowohl den konfigurierten Login als auch den tatsächlichen URL-Login des Abonnenten (match-login), über den die Middleware die Sitzungen ihren eigenen Teilnehmern zuordnet. Die Daten sind auf dem Bildschirm „Clients“ ([Clients](../webui/monitor.md#webui-clients)) sichtbar.

### 12.3. Einschränkungen von prefix-login

- **Gemeinsames Passwort.** Alle Abonnenten eines prefix-Pools verwenden denselben Passwortwert. Eine Kompromittierung des Passworts gewährt Zugriff auf jedes `<prefix><Zeichenfolge>`.
- **Zugriffsgranularität.** Die Liste der erlaubten Streams gilt für den gesamten prefix-Pool. Zugriff auf der Ebene einzelner Abonnenten bietet die Integration mit einem externen Billing-System.
- **Passwortrotation.** Eine Änderung des Passworts trennt alle aktiven Abonnenten. Für einen schrittweisen Wechsel ist die vorübergehende Nutzung zweier prefix-Logins erforderlich.
