WordPress schneller machen: Dateien nur dort laden, wo sie nötig sind
Auf einer typischen WordPress-Webseite lädt der Browser CSS- und JavaScript-Dateien von Plugins, die auf genau dieser Seite nichts zu tun haben. Das Formular-Plugin liefert sein Stylesheet auch im Blogbeitrag aus, der Slider sein Skript auch im Impressum, WooCommerce fragt den Warenkorb auch auf der Startseite ab. Der Besucher lädt dabei Code herunter, den seine Seite nie ausführt.
Dieser Beitrag zeigt drei Wege, das abzustellen: Dateien im eigenen Code abmelden, Regeln über ein Plugin setzen oder die offiziellen Schalter der Anbieter nutzen. Der dritte Weg ist der beste, wird aber am seltensten genutzt, weil kaum jemand weiß, dass es ihn gibt.
Warum WordPress die Dateien aller Plugins auf jeder Seite lädt
WordPress sammelt alle Stylesheets und Skripte über den Hook wp_enqueue_scripts ein. Der läuft bei jedem Seitenaufruf, und jedes aktive Plugin darf dort anmelden, was es braucht. Ob seine Funktion auf der gerade aufgerufenen Seite überhaupt vorkommt, prüfen dabei die wenigsten.
Das hat einen nachvollziehbaren Grund. Ein Plugin kann nicht zuverlässig erkennen, wo sein Inhalt später auftaucht. Ein Shortcode steckt vielleicht in einem Widget, in einem Page-Builder-Modul, in einem Popup oder in einer Theme-Datei. Meldet das Plugin seine Dateien überall an, funktioniert es garantiert. Meldet es sie nur bedingt an, kommen Support-Anfragen von Nutzern, bei denen das Formular plötzlich unformatiert aussieht. Für den Anbieter ist die Rechnung einfach. Deine Ladezeit steht auf seiner Seite nicht in der Bilanz.
Bei drei Plugins fällt das kaum auf. Bei fünfundzwanzig kommen schnell vierzig Dateien zusammen, von denen ein Bruchteil gebraucht wird.
Was überflüssiges CSS und JavaScript wirklich kostet
CSS im head blockiert das Rendern. Der Browser zeichnet nichts, solange er nicht jede eingebundene Stilvorlage geladen und ausgewertet hat. Jedes zusätzliche Stylesheet verschiebt damit den Moment, an dem der Besucher etwas sieht, und genau den misst Google als Largest Contentful Paint in den Core Web Vitals.
JavaScript kostet an einer anderen Stelle. Die Datei muss geladen, geparst und ausgeführt werden, und das passiert im Hauptthread, also dort, wo der Browser auch auf Klicks reagiert. Ein Skript, das auf dieser Seite gar nichts bewirkt, belegt trotzdem Rechenzeit. Auf einem älteren Android-Gerät dauert das Auswerten von 200 KB JavaScript mehrere hundert Millisekunden.
Ehrlich dazugehört: Ab dem zweiten Seitenaufruf liegen die Dateien im Browser-Cache und kosten fast nichts mehr. Der Gewinn liegt beim ersten Besuch, und der erste Besuch ist der, nach dem jemand entscheidet, ob er bleibt.
Herausfinden, welche Dateien eine Seite wirklich braucht
Bevor du etwas abschaltest, brauchst du eine Liste. Der Coverage-Bereich der Chrome DevTools zeigt für jede geladene Datei an, wie viele Bytes davon auf dieser Seite ungenutzt bleiben. Dateien mit 95 Prozent ungenutztem Anteil sind die offensichtlichen Kandidaten.
Die zugehörigen Handles, also die Namen, unter denen WordPress die Dateien führt, liest du direkt aus dem System aus. Dieser Schnipsel schreibt sie ins Fehlerprotokoll:
add_action( 'wp_footer', function () {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
error_log( 'CSS: ' . implode( ', ', wp_styles()->done ) );
error_log( 'JS: ' . implode( ', ', wp_scripts()->done ) );
}, 999 );
done enthält alles, was auf dieser Seite tatsächlich ausgegeben wurde. Die Namen aus dieser Liste brauchst du für jeden weiteren Schritt.
Der Weg ohne Plugin: Dateien im Code abmelden
WordPress bringt vier Funktionen mit, um eine angemeldete Datei wieder loszuwerden: wp_dequeue_style, wp_dequeue_script, wp_deregister_style und wp_deregister_script. Die ersten beiden nehmen die Datei aus der Warteschlange, die anderen beiden löschen die Registrierung komplett.
CSS und JavaScript mit wp_dequeue_style abmelden
Ein Beispiel für Contact Form 7, dessen Handles beide contact-form-7 heißen:
add_action( 'wp_enqueue_scripts', function () {
if ( ! is_page( 'kontakt' ) ) {
wp_dequeue_style( 'contact-form-7' );
wp_dequeue_script( 'contact-form-7' );
}
}, 100 );
Statt is_page() funktioniert jede andere bedingte Abfrage: is_front_page(), is_singular( 'produkt' ), is_archive(). Steckt das Element in einem Shortcode oder Block, fragst du direkt den Inhalt ab, mit has_shortcode( $post->post_content, 'contact-form-7' ) oder has_block( 'gravityforms/form' ). Diese Variante bleibt auch dann richtig, wenn jemand das Formular später auf eine weitere Seite setzt.
Die Priorität entscheidet, ob das Abmelden der Dateien wirkt
Der häufigste Grund, warum der Code oben nichts bewirkt, ist die Reihenfolge. Plugins melden ihre Dateien auf wp_enqueue_scripts mit der Standard-Priorität 10 an. Läuft deine Funktion ebenfalls mit 10, kann sie eine Datei entfernen wollen, die noch gar nicht angemeldet ist. Die WordPress-Dokumentation zu wp_dequeue_style sagt es klar: Die Datei muss registriert sein, bevor du sie entfernst. Eine höhere Zahl bedeutet später, deshalb steht im Beispiel die 100.
Meldet ein Plugin seine Datei trotzdem wieder an, hilft wp_deregister_script(). Damit ist der Handle weg, und ein erneutes wp_enqueue_script() mit demselben Namen läuft ins Leere.
Abhängigkeiten prüfen, bevor Dateien verschwinden
Skripte und Stylesheets können voneinander abhängen. Wird eine Datei als Abhängigkeit einer anderen geführt, holt WordPress sie automatisch zurück, und dein wp_dequeue_style() sieht wirkungslos aus. Was von was abhängt, steht in der Registrierung:
print_r( wp_scripts()->registered['wc-cart-fragments']->deps );
Gefährlich wird es bei jQuery. Viele Themes und Plugins setzen es voraus. Wer es global abmeldet, bekommt eine Seite, die optisch stimmt und deren Menü sich nicht mehr öffnet.
Wohin der Code für das gezielte Laden gehört
Nicht in die functions.php des Eltern-Themes, denn das nächste Theme-Update überschreibt sie. Richtig ist ein Child-Theme oder, besser, ein eigenes kleines Plugin unter wp-content/mu-plugins/. Das läuft immer mit, überlebt jeden Theme-Wechsel und lässt sich zum Testen in einem Schritt umbenennen.
Plugins, die das Laden pro Seite steuern
Wer keinen Code schreiben will, bekommt dieselbe Steuerung über eine Oberfläche. Der Ablauf ist überall gleich: Seite aufrufen, Liste aller geladenen Dateien ansehen, einzeln abschalten.
Asset CleanUp: kostenlos und pro Seite steuerbar
Asset CleanUp: Page Speed Booster hat über 100.000 aktive Installationen und ist in der Grundversion kostenlos. Der CSS & JS Manager erscheint im Bearbeiten-Bildschirm jeder Seite und listet alle geladenen Dateien nach Quelle sortiert, also nach Plugin und Theme. Abschalten kannst du sie für die aktuelle Seite, für einen ganzen Beitragstyp oder für die gesamte Webseite mit Ausnahmen. Die kostenpflichtige Pro-Fassung ergänzt einen Plugins Manager, der ganze Plugins im Frontend deaktiviert.
Perfmatters Script Manager: Regeln pro Seite, Beitragstyp und Regex
Perfmatters ist kostenpflichtig und hat mit dem Script Manager das feinere Werkzeug. Jede Datei lässt sich abschalten für die aktuelle Adresse, für einen Beitragstyp, für Archivseiten, überall mit Ausnahmeliste oder über einen regulären Ausdruck. Letzteres ist praktisch bei Shops, wo ein Muster wie /\/(warenkorb|kasse)\// gleich mehrere Seiten trifft.
Zwei Funktionen machen den Unterschied im Alltag. Der Testmodus wendet alle Regeln nur für angemeldete Administratoren an, sodass Besucher von einem Fehlversuch nichts merken. Und der MU-Modus, bei dem eine Datei nach mu-plugins kopiert wird, greift tiefer und unterbindet auch Datenbankabfragen und eingebettetes CSS eines Plugins.
WP Rocket entfernt nicht benutztes CSS pro Seite
WP Rocket dreht den Ansatz um. Statt Dateien abzuschalten, schickt die Funktion „Remove Unused CSS” jede Adresse an einen Dienst des Anbieters, der daraus ein Stylesheet mit nur den tatsächlich benutzten Regeln erzeugt. Das Ergebnis liegt unter wp-content/cache/used-css/, die Originaldateien fallen weg. Die Erzeugung läuft in Stapeln von bis zu 100 Adressen pro Minute, maximal 300 stehen gleichzeitig in der Warteschlange. Bei einer großen Webseite dauert der erste Durchlauf also seine Zeit.
Der bekannte Haken: Klassen, die erst später per JavaScript ans Element kommen, stehen im erzeugten CSS nicht drin. Menüs, Akkordeons und Popups sehen dann im geöffneten Zustand falsch aus. Dafür gibt es eine Ausschlussliste, die man nach dem ersten Durchlauf meist füllen muss.
Ganze Plugins abschalten statt nur deren Dateien
Noch einen Schritt weiter gehen Plugin Organizer, Freesoul Deactivate Plugins, der Plugins Manager von Asset CleanUp Pro und der MU-Modus von Perfmatters. Sie laden ein Plugin auf ausgewählten Seiten gar nicht erst, sparen also zusätzlich PHP-Laufzeit und Datenbankabfragen. Der Gewinn ist größer, das Risiko auch.
Eine Falle steckt dabei im Detail: Regeln greifen anhand der aufgerufenen Adresse. Ein Formular, das per AJAX an admin-ajax.php sendet, oder ein Block, der die REST-Schnittstelle anspricht, ruft eine ganz andere Adresse auf. Dort passt deine Regel nicht mehr, und das abgeschaltete Plugin fehlt genau dann, wenn es arbeiten soll.
Hooks der Plugin-Anbieter nutzen
Alles bisher Beschriebene ist ein Eingriff von außen. Du räumst hinterher weg, was ein Plugin angemeldet hat, und musst nach jedem Update prüfen, ob die Handles noch gleich heißen. Manche Anbieter bieten stattdessen einen offiziellen Schalter an. Der ist dokumentiert, wird gepflegt und überlebt Updates.
Gravity Forms lädt seine Dateien dort, wo ein Formular steht
Gravity Forms meldet seine Dateien an, wenn es ein Formular ausgibt. Solange das Formular über Shortcode oder Block im Inhalt steckt, passt das von allein. Sitzt es außerhalb des Loops, etwa direkt in einer Theme-Datei oder in einem Popup, kommt die Ausgabe zu spät für wp_head, und die Stile fehlen. Für diesen Fall gibt es gravity_form_enqueue_scripts():
add_action( 'get_header', function () {
if ( is_page( 'kontakt' ) ) {
gravity_form_enqueue_scripts( 5, true );
}
} );
Der erste Wert ist die Formular-ID, der zweite schaltet den AJAX-Versand ein. Der Hook get_header läuft vor wp_head, und genau das verlangt die Funktion laut Dokumentation.
Daneben existiert seit Gravity Forms 2.8 der Filter gform_disable_css, der mit add_filter( 'gform_disable_css', '__return_true' ); sämtliche Formular-Stile abschaltet. Die Dokumentation von Gravity Forms warnt an derselben Stelle deutlich davor: Betroffen sind auch die Regeln, die bedingte Logik steuern, das Honeypot-Feld verstecken und mehrseitige Formulare zusammenhalten. Der Filter ist nur sinnvoll, wenn du das Formular komplett selbst gestaltest.
Contact Form 7 per Konstante abschalten und gezielt nachladen
Contact Form 7 liefert sein CSS und JavaScript auf jeder einzelnen Seite aus. Zwei Konstanten in der wp-config.php beenden das:
define( 'WPCF7_LOAD_JS', false );
define( 'WPCF7_LOAD_CSS', false );
Damit lädt Contact Form 7 nirgends mehr etwas, auch nicht auf der Kontaktseite. Zurückholen lässt sich beides über wpcf7_enqueue_scripts() und wpcf7_enqueue_styles(), aufgerufen vor wp_head:
add_action( 'get_header', function () {
if ( is_page( 'kontakt' ) ) {
wpcf7_enqueue_scripts();
wpcf7_enqueue_styles();
}
} );
WooCommerce: Dateien außerhalb des Shops abbestellen
WooCommerce bringt für seine Stylesheets einen eigenen Filter mit. Der folgende Code lässt sie nur noch auf Shop-, Warenkorb- und Kassenseiten zu:
add_filter( 'woocommerce_enqueue_styles', function ( $styles ) {
if ( ! is_woocommerce() && ! is_cart() && ! is_checkout() ) {
unset( $styles['woocommerce-general'] );
unset( $styles['woocommerce-layout'] );
unset( $styles['woocommerce-smallscreen'] );
}
return $styles;
} );
Der zweite Kandidat heißt wc-cart-fragments. Dieses Skript fragt den Warenkorb-Inhalt per AJAX nach, damit die Zahl im Header auch bei gecachten Seiten stimmt. Auf einem Blog ohne Warenkorb-Anzeige ist es überflüssig und kostet pro Seitenaufruf eine zusätzliche Anfrage. Meldest du es ab, aktualisiert sich der Zähler im Header erst beim nächsten Seitenaufruf. Prüfe vorher, ob dein Theme ihn überhaupt anzeigt.
WordPress selbst: Block-CSS nur für benutzte Blöcke
Auch der Core betrifft dieses Thema. Seit WordPress 5.8 gibt es den Filter should_load_separate_core_block_assets. Steht er auf true, lädt WordPress die Stile eines Blocks nur dann, wenn der Block auf der Seite vorkommt. Bei Block-Themes ist das die Voreinstellung, klassische Themes laden weiterhin eine große Sammeldatei. Umschalten lässt sich das mit einer Zeile:
add_filter( 'should_load_separate_core_block_assets', '__return_true' );
Das Ergebnis sind mehrere kleine Dateien statt einer großen. Über HTTP/2 ist das meist der bessere Tausch, nachmessen lohnt sich trotzdem.
Typische Fehler beim gezielten Laden
- Nur als Administrator getestet. Angemeldete Nutzer sehen oft andere Skripte und umgehen den Cache. Jeder Test gehört in ein privates Fenster.
- Den Cache vergessen. Nach jeder Änderung Seiten-Cache und, falls vorhanden, Objekt-Cache leeren. Sonst prüfst du altes HTML.
- AJAX und REST übersehen. Formularversand, Warenkorb und Filter laufen über andere Adressen als die Seite, auf der sie stehen.
- Stille Fehler übersehen. Ein Formular ohne sein Skript sieht normal aus und sendet nicht ab. Jede abgeschaltete Funktion einmal von Hand durchklicken.
- Direkt im Live-Betrieb gearbeitet. Solche Änderungen gehören zuerst auf eine Staging-Umgebung.
Checkliste
- Mit dem Coverage-Bereich der Chrome DevTools messen, welche Dateien ungenutzt bleiben
- Handles über
wp_styles()->doneundwp_scripts()->doneauslesen - Zuerst prüfen, ob das Plugin einen eigenen Hook oder Schalter anbietet
- Erst danach
wp_dequeue_styleundwp_dequeue_scripteinsetzen, mit Priorität 100 - Code ins Child-Theme oder in ein eigenes MU-Plugin, nie in die
functions.phpdes Eltern-Themes - Abhängigkeiten prüfen, bevor ein Skript verschwindet
- Nach jeder Änderung als ausgeloggter Besucher testen, mit geleertem Cache
- Vorher und nachher messen, sonst bleibt der Gewinn eine Behauptung
Fazit: weniger Dateien pro Seite, kürzere Ladezeit
Das Aufräumen der geladenen Dateien ist einer der wenigen Performance-Schritte, die nichts kosten und nichts am Aussehen ändern. Bei Webseiten mit vielen Plugins bringt er mehr als jede weitere Cache-Einstellung, weil er Arbeit vermeidet, statt sie zu beschleunigen.
Am längsten hält die Lösung über die Hooks der Anbieter, weil sie dokumentiert ist und Updates übersteht. Ein Plugin wie Asset CleanUp ist der schnellere Einstieg und für die meisten Webseiten ausreichend. Wer den Rest herausholen will, kümmert sich danach um die Bilder, denn die machen meist noch immer den größeren Teil des Datenvolumens aus.
Den schnellen Server mit NVMe-Speicher und die Staging-Umgebung zum Ausprobieren bringen wir mit. Welche Dateien deine Seiten wirklich brauchen, entscheidest du.
Siehe auch
Häufige Fragen
Was bringt es, CSS und JavaScript nur auf einzelnen Seiten zu laden?
Der Browser lädt weniger Bytes, blockiert das Rendern kürzer und muss weniger JavaScript auswerten. Besonders stark wirkt das auf Seiten ohne Formular, Slider oder Shop-Funktion, weil dort oft der größte Teil des geladenen Codes ungenutzt bleibt. Wie viel es konkret ist, zeigt der Coverage-Bereich der Chrome DevTools pro Datei in Bytes an.
Welches Plugin steuert, wo CSS und JavaScript geladen werden?
Verbreitet sind Asset CleanUp: Page Speed Booster (kostenlos, über 100.000 aktive Installationen) und Perfmatters mit seinem Script Manager (kostenpflichtig). Beide zeigen pro Seite alle geladenen Dateien und schalten sie einzeln ab, Perfmatters zusätzlich nach Beitragstyp und über reguläre Ausdrücke. WP Rocket geht einen anderen Weg und erzeugt pro Adresse ein Stylesheet aus nur dem benutzten CSS.
Bietet Gravity Forms einen Hook, um die Dateien gezielt zu laden?
Ja. Gravity Forms lädt seine Dateien dort, wo es ein Formular ausgibt. Steckt das Formular außerhalb des Loops, etwa in einem Popup oder direkt in der Theme-Datei, lädst du sie mit gravity_form_enqueue_scripts( $form_id, $is_ajax ) nach. Der Aufruf muss vor wp_head laufen, üblicherweise am Hook get_header. Zusätzlich gibt es seit Gravity Forms 2.8 den Filter gform_disable_css, der alle Formular-Stile abschaltet.
Wie lädt Contact Form 7 seine Dateien nur auf der Kontaktseite?
Contact Form 7 liest zwei Konstanten aus der wp-config.php: WPCF7_LOAD_JS und WPCF7_LOAD_CSS. Auf false gesetzt, liefert das Plugin auf der ganzen Webseite nichts mehr aus. Auf den Seiten mit Formular holst du die Dateien mit wpcf7_enqueue_scripts() und wpcf7_enqueue_styles() zurück, ebenfalls vor wp_head.
Kann das Abschalten von Dateien die Webseite kaputt machen?
Ja, und zwar oft unbemerkt. Ein Formular sieht dann normal aus, sendet aber nicht mehr ab, oder der Warenkorb zählt nicht mehr hoch. Deshalb gehört jede Änderung zuerst auf eine Staging-Kopie, danach ein Test als ausgeloggter Besucher mit geleertem Cache. Perfmatters bringt dafür einen Testmodus mit, der die Regeln nur für angemeldete Administratoren anwendet.