HTTP-inhoudsonderhandeling in hosting: optimaal serverantwoordformaat

HTTP inhoudsonderhandeling past bij de serverantwoord formaat in de hosting past zich automatisch aan de eisen van de klant aan en evalueert headers zoals Accept, Accept-Language en Accept-Encoding. Afhankelijk van de header lever ik de beste variant - zoals JSON in plaats van XML, Gzip of Brotli en de juiste taal - en versterk zo de weboptimalisatie merkbaar.

Centrale punten

De volgende belangrijke punten geven een kort overzicht voordat ik de implementatie stap voor stap uitleg.

  • Kop controleformaat, taal, tekenset en compressie.
  • Servergestuurd Onderhandelen verkort de retourritten en versnelt de levering.
  • Vary-header voorkomt cacheverwarring en houdt varianten netjes gescheiden.
  • Tegenvallers met JSON/HTML en status 406 zorgen voor voorspelbaar gedrag.
  • q-waarden controleprioriteiten als er meerdere varianten mogelijk zijn.

Wat is HTTP-onderhandeling over inhoud bij hosting?

Ik gebruik Onderhandeling over inhoud, om een bron in de best mogelijke variant af te leveren zonder meerdere eindpunten te bouwen. De client stuurt voorkeuren in Accept, Accept-Language, Accept-Charset en Accept-Encoding headers, en ik antwoord met de juiste serverantwoord formaat. Een browser ontvangt bijvoorbeeld HTML, een bot JSON en een afbeeldingsclient WebP of AVIF. In hostingconfiguraties domineert de servergestuurde onderhandeling omdat deze geen extra rondreizen veroorzaakt en direct op de headers reageert. Als er geen geschikte variant overblijft, reageer ik consequent met 406 Niet Aanvaardbaar zodat clients een duidelijk signaal krijgen.

Request en response headers in een oogopslag

Voor betrouwbare onderhandelingen let ik altijd op twee kanten: De inkomende Kop verzoek met voorkeuren en de uitgaande antwoordkoppen met unieke labels. Accept toont toegestane mediatypen, Accept-Language voorkeurstalen, Accept-Charset de tekenset en Accept-Encoding mogelijke compressies. Ik stel het antwoord in met Content-Type, Content-Language, Content-Encoding en de juiste Vary-header, zodat caches geen onjuiste varianten serveren. De Vary header vertelt caches welke kenmerken ze moeten gebruiken om onderscheid te maken tussen varianten, zoals Vary: Accept, Accept-Language. Als je content negotiation http gebruikt, moet je deze headercombinatie consequent onderhouden, anders treden er fouten op in de cache.

Kop Doel Voorbeeld Belangrijk antwoord Cache hint
Accepteer Toegestane mediatypen toepassing/json; q=0.9, tekst/html; q=0.8 Content-Type: application/json Vary: Accepteren
Accept-Language Gewenste talen de-DE, en-US; q=0,7 Inhoud-Taal: de-DE Vary: Accept-Language
Charset accepteren Tekenset utf-8 Inhoud-Type: text/html; charset=utf-8 Vary: Charset accepteren
Accept-Encoding Compressie br, gzip; q=0.8 Inhoud codering: br Vary: Accept-Encoding

Server-gestuurd, client-gestuurd en verzoek-gestuurd

Ik maak onderscheid tussen drie benaderingen en kies, afhankelijk van het project, de geschikt Model. Servergestuurd (proactief) is mijn standaard, omdat de server direct beslist op basis van de headers en direct een variant terugstuurt. Clientgestuurd (reactief) laat de client kiezen uit een lijst, maar genereert extra werk door extra verzoeken. Request-driven mengt beide, bijvoorbeeld door parameters in de URL te tellen samen met Accept headers. Voor hostingomgevingen met een hoge belasting is servergestuurd gedrag overtuigend omdat het round trips bespaart, caches ontlast en duidelijke regels mogelijk maakt.

