Skip to content

Alles für WordPress, Webentwicklung — und mehr

⚡ So fügen Sie defer und async für WordPress-Skripte in function.php hinzu

⚡ So fügen Sie defer und async für WordPress-Skripte in function.php hinzu

Seiten laden langsam, Google PageSpeed Insights zeigt orangefarbene Warnungen, und der Kunde fragt: „Warum ist die Seite langsam?" In neun von zehn Fällen ist die Ursache JavaScript, das das Rendering blockiert. Der Browser stößt auf ein <script>, stoppt den DOM-Aufbau, lädt das Skript, führt es aus und macht erst dann weiter. Bei einer modernen Website mit einem Dutzend Plugins summiert sich diese Verzögerung auf Sekunden.

WordPress bot lange Zeit keinen standardisierten Weg, das Laden von Skripten zu steuern. Entwickler behalfen sich mit Workarounds: Filtern von script_loader_tag, Manipulation der Ausgabe über clean_url oder sogar dem Schreiben eigener Walker für WP_Scripts. Mit dem Release von WordPress 6.3 hat sich die Situation grundlegend geändert, und wir haben jetzt einen sauberen, unterstützten Weg, defer oder async zu jedem Skript hinzuzufügen, ganz ohne Hacks.

Nachfolgend zwei funktionierende Methoden: der moderne, native Ansatz (WP 6.3+) und der bewährte script_loader_tag-Filter (WP 4.1+). Beide wurden an realen Projekten getestet, beide bewahren die Integrität der Abhängigkeitswarteschlange.

💡 Kurzübersicht:

  • Verstehen Sie den Unterschied zwischen defer und async und wann Sie welches Attribut einsetzen, das entscheidet darüber, ob nach der Optimierung Funktionen kaputtgehen
  • Nutzen Sie die native WordPress-6.3-Methode über wp_enqueue_script() mit dem Parameter strategy, der sauberste Ansatz, der die Ausführungsreihenfolge bewahrt
  • Läuft die Seite unter einer Version vor 6.3, setzen Sie den script_loader_tag-Filter mit einem Array von Handles ein, das funktioniert ab WordPress 4.1
  • Bei mehreren Skripten sammeln Sie die Handles in einem Array und durchlaufen es mit einer Schleife, ein Filter für alle Skripte statt Copy-and-paste

Was defer und async sind und wann man sie einsetzt

Trifft ein Browser auf ein gewöhnliches <script>-Tag, tut er drei Dinge nacheinander: Er stoppt das HTML-Parsing, lädt das Skript und führt es aus. Erst danach kehrt er zum HTML zurück. Auf einer Seite mit fünf Skripten im <head> bedeutet das: Der Nutzer sieht einen weißen Bildschirm, während das letzte Kommentar-Plugin lädt, obwohl der Beitrag selbst schon längst hätte gerendert werden können.

Die Attribute defer und async lösen dieses Problem, funktionieren aber unterschiedlich:

Attribut

Wann es lädt

Wann es ausführt

Ausführungsreihenfolge

(keins)

Blockiert Parsing sofort

Sofort nach dem Laden

In DOM-Reihenfolge

defer

Parallel zum Parsing

Nach vollständigem DOM-Aufbau

In DOM-Reihenfolge

async

Parallel zum Parsing

Sofort nach dem Laden

Wer zuerst fertig lädt

Defer ist das Arbeitspferd für die meisten Szenarien. Das Skript lädt parallel zum HTML und wird erst ausgeführt, wenn das DOM vollständig aufgebaut ist. Die Reihenfolge bleibt erhalten: Skript A wird vor Skript B ausgeführt, selbst wenn B schneller geladen hat. Das ist entscheidend für jQuery und alles, was davon abhängt.

Async ist ein Werkzeug für unabhängige Skripte. Analytics, Werbung, Social-Media-Widgets: Sie brauchen das DOM nicht, ihnen ist die Reihenfolge egal, sie müssen nur so schnell wie möglich laufen. Setzen Sie jedoch async bei einem Skript, das von jQuery abhängt, erhalten Sie mit hoher Wahrscheinlichkeit $ is not defined.

