Eine API soll standardmĂ€Ăig JSON liefern, aber manche Clients möchten XML. Eine Website soll Deutsch zeigen, wenn der Browser Deutsch bevorzugt. Und ein Bild-Endpoint soll AVIF liefern, wenn der Browser es kann, sonst WebP oder JPEG. Genau fĂŒr solche FĂ€lle gibt es Content Negotiation (Inhaltsaushandlung) in HTTP: Client und Server einigen sich ĂŒber Header darauf, welches Format, welche Sprache oder welche Codierung am besten passt.
In der Praxis wird das Thema oft unterschĂ€tzt. Dann entstehen Endpoints wie /api/v1/data.json, ?lang=de oder separate Routen pro Format. Das funktioniert, ist aber hĂ€ufig schwer zu pflegen und fĂŒhrt zu Inkonsistenzen. Mit sauberer Aushandlung ĂŒber Header bleibt die URL stabil und die Regeln sind an einer Stelle konzentriert.
WofĂŒr Content Negotiation im Alltag wirklich genutzt wird
Formatwahl: JSON, HTML, XML oder CSV
Die bekannteste Variante ist die Formatwahl ĂŒber den Accept-Header. Ein Client sagt damit, welche Medien-Typen (MIME Types) er akzeptiert, zum Beispiel:
- application/json fĂŒr JSON
- text/html fĂŒr HTML
- application/xml fĂŒr XML
- text/csv fĂŒr CSV
Wichtig: Der Server entscheidet am Ende. Der Client Ă€uĂert nur PrĂ€ferenzen. Wenn ein Format nicht angeboten wird, sollte der Server das klar signalisieren (dazu spĂ€ter mehr).
Sprachen: Deutsch vs. Englisch
FĂŒr Sprache ist Accept-Language zustĂ€ndig, etwa de-DE,de;q=0.9,en;q=0.8. Browser schicken diesen Header automatisch. Der Server kann damit zum Beispiel:
- die richtige Ăbersetzung wĂ€hlen,
- Datums-/Zahlenformate lokalisiert ausgeben,
- Fallbacks nutzen (z. B. erst de-DE, dann de, dann en).
Wenn eine App zusÀtzlich eine explizite Spracheinstellung im Profil hat, kann diese Vorrang bekommen. Content Negotiation ist dann ein guter Default, aber nicht die einzige Quelle.
Kompression und Varianten (kurzer Ăberblick)
Es gibt weitere Formen, z. B. Aushandlung von Kompression ĂŒber Accept-Encoding oder Bildformaten ĂŒber Accept (z. B. image/avif). Diese Logik ist Ă€hnlich, nur der Fokus ist ein anderer. Der Kern bleibt: Client sendet PrĂ€ferenzen, Server wĂ€hlt eine passende Variante.
So lesen Server die Header: Werte, PrioritÀten und q-Faktoren
Was bedeutet q=0.8?
In Accept-Headern können PrioritĂ€ten ĂŒber sogenannte QualitĂ€tsfaktoren (q-Werte) angegeben werden. Beispiel:
- Accept: application/json;q=1.0, application/xml;q=0.8, */*;q=0.1
Das bedeutet: JSON ist am liebsten, XML geht auch, und âirgendwasâ ist nur Notlösung. Wenn kein q angegeben ist, gilt implizit q=1.0.
Wildcards sind erlaubt â aber tĂŒckisch
Viele HTTP-Clients senden standardmĂ€Ăig Accept: */*. Das heiĂt nicht âliefere allesâ, sondern âmir ist das Format egalâ. FĂŒr APIs ist es sinnvoll, dann einen klaren Standard zu definieren (meist JSON) und nicht zufĂ€llig HTML zu liefern, nur weil derselbe Host auch eine Website ausliefert.
Warum Reihenfolge allein nicht reicht
Manche Entwickler verlassen sich auf die Reihenfolge in der Header-Liste. Das ist riskant: Ausschlaggebend ist primÀr q, nicht die Position. Eine robuste Implementierung wertet q korrekt aus und nutzt Reihenfolge nur als Tie-Breaker (wenn zwei Optionen den gleichen q-Wert haben).
Robuste Umsetzung auf dem Server: Regeln, Defaults, klare Fehler
Schritt 1: UnterstĂŒtzte Varianten definieren
Startpunkt ist immer eine feste Liste von Varianten, die der Server wirklich liefern kann. Zum Beispiel:
- Format: application/json, text/csv
- Sprache: de, en
Diese Liste sollte nicht âtheoretischâ sein, sondern tatsĂ€chlich implementiert. Sonst entstehen Situationen, in denen ein Client ein Format korrekt anfragt, aber der Server unvollstĂ€ndige Daten liefert.
Schritt 2: Sinnvolle Defaults festlegen
Wenn der Client keine PrÀferenz sendet (oder nur */*), braucht es einen Default. Typisch:
- API: JSON als Standard
- Website: HTML als Standard
- Sprache: z. B. Deutsch oder Englisch, abhÀngig vom Projekt
Wichtig ist die Konsistenz: Ein Endpoint sollte nicht je nach Client âmal so, mal soâ wirken. Wenn eine Route eine API ist, sollte sie im Zweifel immer API-Verhalten zeigen.
Schritt 3: Fehlersituationen sauber abbilden
Wenn der Client ausschlieĂlich Formate anfragt, die nicht unterstĂŒtzt werden, ist ein âbest effortâ oft keine gute Idee. Dann ist ein klarer HTTP-Fehler hilfreicher. Bei Formatwahl ist dafĂŒr ĂŒblich:
- HTTP 406 (Not Acceptable), wenn kein angefragtes Format lieferbar ist
FĂŒr Sprache gilt das strenger selten, weil ein Fallback (z. B. auf Englisch) meist akzeptabel ist. Dennoch sollte die Anwendung einheitliche Regeln haben: Entweder Sprache ist âverhandelbar mit Fallbackâ, oder Sprache ist âhartâ und fĂŒhrt zu einer Fehlermeldung.
Praktische Schritte fĂŒr ein sauberes Setup
- Auf dem Server eine kleine Funktion bauen, die den Accept-Header gegen eine Whitelist matcht (inklusive q-Werten).
- FĂŒr APIs einen Default (z. B. JSON) definieren, wenn Accept fehlt oder */* ist.
- Bei Sprache zuerst eine explizite Einstellung (z. B. Benutzerprofil) berĂŒcksichtigen, danach Accept-Language, danach Fallback.
- Bei nicht unterstĂŒtzten Formaten konsequent mit 406 antworten, statt âirgendwasâ zu liefern.
- Die tatsÀchlich gewÀhlte Variante im Response klar machen (Content-Type setzen, ggf. Content-Language).
Typische Stolperfallen in APIs und Webapps
Format in die URL packen: schnell gebaut, schwer gepflegt
URLs wie /resource.json oder /resource.xml sind simpel, aber fĂŒhren schnell zu doppelten Routen und doppelter Dokumentation. AuĂerdem wird Caching unĂŒbersichtlich, weil unterschiedliche Varianten zwar unterschiedliche URLs haben, aber fachlich denselben âRessourcen-Namenâ tragen.
Sprache per Query-Parameter: okay, aber mit klaren Regeln
?lang=de kann sinnvoll sein (z. B. fĂŒr sharebare Links). Dann sollte aber klar sein, wie es mit Accept-Language zusammenspielt: Query ĂŒberschreibt Header, oder umgekehrt. Ohne feste PrioritĂ€t entstehen schwer reproduzierbare Bugs.
Cache-Fallen: Varianten brauchen saubere Trennung
Wenn ein Endpoint je nach Accept oder Accept-Language unterschiedliche Inhalte liefert, muss Caching das berĂŒcksichtigen. Sonst kann es passieren, dass ein Cache Deutsch speichert und spĂ€ter Englisch ausliefert (oder umgekehrt). In solchen FĂ€llen ist der HTTP-Header Vary entscheidend: Er sagt Caches, dass die Antwort je nach bestimmten Request-Headern variiert. Typisch sind Vary: Accept und/oder Vary: Accept-Language.
Wer bereits Caching nutzt, kann das Thema gut mit HTTP Caching: Cache-Control und ETag richtig nutzen zusammendenken, weil Varianten und Cache-Strategie zusammengehören.
Mini-Fallbeispiel: Ein Endpoint, zwei Formate, zwei Zielgruppen
Ausgangslage
Ein Team betreibt einen Endpoint /reports. Intern nutzt das Dashboard JSON. Externe Partner möchten regelmĂ€Ăig CSV importieren. Anfangs gab es zwei Routen: /reports (JSON) und /reports.csv (CSV). Das fĂŒhrte zu doppelter Authentifizierung, doppelter Dokumentation und zwei Fehlerquellen.
Lösung ĂŒber Accept
Beide Clients nutzen dieselbe URL, aber senden unterschiedliche Header:
- Dashboard: Accept: application/json
- Partner: Accept: text/csv
Der Server liefert die passende ReprÀsentation und setzt Content-Type entsprechend. Wenn ein Client etwas Unbekanntes anfragt, kommt 406 mit einer klaren Fehlermeldung. Ergebnis: weniger Routen, weniger Dokumentationsaufwand, und Format-Erweiterungen sind einfacher.
Vergleich: Header-Aushandlung vs. separate Endpoints
| Ansatz | Vorteile | Nachteile |
|---|---|---|
| Header (Accept, Accept-Language) | Eine URL pro Ressource, klare Standards, gut erweiterbar | Erfordert saubere Implementierung (q, Vary), Debugging ĂŒber Header nötig |
| Separate URLs pro Format | Schnell zu verstehen, leicht manuell im Browser testbar | Doppelte Routen/Docs, hÀufiger Drift zwischen Varianten |
| Query-Parameter (z. B. ?format=csv) | Sharebare Links, einfach in Tools | Oft Mischformen ohne klare PrioritÀt, kann mit Caching kollidieren |
Wie das mit API-Design und Fehlern zusammenhÀngt
Klare Antworten statt âzufĂ€llig passendâ
Content Negotiation ist kein Ersatz fĂŒr gutes API-Design, aber ein Baustein davon. Wer Formate aushandelt, sollte auch Fehler konsistent modellieren: Wenn ein Client ein falsches Format anfragt, ist das ein anderes Problem als âfalsche Daten gesendetâ. FĂŒr die grundsĂ€tzliche Fehlerlogik hilft API-Fehler richtig behandeln.
Security: Nicht alles akzeptieren, was ein Client behauptet
Header sind Eingaben. Sie sollten daher nicht blind vertrauenswĂŒrdig sein. Statt âAccept: application/pdfâ automatisch zu bedienen, sollte der Server nur aus bekannten, implementierten Varianten wĂ€hlen. Das reduziert AngriffsflĂ€che und verhindert unerwartete Content-Type-Ausgaben.
HĂ€ufige Fragen aus der Praxis
Reicht es, immer JSON zu liefern und Accept zu ignorieren?
FĂŒr viele private APIs ja, solange es dokumentiert und konsistent ist. SpĂ€testens wenn mehrere Clients oder Export-Formate dazukommen, lohnt sich Content Negotiation, weil sie spĂ€tere Ănderungen ohne neue URLs ermöglicht.
Sollte Sprache ĂŒber Accept-Language oder ĂŒber einen Parameter gesteuert werden?
FĂŒr Websites ist Accept-Language ein guter Start, weil Browser ihn automatisch senden. FĂŒr Apps mit Nutzerprofil ist eine explizite Einstellung oft besser. Wenn Links teilbar sein mĂŒssen, ist ein Parameter sinnvoll, aber mit klarer PrioritĂ€tsregel.
Kann Content Negotiation Breaking Changes vermeiden?
Sie kann helfen, neue Formate parallel anzubieten, ohne alte abzuschalten. FĂŒr echte Versionierung (z. B. unterschiedliche Felder im JSON) ist das aber nicht gedacht. DafĂŒr ist ein eigenes Versionierungs-Konzept besser geeignet, zum Beispiel wie in API Versioning verstehen beschrieben.
Welche Header sollten Responses setzen?
Mindestens Content-Type. Bei Sprache zusÀtzlich Content-Language. Und wenn sich Antworten je nach Header unterscheiden, sollte Vary gesetzt werden, damit Caches korrekt arbeiten.