Apache: .htaccess, MultiViews en typemaps

Op Apache activeer ik MultiViews of gebruik type maps om automatisch taal- en formaatvarianten te serveren. MultiViews staat bestandsparen toe zoals index.html.de en index.html.en, die Apache selecteert op basis van Accept-Language. Ik stel q-waarden in voor mediatypen zodat moderne formaten voorrang krijgen, zoals image/webp voor image/jpeg. Ik zorg er altijd voor dat ik Vary correct instel en 406 lever als clients een formaat aanvragen dat niet wordt ondersteund. Dit houdt het gedrag voorspelbaar en voorkomt dat caches tegenstrijdige antwoorden opslaan.

# .htaccess
Opties +MultiViews

# Voorbeeld voor Type-Map (file.var)
URI: afbeelding
Inhoudstype: image/webp; qs=0.9
Inhoudstype: afbeelding/jpeg; qs=0.8
Inhoud-taal: de

# Taalvariant automatisch bedienen
# bestanden: index.html.de, index.html.en

Nginx: kaart, Lua en randlogica

Onder Nginx stel ik vaak kaart-directieven om Accept-headers te evalueren en geschikte eindpunten toe te wijzen. Voor API's redirect ik tussen HTML en JSON afhankelijk van Accept, optioneel aangevuld met Lua voor fijnere regels. Ik houd de Vary header in de gaten, omdat caches beslissingen moeten koppelen aan Accept en Accept-Language. In gedistribueerde opstellingen verplaats ik delen van de onderhandeling naar edge nodes om latentie te minimaliseren. Een whitelist blijft belangrijk zodat ik alleen geverifieerde mediatypes aanbied en niet val voor exotische formaten.

# nginx.conf (uittreksel)
map $http_accept $fmt {
  standaard "html";
  "~*application/json" "json";
  ~*application/json" "json"; ~*application/json" "json";
}

server {
  add_header Vary "Accept, Accept-Language";
  locatie /api {
    try_files $uri/ /api.$fmt;
  }
}

Caching, Vary en SEO-signalen

Zonder correcte Variëren-headers, gedragen caches zich onvoorspelbaar en leveren onjuiste varianten aan andere gebruikers. Ik stel Vary precies in op de headers die ik gebruik om te differentiëren, d.w.z. meestal Accept, Accept-Language en Accept-Encoding. Dit versterkt niet alleen de consistentie, maar geeft ook duidelijke signalen voor prestaties, wat indirect SEO-voordelen oplevert. Degenen die dieper willen ingaan op headerstrategieën zullen baat hebben bij deze gids voor HTTP-headers voor prestaties en SEO. Ik controleer ook of de CDN-cache key deze dimensies in kaart brengt, zodat edge nodes de juiste objecten bevatten.

API's: Whitelisting formaten en schone fallback

Met API's bewaar ik de ondersteunde mediatypen in een Whitelist bijvoorbeeld application/json en application/xml. Als de Accept header ontbreekt of niets past, lever ik JSON als standaard, omdat dit het meest ondersteund wordt. Als een client expliciet om een onbekend formaat vraagt, reageer ik met 406 Niet Aanvaardbaar in plaats van stilletjes te raden. De profielinstellingen van een gebruiker hebben voorrang op Accept als de applicatie dit aangeeft. Ik zorg ervoor dat deze regels gecentraliseerd, reproduceerbaar en gevalideerd zijn door middel van tests, zodat integraties stabiel blijven.

Talen, lettertypen en toegankelijkheid

Voor Meertaligheid Ik gebruik Accept-Language om automatisch taalvarianten te selecteren en Content-Language te markeren in het antwoord. Ik formuleer fallbacks duidelijk: als de gewenste taal niet bestaat, gebruik ik een gedefinieerde standaardtaal. Ik gebruik Accept-Charset om ervoor te zorgen dat UTF-8 overal van toepassing is, zodat speciale tekens consistent worden weergegeven. Schermlezers hebben ook baat bij correcte taalnamen in de content language en lang attributen in de markup. Dit houdt de levering inclusief, transparant en technisch schoon.