Einfache Regel: Skript hängt von anderen Skripten oder vom DOM ab → defer. Skript ist vollständig autonom → async. Beginnen Sie im Zweifelsfall immer mit defer.

Methode 1: Nativer WordPress-6.3-Ansatz

Seit Juli 2023 funktioniert ein neuer Mechanismus im WordPress-Core. Die Funktionen wp_register_script() und wp_enqueue_script() haben einen überladenen fünften Parameter $args erhalten, ein Array, in dem Sie die Ladestrategie angeben können. Keine Filter, keine String-Manipulation, kein Risiko, die Abhängigkeitsreihenfolge zu zerstören.

Grundlegende Syntax für defer:

1wp_enqueue_script(
2 'my-js-handle',
3 get_template_directory_uri() . '/js/my-script.js',
4 array('jquery'),
5 '1.0.0',
6 array(
7 'strategy' => 'defer',
8 'in_footer' => true,
9 )
10);

Für async, gleiche Mechanik:

1wp_enqueue_script(
2 'google-analytics',
3 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX',
4 array(),
5 '1.0.0',
6 array(
7 'strategy' => 'async',
8 'in_footer' => false,
9 )
10);

Der Schlüssel in_footer innerhalb des Arrays funktioniert wie der alte boolesche Parameter: true setzt das Skript in den Footer, false in den <head>. Für defer setzen Sie typischerweise true (das Skript wartet ohnehin auf das DOM, es gibt keinen Grund, es früh zu laden), für async das, was passt.

Der Hauptvorteil der nativen Methode: Der Core selbst prüft den Abhängigkeitsbaum. Hängt Skript A mit defer von Skript B ab und B ist ohne Strategie (blockierend) registriert, zerstört WordPress Ihre Seite nicht: Es stuft die Strategie von Skript A automatisch auf blockierend herab. Bei Nutzung von script_loader_tag fehlt Ihnen dieser Schutz, der Filter fügt das Attribut einfach ein, ohne auf Abhängigkeiten zu achten.

Wichtig: Das $args-Array erschien in WordPress 6.3. Muss ein Theme oder Plugin mit niedrigeren Versionen funktionieren, nutzen Sie Methode 2 oder fügen eine Prüfung ein:

1if ( version_compare( $GLOBALS['wp_version'], '6.3', '>=' ) ) {
2 // native method
3} else {
4 // script_loader_tag filter
5}

Methode 2: script_loader_tag-Filter (WordPress 4.1+)

Läuft die Seite unter einer Version vor 6.3 oder müssen Sie Abwärtskompatibilität wahren, setzen Sie den bewährten script_loader_tag-Filter ein. Er existiert seit WordPress 4.1 und funktioniert weiterhin einwandfrei.

Der Filter feuert unmittelbar bevor das <script>-Tag im HTML ausgegeben wird. Sie erhalten den fertigen Tag-String, den Skript-Handle und den Dateipfad und können src durch defer="defer" src oder async="async" src ersetzen.

Einzelnes Skript mit defer:

1function add_defer_to_my_script($tag, $handle) {
2 if ( 'my-js-handle' !== $handle ) {
3 return $tag;
4 }
5 return str_replace( ' src', ' defer="defer" src', $tag );
6}
7add_filter('script_loader_tag', 'add_defer_to_my_script', 10, 2);

Der Code kommt in die functions.php des aktiven Themes oder, korrekter, in ein separates Snippet-Plugin wie Code Snippets oder WPCode. Platzieren Sie ihn in der functions.php eines Child-Themes, werden die Skripte beim Theme-Wechsel wieder blockierend, und Sie bemerken es nicht sofort.

Der Skript-Handle ist der erste Parameter, den Sie an wp_register_script() oder wp_enqueue_script() übergeben haben. Genau das steht in der if-Bedingung. Raten Sie den Handle nicht, öffnen Sie den Quellcode des Plugins oder Themes und suchen Sie den wp_enqueue_script-Aufruf.

Defer und async für mehrere Skripte

Einen Filter pro Skript hinzuzufügen ist ein Weg zu aufgeblähter functions.php und Copy-and-paste-Fehlern. Die richtige Lösung: ein Array mit Handles und ein Filter mit einer Schleife.

