Kurzdefinition
Stellen Sie sich ein Gespräch vor, bei dem Ihr Gegenüber nach jedem Satz aus dem Raum geht, alles vergisst und beim Wiedereintreten darauf besteht, dass Sie von vorne beginnen.
So funktionierte das Web bis Mitte der 2000er-Jahre. Klick: Komplettladung. Formularfeld geändert: Komplettladung. Schaltfläche gedrückt: Komplettladung. Das ganze Theater jedes Mal.
Dann kam Ajax.
Ajax (Asynchronous JavaScript and XML) ist ein Muster der Webentwicklung. Der Browser tauscht im Hintergrund Daten mit dem Server aus, während die Seite drumherum bestehen bleibt. Nur die Teile, die sich wirklich ändern, werden neu geholt. Praktisch läuft das heute über die Web-APIs XMLHttpRequest (XHR) und fetch(). Als Datenformat hat sich JSON durchgesetzt, obwohl das X im Namen für XML steht. Eine schöne Pointe, zu der wir gleich kommen.

Ajax aktualisiert einzelne Produktkarten im Webshop, während die restliche Seite bestehen bleibt und kein kompletter Reload nötig ist.
Historische Einordnung
Den Begriff prägte Jesse James Garrett im Februar 2005 in einem kurzen Essay: Ajax: A New Approach to Web Applications.
Interessant daran: Garrett hat die Technologie nicht erfunden. XMLHTTP existierte bereits, als ActiveX-Objekt von Microsoft. Andere Browser hatten den Mechanismus längst als XMLHttpRequest übernommen. Was Garrett tat, war etwas anderes. Er gab dem Zusammenspiel einen Namen.
Manchmal ist genau das die Erfindung. Nicht das Werkzeug. Sondern das Verständnis, wofür es da ist.
Sichtbar wurde das Paradigma durch Google Maps und Gmail. Karten, die sich verschieben ließen, ohne dass die Seite neulud. Eine Mailbox, die sich anfühlte wie ein Programm, nicht wie ein Brief im Formular. Das Web hatte plötzlich einen Puls. Rückblicke zum 20-jährigen Jubiläum ordnen Ajax heute als Katalysator des Web 2.0 ein.
Funktionsprinzip: asynchron, ereignisgesteuert, nicht blockierend
Denken Sie an einen guten Kellner.
Er nimmt Ihre Bestellung auf, geht in die Küche, übergibt sie. Aber er bleibt nicht dort stehen, bis das Essen fertig ist. Er kommt zurück, fragt am Nachbartisch nach Getränken, schenkt Wasser nach, räumt einen Teller ab. Wenn das Essen bereit ist, bringt er es. Dazwischen war er nicht untätig, und vor allem: Das Restaurant stand nicht still.
Genau so funktioniert Ajax.
JavaScript ist im Browser ereignisgesteuert. Eine Anfrage geht an den Server, der Browser wartet nicht an dieser Stelle, sondern reagiert weiter auf Klicks, Scrollen, Eingaben. Trifft die Antwort ein, wird sie über Callbacks, Promises oder async/await dort eingespeist, wo sie hingehört. Das Fetch-Modell ist Promise-basiert und fügt sich konsistent in den Event-Loop und die Microtask-Abwicklung ein.
Die Folge: Die Oberfläche bleibt ansprechbar, während im Hintergrund Daten fließen.
Die zwei APIs: XHR und Fetch
XMLHttpRequest (XHR)
XMLHttpRequest ist die klassische Ajax-Schnittstelle. Sie bietet Methoden wie open() und send(), Zugriff auf Statuscodes, Header, responseText/responseXML, Timeouts sowie Progress-Events. Sie wird von allen Browsern unterstützt und steckt in unzähligen Legacy-Codebasen.
Für neue Entwicklungen gilt XHR heute als Altbau. Funktioniert. Aber niemand baut so noch von vorne.
Minimalbeispiel (XHR, JSON abrufen):
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/products');
xhr.responseType = 'json';
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) renderList(xhr.response);
else showError(`Serverfehler: ${xhr.status}`);
};
xhr.onerror = () => showError('Netzwerkfehler');
xhr.send();
Fetch-API
fetch() ist der moderne Standard. Promises statt Callbacks, saubere Request/Response/Headers-Objekte, Streaming, integrierte CORS-Logik. In der Praxis: kompakterer, testbarerer Code und bessere Komposition mit async/await.
Minimalbeispiel (Fetch mit Fehlerbehandlung):
async function loadProducts() {
try {
const res = await fetch('/api/products', {
headers: { 'Accept': 'application/json' }
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
renderList(data);
} catch (err) {
showError(err.message);
}
}
Anfragen abbrechen mit AbortController:
const ctrl = new AbortController();
document.querySelector('#cancel')
.addEventListener('click', () => ctrl.abort());
try {
const res = await fetch('/api/export', { signal: ctrl.signal });
// ...
} catch (e) {
if (e.name === 'AbortError') console.log('Abgebrochen');
}
Bei abort() verwirft fetch() mit AbortError. Nützlich, wenn der Nutzer auf einen anderen Tab wechselt, ein Formular abbricht oder die Navigation weiterspringt, bevor die Antwort da ist.
Welche von beiden?
Für neue Projekte: Fetch. Punkt. XHR ist sinnvoll, wenn Sie ein etabliertes Projekt mit eigenen Abstraktionen pflegen oder wenn Sie Upload-/Download-Progress-Events brauchen, ohne mit Streams zu arbeiten. Sonst nicht.
Datenformate: warum JSON, nicht XML
Das X im Namen Ajax ist Geschichte.
Als Garrett den Begriff prägte, war XML die Erwartung. Strukturiert, mächtig, durchspezifiziert. Was er nicht war: leichtgewichtig. JSON kam später und gewann den Stilkampf. Weniger Klammern. Weniger Ballast. Direkt in JavaScript verarbeitbar, ohne Parser-Klimmzüge.
JSON ist in IETF RFC 8259 und ECMA-404 spezifiziert. UTF-8 ist Standard, Medientyp: application/json. Damit ist Interoperabilität auf solidem Boden.
Niemand spricht mehr von „Asynchronous JavaScript and JSON“. Das Akronym AJAJ klingt schlicht falsch. Also bleibt Ajax. Mit XML im Namen, aber JSON in der Leitung.
Sicherheit: SOP, CORS, CSRF
Hier wird es ernst. Und hier wird in der Praxis am häufigsten geschlampt.
Same-Origin-Policy (SOP)
Die SOP begrenzt den Zugriff von Skripten auf Ressourcen anderer Origins (Schema, Host, Port). Das ist kein Schikane-Mechanismus. Das ist der Grund, warum eine bösartige Website Ihre Bankseite nicht im Hintergrund auslesen kann, während Sie eingeloggt sind.
Wenn ein einfacher Cross-Site-Aufruf scheitert: System funktioniert wie gedacht.
CORS (Cross-Origin Resource Sharing)
CORS ist die kontrollierte Tür durch diese Wand. Über HTTP-Header wie Access-Control-Allow-Origin erlaubt der Server gezielt, wer von wo zugreifen darf. Bei komplexeren Anfragen löst der Browser einen Preflight aus (eine OPTIONS-Vorab-Anfrage), bevor die eigentliche Anfrage rausgeht.
Eine Stolperfalle, die in Tutorials oft unterschlagen wird: Bei Credentialed Requests (also Anfragen mit Cookies oder HTTP-Auth) muss der Server Access-Control-Allow-Credentials: true senden und eine konkrete Origin ausweisen. Der Platzhalter * ist hier verboten.
Browserseitig setzen Sie das so:
- Fetch:
credentials: 'include'oder Standardsame-origin - XHR:
withCredentials = true
Ein Cross-Origin-Request mit Credentials erhöht das CSRF-Risiko. Das führt direkt zum nächsten Punkt.
CSRF bei Cookie-Authentisierung
Wenn Authentifizierung über Cookies läuft, sind Ajax-Endpunkte gegen CSRF zu härten. Etablierte Mittel:
- Synchronizer Token
- Double-Submit-Cookies
- SameSite-Cookies (Lax oder Strict)
- Origin/Referer-Prüfung serverseitig
Und: XSS hebelt CSRF-Schutz aus. Wer also irgendwo unverarbeiteten Nutzer-Input ins DOM lässt, kann sich das CSRF-Token-Theater sparen. Wer dachte, Ajax sei automatisch sicher, weil „kein Formular submit“, liegt falsch. Eine HTTP-Anfrage bleibt eine HTTP-Anfrage.
Netzwerk und Performance
HTTP-Semantik und Idempotenz
Ajax setzt auf die Semantik der HTTP-Methoden:
- GET: sicher, cacheable
- PUT, DELETE: idempotent (mehrfache Ausführung führt zum gleichen Ergebnis)
- POST, PATCH: zustandsverändernd
Diese Eigenschaften sind nicht bürokratisch. Sie sind die Grundlage für sinnvolle Retry-Strategien. Einen POST automatisch wiederholen, weil das Netzwerk gewackelt hat? Vielleicht haben Sie gerade zweimal kassiert.
Caching: Header, Modi, Service Worker
Drei Ebenen, die zusammenspielen:
1. HTTP-Caching über Header. Cache-Control mit Direktiven wie max-age, no-store, stale-while-revalidate oder immutable reduziert Netzlast und Latenz. Ohne explizite Direktiven greifen heuristische Caches mit Effekten, die niemand bewusst gewollt hat.
2. Fetch-Cache-Modi. Über Request.cache steuern Sie pro Request, ob der Browser den Cache umgeht, revalidiert oder bevorzugt aus dem Cache liefert. Praktisch, wenn einzelne Endpunkte aus der globalen Policy ausbrechen sollen.
3. Service Worker. Mit Service Worker und Cache API bauen Sie Offline-First-Erlebnisse, Pre-Caching und feingranulares Runtime-Caching. Voraussetzung: HTTPS. Lokal gilt localhost als sicherer Ursprung.
// sw.js (stark vereinfacht)
self.addEventListener('install', e => {
e.waitUntil(caches.open('app-v1').then(c => c.addAll([
'/', '/styles.css', '/app.js'
])));
});
self.addEventListener('fetch', e => {
e.respondWith(
caches.match(e.request).then(cached => cached || fetch(e.request))
);
});
Besonders elegant ist das Pattern stale-while-revalidate: Abgelaufene Antworten werden kurz weiter ausgeliefert, während im Hintergrund eine Revalidierung läuft. Gefühlt sofort. Inhaltlich aktuell.
HTTP/2 und HTTP/3
Neuere Protokolle senken Latenz und verbessern Multiplexing sowie Resilienz. HTTP/3 setzt auf QUIC/UDP.
Wichtig: Diese Protokolle ersetzen kein sauberes Request-Design. Wenn Sie 200 Mini-Anfragen pro Sekunde rausjagen, hilft kein Protokoll der Welt. Bündeln, Deduplizieren, Debounce, Throttle. Das eigene Verhalten ist die größte Stellschraube.
Barrierefreiheit: was Screenreader nicht von selbst mitbekommen
Ajax aktualisiert Inhalte ohne Seitenwechsel. Das ist eine UX-Stärke und eine A11y-Schwäche zugleich.
Denn: Was sich im DOM ändert, merkt ein Screenreader nicht automatisch. Er liest, was auf der Seite stand, als sie geladen wurde. Wenn sich später ein Statusfeld ändert, weil ein Formular abgeschickt wurde, hört der sehende Nutzer das nicht. Der nicht-sehende auch nicht. Außer Sie sagen es ihm.
Genau dafür gibt es WAI-ARIA Live-Regionen. Mit Attributen wie role="status", aria-live="polite" und optional aria-atomic teilen Sie dem assistiven Werkzeug mit: Hier ändert sich etwas, das beachtenswert ist.
Beispiel:
<div id="status" role="status" aria-live="polite" aria-atomic="true"></div>
<script>
async function submitForm(data) {
const status = document.getElementById('status');
status.textContent = 'Sende Daten …';
const res = await fetch('/api/submit', { method:'POST', body: data });
status.textContent = res.ok ? 'Erfolg.' : 'Fehler.';
}
</script>
Sparsam einsetzen. Wenn jede Kleinigkeit eine Live-Region ist, wird Ihr Screenreader-Nutzer das Gerät zur Seite legen. Die Regel: Was ist die Statusmeldung wert? ARIA 1.2 ist W3C-Empfehlung und definiert Rollen und Attribute für dynamische Inhalte.
SEO bei Ajax-basierten Seiten
Eine verbreitete Annahme: Google rendert JavaScript inzwischen, also ist alles gut.
Stimmt halb.
Google nutzt tatsächlich eine Evergreen-Chromium-Engine für JavaScript-Rendering. Aber: Rendern ist teurer als Crawlen. Es passiert später. Es kann scheitern. Und andere Suchmaschinen oder Crawler (Bing, Brave, Social-Media-Bots für Vorschaubilder) sind weit weniger zuverlässig.
Konsequenz für indexrelevante Inhalte: SSR oder SSG. Titel, Meta, Hauptinhalt, interne Verlinkung müssen ohne JavaScript da sein. Strukturierte Daten sowieso. Wenn Sie das nicht prüfen, indexieren Sie auf gut Glück.
Eine ehrliche Frage: Wann haben Sie zuletzt die Indexierbarkeit Ihrer Seite mit der URL-Inspektion in der Search Console kontrolliert?
Testing und Debugging
Die DevTools/Network-Panels von Chrome, Edge und Firefox zeigen alles, was Sie brauchen: Anfragen, Header, CORS-Fehler, Preflights, Cache-Treffer, Timings. Netzwerkbedingungen simulieren Sie dort mit zwei Klicks (Slow 3G, Offline). Wer Ajax-Probleme jenseits der DevTools jagt, jagt im Dunkeln.
Häufige Fehlerbilder und ihre Gegenmittel
- CORS verschlampt. Klassiker:
Access-Control-Allow-Origin: *kombiniert mit Credentials. Geht nicht. Lösung: nur die nötigen Origins explizit freigeben, bei Credentials niemals den Platzhalter. - CSRF unterschätzt. „Ist doch alles per Ajax und JSON, das ist sicher.“ Nein. Sobald Cookies im Spiel sind, brauchen Sie Tokens, SameSite und Origin-Prüfung. Parallel: XSS verhindern, sonst hilft alles andere nichts.
- Stille Nicht-Indexierung. Inhalte werden ausschließlich clientseitig geladen, sind also für Crawler nicht zuverlässig sichtbar. Lösung: SSR/SSG für alles, was indexierbar sein soll.
- Wildwestcaching. Fehlende Header, falsche Werte oder das Vertrauen in heuristisches Caching. Lösung: definierte Cache-Policy mit
Cache-Control, ETag/Last-Modified, ggf.stale-while-revalidate. - Silent Fails. Anfragen scheitern, aber der Nutzer merkt nichts. Lösung: Fehler sichtbar machen. Spinner verschwindet, Toast erscheint, Status-Live-Region meldet. Schweigen ist hier kein Service.
Best Practices (kompakt)
- Fetch als Default. XHR nur bei Legacy oder Spezialfällen mit Progress-Events.
- Sauberes Error-, Timeout- und Abbruch-Design mit
AbortController. - SOP respektieren, CORS minimal und präzise öffnen. Bei Credentials kein
*. - CSRF-Schutz bei Cookie-Auth obligatorisch. XSS-Prävention immer mitdenken.
- Explizite Cache-Header, zielgerichtete Fetch-Cache-Modi, Service Worker für PWA und Offline.
- Live-Regionen bewusst einsetzen. Visuelles und assistives Feedback synchronisieren.
- Rendering-Strategie definieren (SSR/SSG/Hydration) für alles, was indexrelevant ist.
Abgrenzung zu verwandten Technologien
Ajax ist nicht der einzige Weg, Daten zwischen Browser und Server zu schicken. Aber er ist der breiteste. Drei verwandte Verfahren, die andere Probleme lösen:
- Server-Sent Events (SSE): Einweg-Push vom Server zum Client. Sinnvoll für Newsfeeds, Benachrichtigungen, Live-Ticker. Der Client kann nicht zurücksprechen über denselben Kanal.
- WebSocket: Vollduplex-Kommunikation mit niedriger Latenz. Pflichtwerkzeug für Chats, Live-Kollaboration, Multiplayer. Ergänzt Ajax, ersetzt es nicht.
- WebTransport: Moderne API über HTTP/3/QUIC, mit zuverlässigen und unzuverlässigen Streams. Für Echtzeitfälle gebaut, kein generischer HTTP-Ersatz.
Für 80 Prozent aller Anwendungsfälle bleibt klassisches Ajax die richtige Wahl. Erst wenn die Nutzungsmuster wirklich anders sind (kontinuierlicher Datenfluss, echte Bidirektionalität), greifen Sie zu den Alternativen.
Was bleibt
Ajax hat dem Web etwas gegeben, das vorher fehlte: einen Puls.
Frameworks kommen und gehen. React, Vue, Svelte, was als Nächstes kommt. Aber das Grundmuster bleibt: Im Hintergrund passiert etwas. Im Vordergrund bleibt der Mensch, der die Seite gerade benutzt, ohne dass sie ihn unterbricht.
Wer heute Ajax baut, baut nicht mehr mit XML. Selten mit XHR. Vielleicht nicht einmal mit fetch() im Rohzustand, sondern mit einer Abstraktion darüber. Aber er baut auf demselben Gedanken, den Garrett 2005 einen Namen gab.
Eine Frage zum Mitnehmen: Welcher Teil Ihrer Anwendung lädt heute noch die ganze Seite, obwohl er es nicht müsste?