Afbeeldingen, compressie en mediatypen

Als het op afbeeldingen aankomt, geef ik moderne formaten een Projectie en let op de Accept headers van browsers. Als de klant AVIF of WebP ondersteunt, lever ik bij voorkeur deze versies, anders kies ik voor JPEG of PNG. Deze praktische gids helpt me om te kiezen tussen WebP en AVIF WebP vs AVIF vergelijking. Ik verminder de hoeveelheid gegevens ook aanzienlijk door acceptatiecodering met Brotli of Gzip te gebruiken, in de praktijk vaak tot 50 %. Dit bespaart bandbreedte, verkort de time-to-first-byte en stabiliseert de waargenomen snelheid.

Meten, testen, uitrollen

Ik meet het effect van de onderhandeling voortdurend, anders blijft het potentieel bestaan. ongebruikt. Ik gebruik curl om varianten te controleren, zoals curl -H „Accept: application/json“ of curl -H „Accept-Language: de“. Ik controleer hitrates per variant in logs en vergelijk ze met CDN-statistieken. Voor coderingsstrategieën en Brotli-graden vergelijk ik resultaatcurves voordat ik globale standaardwaarden instel. Deze gids voor setup en tuning geeft me een compacte introductie tot de HTTP-compressie configureren, die ik parallel met de onderhandelingen coördineer.

Foutcodes en randgevallen in de praktijk