1function add_defer_to_scripts($tag, $handle) {
2 $scripts_to_defer = array(
3 'my-js-handle',
4 'another-handle',
5 'third-party-lib',
6 );
7
8 foreach ( $scripts_to_defer as $defer_script ) {
9 if ( $defer_script === $handle ) {
10 return str_replace( ' src', ' defer="defer" src', $tag );
11 }
12 }
13 return $tag;
14}
15add_filter('script_loader_tag', 'add_defer_to_scripts', 10, 2);

Für async ändern sich nur das Attribut und der Array-Name:

1function add_async_to_scripts($tag, $handle) {
2 $scripts_to_async = array(
3 'google-tag-manager',
4 'facebook-pixel',
5 'hotjar',
6 );
7
8 foreach ( $scripts_to_async as $async_script ) {
9 if ( $async_script === $handle ) {
10 return str_replace( ' src', ' async="async" src', $tag );
11 }
12 }
13 return $tag;
14}
15add_filter('script_loader_tag', 'add_async_to_scripts', 10, 2);

Beide Filter können gleichzeitig eingehängt werden, defer für Ihre Skripte, async für Drittanbieter-Tracker. Sie arbeiten unabhängig voneinander und kommen sich nicht in die Quere.

Praxisbeispiel: Google Maps API

Google Maps ist ein klassischer Kandidat für defer. Die Karte steht typischerweise im Footer der Kontaktseite, das Skript zieht über 100 KB, und der Nutzer braucht die Karte nicht sofort. Zudem hängt die API selbst nicht von anderen Seitenskripten ab, ein Idealfall.

Einbinden und mit defer versehen:

1// theme's functions.php
2function enqueue_google_maps() {
3 if ( ! is_page('contacts') ) {
4 return;
5 }
6
7 wp_enqueue_script(
8 'google-maps-api',
9 'https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY',
10 array(),
11 null,
12 array(
13 'strategy' => 'defer',
14 'in_footer' => true,
15 )
16 );
17}
18add_action('wp_enqueue_scripts', 'enqueue_google_maps');

Gleiches Ergebnis über script_loader_tag:

1function add_defer_to_google_maps($tag, $handle) {
2 if ( 'google-maps-api' !== $handle ) {
3 return $tag;
4 }
5 return str_replace( ' src', ' defer="defer" src', $tag );
6}
7add_filter('script_loader_tag', 'add_defer_to_google_maps', 10, 2);

Prüfen Sie nach dem Einbau beider Varianten unbedingt die Karte auf der Kontaktseite. Öffnen Sie die Browser-Konsole (F12), stellen Sie sicher, dass keine JavaScript-Fehler auftreten und die Karte korrekt gerendert wurde. Erhalten Sie einen Fehler wie initMap is not a function, bedeutet das, dass Ihr Initialisierungsskript ebenfalls als defer markiert und strikt nach der API-Einbindung platziert werden muss.

So prüfen Sie, ob defer und async funktionieren

Nach der Implementierung kommt die Verifikation. Ohne sie wissen Sie nicht, ob die Optimierung gewirkt hat oder nur toter Code ist.

Öffnen Sie den Seitenquelltext (Strg+U) und suchen Sie Ihre Skripte. Das <script>-Tag sollte die Attribute enthalten:

1<script defer="defer" src="/wp-content/themes/my-theme/js/my-script.js"></script>

Fehlen die Attribute, prüfen Sie, ob der Handle im Filter mit dem tatsächlichen Skript-Handle übereinstimmt. Häufiger Fehler: In wp_enqueue_script lautet der Handle my-plugin-frontend, im Filter aber my_plugin_frontend. Bindestrich versus Unterstrich, und der Filter überspringt das Skript stillschweigend.

Letzter Schliff: Google PageSpeed Insights oder Lighthouse im Tab „Audits" der Entwicklertools. Der Abschnitt „Render-blockierende Ressourcen eliminieren" sollte eine Verbesserung zeigen. Der konkrete Gewinn hängt von Anzahl und Größe der Skripte ab, aber für eine typische WordPress-Seite mit 5-7 Plugins ist eine Reduzierung des blockierenden JavaScripts um 40-60% ein erreichbares Ergebnis.