Ik maak een duidelijk onderscheid tussen 406 Niet Aanvaardbaar en 415 Niet Ondersteund Mediatype: ik stel 406 in als het Antwoord niet beschikbaar is in een geaccepteerde variant (Accept geweigerd); ik gebruik 415 als de Aanvraag stuurt een niet-ondersteund mediatype (inhoudstype van de payload van het verzoek). In zeldzame gevallen is 300 Multiple Choices zinvol als ik de client meerdere exact overeenkomende varianten wil aanbieden - in de praktijk gebruik ik echter duidelijke standaardinstellingen in plaats van interactieve selectie in omgevingen met een hoge belasting. Voor caching blijf ik reageren met 304 Not Modified per variant; ETag en Last-Modified zijn altijd variant-specifiek van toepassing. Als Accept volledig ontbreekt, interpreteer ik dit als „alles is toegestaan“ en gebruik ik de gedefinieerde standaard (meestal JSON voor API's, HTML voor websites). Als een klant q=0 instelt voor een type, sluit ik deze variant expliciet uit.

Beveiliging: sniffing, whitelists en inputhygiëne

Ik laat de browser het inhoudstype niet „raden“, maar lieg met consistent inhoudstype en X-Content-Type-Options: nosniff opgelost. In de onderhandelingslogica accepteer ik alleen types/talen die op de witte lijst staan en beperk ik headerlengtes zodat ongewoon lange accept-taallijsten geen bronnen in beslag nemen. Voor logs en statistieken maak ik headerwaarden schoon om injectierisico's te vermijden. Ik let ook op gegevensbescherming: met Accept-taal kunnen conclusies worden getrokken over gebruikers; ik sla alleen zoveel op als nodig is en aggregeer voor statistieken. Met CORS laat ik onderhandeling onafhankelijk beslissen - ik bind cross-origin regels apart aan Origin/Methods/Headers, niet aan Accept-varianten, zodat ik geen onbedoelde autorisaties genereer.

CDN, cache-sleutels en ETags per variant

Met CDN's definieer ik de cache-sleutel bewust als variabel. Naast URL omvat dit Accept, Accept-Language en Accept-Encoding, precies zoals ik aangeef in de Vary-header. Ik stel mijn eigen ETags in voor elke variant (bijv. hash met achtervoegsel „.json.de.br“) en zorg ervoor dat voorwaardelijke verzoeken correct werken. Voor statische assets werk ik met vooraf gegenereerde, gecomprimeerde bestanden (br/gz) die het CDN 1:1 serveert. Om de origin load te verminderen, gebruik ik „collapsed forwarding“ of „stale-while-revalidate“: de eerste misser wordt bijgewerkt, alle anderen ontvangen een verse of „stale acceptable“ variant. Ik combineer bereikverzoeken alleen met compressie als de server en CDN consequent omgaan met de functie; anders schakel ik bereik uit voor dynamisch gecomprimeerde antwoorden om fragmentatie van de varianten te voorkomen.

q-waarden, jokertekens en overeenstemmingsalgoritme

Als er meerdere varianten van toepassing zijn, sorteer ik op q-waarden en precisie: exact type/subtype verslaat type/*, beide verslaan */*. Als q gelijk is, wint de specifiekere variant. Als de klant geen q-waarde instelt, interpreteer ik deze als 1,0. Met q=0 sluit de klant expliciet een type uit. Voor afbeeldingen en documenten geef ik de voorkeur aan moderne formaten met een iets hogere q, maar bied ik fallbacks als de client bijvoorbeeld AVIF niet herkent.

# Pseudocode voor acceptatiematching
parseer acceptHeader in kandidaten (type, subtype, q)
voor variant in serverVarianten:
  score = 0
  voor cand in kandidaten:
    als cand.type == variant.type en cand.subtype == variant.subtype:
      score = max(score, 1000 * cand.q + 2) # precies
    elif cand.type == variant.type en cand.subtype == "*":
      score = max(score, 1000 * cand.q + 1) # type/*
    elif cand.type == "*" en cand.subtype == "*":
      score = max(score, 1000 * cand.q) # */*
  beste score toekennen
kies variant met hoogste score of 406 als alle scores 0 zijn

Ik ga op een vergelijkbare manier te werk met Accept-Language: „de-CH“ geeft voorrang aan „de-CH“ boven „de“, pas daarna valt de keuze op de globale standaard. Ik houd de selectie deterministisch zodat caches betrouwbare objecten opslaan.

Voorbeelden van frameworks: Express/Node en Go

In toepassingsframeworks kapsel ik de regels in middleware in, stel ik Vary consequent in en houd ik fallbacks gecentraliseerd.

// Express/Node (vereinfacht)
const vary = require('vary');

function negotiate(req, res, next) {
  vary(res, 'Accept, Accept-Language, Accept-Encoding');

  const types = req.accepts(['json', 'html']);
  const lang = req.acceptsLanguages(['de', 'en']) || 'de';
  res.set('Content-Language', lang);

  if (!types) return res.status(406).send('Not Acceptable');

  if (types === 'json') {
    res.type('application/json; charset=utf-8');
    return res.json({ ok: true, lang });
  }
  res.type('text/html; charset=utf-8');
  res.send(`<html lang="${lang}">OK</html>`);
}

app.get('/resource', negotiate);
// Ga net/http (vereenvoudigd)
func negotiateJSON(r *http.Request) bool {
  a := r.Header.Get("Accept")
  if a == "" || strings.Contains(a, "*/*") { return true }
  if strings.Contains(strings.ToLower(a), "application/json") { return true }
  return false
}

func handler(w http.ResponseWriter, r *http.Request) {
  w.Header().Add("Vary", "Accept, Accept-Language, Accept-Encoding")

  Als !negotiateJSON(r) {
    w.WriteHeader(http.StatusNotAcceptable)
    w.Write([]byte("Niet aanvaardbaar"))
    return
  }
  w.Header().Set("Content-Type", "application/json; charset=utf-8")
  io.WriteString(w, `{"ok":true}`)
}

Het is belangrijk dat ik nooit afga op User-Agent, maar alleen op expliciete Accept* headers. Dit maakt gedrag reproduceerbaar en testbaar.

Internationalisering in detail

Ik zet een duidelijke fallback-keten op, bijv. de-CH → de-DE → de → Default. Als een regiocode niet bestaat, breek ik deze af naar de basistaal. In het antwoord gebruik ik Content-Language om precies de geselecteerde variant aan te geven en mengvormen te vermijden. Voorkeuren van gebruikers (zoals accountlocale) hebben voorrang op Accept-Language, maar worden ook deterministisch gekoppeld aan talen die het systeem daadwerkelijk aanbiedt. Voor SEO en toegankelijkheid zorg ik ervoor dat lang attributen in de HTML en content taal consistent zijn; ik voorkom ook redirect loops door de beslissing aan de server kant te maken en caches correct te instrueren via Vary.

Verzoekgestuurde varianten netjes canoniseren

Als ik URL-parameters combineer (bijv. formaat=json) met Accept, heeft de pagina een duidelijke canonicalisatie nodig: of ik accepteer de parameter als een harde standaard en negeer Accept, of de parameter is slechts een hint die kan worden overschreven door Accept. Ik documenteer de regel duidelijk en stel consistente headers in het antwoord in zodat caches geen twee verschillende weergaven van dezelfde URL opslaan zonder een scheidende Vary sleutel. Voor HTML-pagina's zorg ik ook voor een uniek canoniek adres per taal-/formaatvariant binnen het systeem, zodat analyses en monitoring geen duplicaten tellen.

Fijnafstemming en pre-produceren van compressie

Voor dynamische reacties weeg ik de CPU-kosten van compressie af tegen de netwerkbesparingen. Brotli op niveau 4-6 biedt meestal een goede verhouding; hogere niveaus zijn vooral de moeite waard voor statische activa die ik van tevoren comprimeer. Ik houd zowel br als gzip bij de hand voor grote bestanden omdat niet alle clients Brotli ondersteunen. In de praktijk sla ik de voorgecompileerde bestanden op met bestandsextensies (.br/.gz), laat de server beslissen op basis van de drempels voor het accepteren van codering en bestandsgrootte en stel de codering van de inhoud correct in. Belangrijk: Elke gecomprimeerde variant krijgt zijn eigen ETag, anders leveren voorwaardelijke verzoeken onjuiste 304 reacties op.

Waarneembaarheid, canary en rollback

Ik introduceer onderhandelingsregels met kenmerkvlaggen, activeer ze stap voor stap (bijv. 5 %, 25 %, 100 %) en monitor kerncijfers voor elke variant: foutpercentage, latency, bytes out, cache hit rate, aandeel 406/415. Ik noteer de geselecteerde variant en de triggering headers (samengevoegd) in de logs zodat ik snel mismatches kan vinden. Voor tests gebruik ik synthetische testers die regelmatig bekende acceptatiecombinaties uitvoeren tegen staging en productie. In het geval van afwijkingen rol ik varianten specifiek terug zonder het hele systeem te stoppen - bijvoorbeeld door AVIF tijdelijk uit te schakelen, JSON als standaard te forceren of de Vary-dimensie te verkleinen totdat de cache zich herstelt.

Samenvatting: Het juiste antwoordformaat loont

Ik lever sneller, bespaar Bandbreedte en de tevredenheid te verhogen als ik contentonderhandeling consistent gebruik. De combinatie van Accept headers, duidelijke fallbacks, q-waarden en Vary zorgt voor stabiele, reproduceerbare antwoorden. In de praktijk geef ik voorrang aan servergestuurde beslissingen, houd ik caches variantgeschikt en test ik elke regel met curl. API's krijgen een strikte whitelist, websites profiteren van taal- en afbeeldingsvarianten en moderne compressie. Op deze manier bereikt het project meetbare voordelen op het gebied van prestaties, toegankelijkheid en onderhoudbaarheid - met een setup die ik doelgericht beheer en op elk moment kan volgen.

Huidige artikelen