⁉️🤔 Häufig gestellte Fragen

Kann ich defer und async gleichzeitig für ein Skript verwenden?

Nein. Geben Sie beide Attribute gleichzeitig an, ignoriert der Browser defer und führt das Skript als async aus. Dieses Verhalten ist in der HTML-Spezifikation festgelegt, async hat immer Vorrang. Entscheiden Sie sich für eines, je nachdem, ob die Ausführungsreihenfolge relevant ist.

Was tun, wenn ein Skript nach dem Hinzufügen von defer nicht mehr funktioniert?

Höchstwahrscheinlich erwartet das Skript, dass das DOM noch nicht aufgebaut ist, und versucht, Elemente zu manipulieren, die zum Ausführungszeitpunkt nicht existieren. Ersetzen Sie defer für dieses spezifische Skript durch das standardmäßige blockierende Laden. Oder wrappen Sie den Skript-Code in DOMContentLoaded, dann kann es fehlerfrei mit defer arbeiten. Die zweite Option ist vorzuziehen: Sie behalten die Optimierung und beheben die Kompatibilität.

Was ist der Unterschied zwischen defer und dem Verschieben des Skripts in den Footer per wp_enqueue_script mit $in_footer = true?

$in_footer = true verschiebt das <script>-Tag lediglich vom <head> ans Ende des <body>. Das Skript blockiert das Rendering weiterhin, nur eben später. defer lädt parallel zum HTML-Parsing und wird strikt nach dem DOM-Aufbau ausgeführt. Die kombinierte Nutzung (in_footer => true + strategy => 'defer') bringt den maximalen Effekt: Das Skript im Footer verzögert den ersten Render nicht, und defer garantiert, dass es auch das finale Rendering nicht blockiert.

Sollte ich WordPress nur wegen der nativen Methode auf 6.3 aktualisieren?

Läuft die Seite auf Version 6.2 oder älter, lohnt sich ein Update nicht nur wegen strategy. WordPress 6.3 hat Dutzende Sicherheitslücken geschlossen und Performance-Verbesserungen im Core gebracht. Ist ein Update jedoch aus irgendeinem Grund unmöglich, funktioniert der script_loader_tag-Filter absolut zuverlässig ab Version 4.1, veröffentlicht 2014. Sie verlieren nichts, wenn Sie ihn nutzen.

Was ist mit jQuery, defer oder so lassen?

jQuery sollte mit defer laden, wenn alle abhängigen Skripte ebenfalls mit defer markiert sind. Das Problem: WordPress-Plugins verwalten Attribute für ihre Skripte äußerst selten. Setzen Sie defer auf jQuery, während ein Kontaktformular-Plugin sein Skript ohne Attribute einbindet, führt der Browser das Plugin vor jQuery aus und das Formular geht kaputt. Praktischer Rat: Beginnen Sie mit defer für Ihre eigenen Theme-Skripte. Fassen Sie jQuery nicht an, bevor Sie nicht jedes Plugin auf der Seite getestet haben.

Was 2026 auf eine Produktivseite gehört

Läuft der Server mit WordPress 6.3 oder neuer, nur die native Methode. Sauberer Code, Schutz vor Abhängigkeitskonflikten, Core-Support. Beginnen Sie mit defer für alle Theme-Skripte und geschäftskritische Plugins; reservieren Sie async für Analytics und Drittanbieter-Widgets.

Ist die Version unter 6.3, der script_loader_tag-Filter mit einem Array von Handles. Er funktioniert seit einem Jahrzehnt, da geht nichts kaputt. Das Einzige, was er nicht kann, ist die automatische Prüfung des Abhängigkeitsbaums, fügen Sie Skripte also einzeln zum Array hinzu und prüfen Sie die Seite nach jedem Schritt.

Und das Wichtigste: Keine Methode ersetzt das Audit der Skripte selbst. Verbindet ein Galerie-Plugin 15 Dateien, nur um drei Bilder anzuzeigen, helfen weder defer noch async radikal. Ladeoptimierung beginnt mit der Frage: „Wird dieses Skript überhaupt gebraucht?", und erst dann: „Wie lade ich es?"

🔗 Offizielle WordPress-6.3-Dokumentation, Script Loading Strategies