WP-CLI: WordPress Kommentare massenhaft schließen – schnell, sauber, ohne Plugin über die WP-CLI

Ausgangslage (Story)

Du hast eine WordPress-Seite, die seit Jahren Kommentare erlaubt. Anfangs war das nett, inzwischen wirst du zugespammt: Bots kippen täglich Müll in alte Beiträge, die Kommentar-Moderation verstopft, Mailbenachrichtigungen nerven, und irgendwo hängen sogar noch pingbacks/trackbacks dran. Du willst das jetzt abstellen — ohne erst wieder ein Plugin zu installieren, das nur die UI versteckt, aber nichts wirklich ändert.

Die Lösung: WP-CLI. Damit kannst du bestehende Beiträge/Seiten in Massen auf „Kommentare geschlossen“ setzen, Pings deaktivieren und zukünftige Kommentare global unterbinden — mit ein paar Befehlen. Und ja: Das geht schneller, sicherer und rückverfolgbarer als per Klickorgie im Backend.

Ziele und Entscheidungsbaum

Was du erreichen willst:

  1. Keine neuen Kommentare auf bestehenden Inhalten.
  2. Keine Pings/Trackbacks mehr.
  3. Zukünftig sollen neue Inhalte standardmäßig keine Kommentare erlauben.
  4. Optional: UI/Template für Kommentare verstecken, damit auch kein Formular erscheint (selbst wenn mal etwas offen bliebe).
  5. Optional: Spam aufräumen (bestehende Spam-/wartende Kommentare löschen).
  6. Optional: Multisite-weit anwenden.

Minimal-Variante (schnell):

  • Alle Posts/Pages schließen (comment_status/ping_status = closed).
  • Default-Optionen ändern (keine neuen Kommentare/Pings).
  • Fertig.

Harte Variante (zusätzlich):

  • Kommentar-UI per Filter killen, damit auch im Theme nichts mehr auftaucht.
  • Spam und Pending-Kommentare löschen.
  • Anhänge und ggf. Custom Post Types berücksichtigen.

Voraussetzungen & Sicherheit

  • SSH-Zugang zum Server.
  • WP-CLI installiert (wp --info zeigt Version und Pfad).
  • Du läufst Befehle am besten als derselbe Nutzer wie PHP/der Webserver, z. B.: sudo -u www-data -i (Ubuntu/Debian; auf anderen Setups entsprechend anpassen.)
  • Pfad ins WordPress-Root, wo wp-config.php liegt.
  • Backup: Vor Massenupdates ein Datenbank-Backup ziehen. Ja, auch wenn’s „nur Kommentar-Flags“ sind. DB-Dump dauert 10–30 Sekunden: wp db export backup-before-closing-comments.sql
  • Staging: Wenn du sensible Umgebung hast, teste auf Staging. Wenn nicht: Backup, dann los.

WP-CLI Basics für diesen Job

  • IDs statt Tabellen: Wir arbeiten auf wp_posts.comment_status und ping_status. WP-CLI nimmt uns die SQL-Arbeit ab.
  • Listen → Aktualisieren: wp post list liefert IDs, wp post update setzt Felder.
  • Batching: Viele IDs pipen wir in xargs, z. B. -n50 (50 IDs pro Aufruf).
  • Filter:
    • --post_type=post,page (ergänze eigene CPTs).
    • --post_status=publish,private,future,draft (was immer du abdecken willst).
    • --date_query= oder --format=ids für gezielte Selektionen.

Schritt 1: Bestehende Beiträge & Seiten schließen (Kommentare & Pings)

1.1 Posts und Pages (alle Status)

# Beiträge & Seiten: Kommentare + Pings schließen
wp post list --post_type=post,page --post_status=publish,private,future,draft --format=ids \
| xargs -n50 wp post update --comment_status=closed --ping_status=closed

Erläuterung:

  • --format=ids gibt nur IDs aus → ideal für Pipes.
  • Wir schließen Kommentare und Pings gleichzeitig. Pings sind Trackbacks/Pingbacks — alt, aber für Spam immer gut.

1.2 Nur veröffentlichte Beiträge

wp post list --post_type=post,page --post_status=publish --format=ids \
| xargs -n50 wp post update --comment_status=closed --ping_status=closed

1.3 Anhänge (Medien)

Viele Installationen haben „Kommentar offen“ auf Anhängen (!) – die sind eigene Post-Typen (attachment). Schließ die mit:

wp post list --post_type=attachment --format=ids \
| xargs -n50 wp post update --comment_status=closed --ping_status=closed

Warum? Anhangsseiten werden von Bots gern missbraucht, weil Themes dort oft das Kommentarformular nicht ausblenden.

1.4 Custom Post Types (CPT)

Wenn du CPTs hast (z. B. product, portfolio), entscheide bewusst. Shop-Reviews? Vielleicht nicht abschalten.

# Beispiel: Portfolio und Events zusätzlich schließen
wp post list --post_type=portfolio,event --format=ids \
| xargs -n50 wp post update --comment_status=closed --ping_status=closed

1.5 Nur alte Beiträge schließen (optional)

Wenn du nur ältere Posts schließen willst (z. B. alles älter als 90 Tage):

wp post list --post_type=post --date_query=before=90days ago --format=ids \
| xargs -n50 wp post update --comment_status=closed --ping_status=closed

1.6 Ergebnis prüfen

# Wie viele Posts sind noch offen?
wp post list --post_type=post,page --meta_key=comment_status --format=count  # (Hinweis: comment_status ist kein Meta)

Korrekt prüfen:
WP-CLI listet comment_status direkt nicht im Standard aus. Nimm --fields:

wp post list --post_type=post --fields=ID,post_status,comment_status --format=table | head -n 20

Oder gezielt „offene“ zählen:

wp db query "SELECT COUNT(*) FROM wp_posts WHERE post_type IN ('post','page') AND comment_status='open';"

Passe wp_ an dein Tabellenpräfix an.


Schritt 2: Für die Zukunft deaktivieren (Defaults setzen)

Damit neue Inhalte standardmäßig keine Kommentare/Pings mehr erlauben:

# Neue Beiträge: Kommentare standardmäßig geschlossen
wp option update default_comment_status closed

# Neue Beiträge: Pings standardmäßig geschlossen
wp option update default_ping_status closed

Wichtig:

  • Das wirkt nur auf zukünftig erstellte Inhalte.
  • Für bereits existierende Inhalte brauchst du Schritt 1.

Optional kannst du alte Beiträge zusätzlich auto-schließen lassen:

# Auto-Schließen alter Beiträge aktivieren (1 = an)
wp option update close_comments_for_old_posts 1

# Nach wie vielen Tagen schließen? (z. B. 1 Tag – nimmt praktisch alles aus der Schusslinie)
wp option update close_comments_days_old 1

Das ist ein „Sicherheitsnetz“. Ich empfehle trotzdem die einmalige Massenschließung per Schritt 1.


Schritt 3: Kommentar-UI hart deaktivieren (Theme/Hook-Ebene, optional)

Wenn du sichergehen willst, dass nirgends ein Kommentarformular mehr auftaucht — auch nicht durch Theme-Fehler —, setz Filter. Kein Plugin nötig: Du kannst das als MU-Snippet (must-use) oder ins Theme packen. Minimal-Variante:

<?php
// Datei: wp-content/mu-plugins/disable-comments-hard.php (anlegen)
add_action('init', function () {
    // Kommentare & Trackbacks für alle Post Types aus der UI entfernen
    foreach (get_post_types() as $pt) {
        remove_post_type_support($pt, 'comments');
        remove_post_type_support($pt, 'trackbacks');
    }
});

add_filter('comments_open', '__return_false', 20, 2);
add_filter('pings_open', '__return_false', 20, 2);
add_filter('comments_array', '__return_empty_array', 10, 2);

Effekt:

  • Kommentarformular erscheint nicht mehr.
  • Bestehende Kommentare werden nicht angezeigt.
  • Aber: Das ändert comment_status in der DB nicht. Deshalb zuerst Schritt 1 ausführen, dann dieses Hardening.

Wenn du „ohne Plugin“ sehr wörtlich nimmst: MU-Plugins sind technisch Plugins, aber ohne Admin-UI, schnell, wartungsarm. Alternativ kannst du das in functions.php des Child-Themes einfügen.


Schritt 4: Spam und Pending-Kommentare aufräumen (optional, aber sinnvoll)

Wenn du zugespammt wurdest, räum die Leiche(n) weg:

4.1 Spam löschen

# Alle Spam-Kommentare löschen
wp comment delete $(wp comment list --status=spam --format=ids) --force

4.2 Ausstehende Kommentare löschen (wenn du wirklich alles dicht machen willst)

# Alle 'hold' (moderation) Kommentare löschen
wp comment delete $(wp comment list --status=hold --format=ids) --force

4.3 Papierkorb leeren (falls vorhanden)

wp comment delete $(wp comment list --status=trash --format=ids) --force

Hinweis: --force überspringt den Papierkorb und löscht endgültig. Wenn du vorsichtiger sein willst, lass --force weg.


Schritt 5: Caches und Nebeneffekte

  • Object Cache (falls aktiv, z. B. Redis): wp cache flush
  • Page-Cache (NGINX, Varnish, Plugin-Cache): musst du am jeweiligen Layer leeren. WP-CLI kann das nicht generisch.

Multisite (wenn du ein Netzwerk hast)

Du willst netzwerkweit alles dicht machen. So gehst du vor:

6.1 Netzwerkeigene „Defaults“ setzen (global ist in Multisite tricky)

WordPress speichert Optionen pro Site/Blog. Du musst also pro Site arbeiten. Nutze --url=.

6.2 Alle Sites iterieren

# Alle Sites-URLs holen
wp site list --field=url \
| while read -r url; do
    echo ">>> Bearbeite $url"

    # Bestehende Inhalte schließen
    wp --url="$url" post list --post_type=post,page --post_status=publish,private,future,draft --format=ids \
    | xargs -n50 wp --url="$url" post update --comment_status=closed --ping_status=closed

    # Anhänge schließen
    wp --url="$url" post list --post_type=attachment --format=ids \
    | xargs -n50 wp --url="$url" post update --comment_status=closed --ping_status=closed

    # Defaults für neue Inhalte
    wp --url="$url" option update default_comment_status closed
    wp --url="$url" option update default_ping_status closed
done

Tipp: Bei sehr großen Netzwerken xargs -n200 und ggf. -P4 (parallel) nutzen — aber nur, wenn du weißt, was du tust und dein DB-Server das abkann.


Verifikation & Reporting

Vorher/Nachher prüfen ist Pflicht, sonst weißt du nicht, ob alles zu ist.

7.1 Offene Kommentare nach Operation

# Posts/Pages, die noch 'open' haben
wp db query "SELECT post_type, COUNT(*) AS cnt 
             FROM wp_posts 
             WHERE post_type IN ('post','page') AND comment_status='open'
             GROUP BY post_type;"

7.2 Stichprobe anzeigen

wp db query "SELECT ID, post_title, post_type, comment_status, ping_status 
             FROM wp_posts 
             WHERE comment_status='open' 
             ORDER BY post_date DESC 
             LIMIT 20;"

7.3 Kommentaranzahl / Spamrest

wp comment list --status=spam --format=count
wp comment list --status=hold --format=count

Edge Cases & Entscheidungen

8.1 WooCommerce-Produkte (Reviews)

product ist ein CPT. Produktbewertungen nutzen das Kommentarsystem. Wenn du Reviews behalten willst:

  • Nicht product schließen.
  • Oder: Nur Kommentare schließen, Pings egal: wp post list --post_type=product --format=ids | xargs -n50 wp post update --ping_status=closed

8.2 Foren/Community

Wenn du bbPress/BuddyPress nutzt: Diese Systeme handeln Kommentare/Antworten oft anders. Nicht blind drüberbügeln.

8.3 API/Headless

Wenn du headless unterwegs bist: Kommentare offen/zu kann auch über API-Gateways beeinflusst werden. Stelle sicher, dass dein Frontend kein Formular mehr rendert.

8.4 Altlasten im Theme

Manche Themes ignorieren comments_open() falsch oder rendern Formulare hart. Deshalb der Hook-Killer aus Schritt 3.

8.5 Pings/Trackbacks

Alt, nervig, nutzlos: Immer schließen. Historische Pingbacks bleiben als Kommentare bestehen, aber neue kommen nicht rein.

8.6 Medien-Anhangseiten

Viele vergessen attachment. Bots nicht. Immer mit schließen.

8.7 Rollen & Rechte

WP-CLI setzt direkt in der DB. Redakteure können im Backend pro Post wieder öffnen — falls du die UI nicht rausnimmst. Wenn du Ruhe willst, nimm die Filter aus Schritt 3.


Performance-Tipps für große Sites

  • Batchgröße: xargs -n50 ist konservativ. 100–200 sind meist auch ok.
  • Parallelisierung: wp post list --post_type=post,page --format=ids \ | xargs -n100 -P4 wp post update --comment_status=closed --ping_status=closed -P4 = bis zu 4 Prozesse parallel. Vorsicht bei Shared-DBs.
  • Direkt-SQL (nur wenn du weißt, was du tust): UPDATE wp_posts SET comment_status='closed', ping_status='closed' WHERE post_type IN ('post','page','attachment'); Danach Cache flushen. Vorteil: ultraschnell. Nachteil: kein Audit über WP-CLI.

Häufige Fehlannahmen (kurz und schmerzlos)

  • „Einstellungen → Diskussion“ reicht rückwirkend.
    Falsch. Das ändert nur Defaults für neue Posts. Bestehende bleiben offen.
  • „Disable Comments“ (Plugin) löst alles.
    Jein. Das Plugin versteckt und filtert — ändert aber nicht zwingend den DB-Status. Ich will saubere Daten.
  • „Wenn ich das Formular im Theme verstecke, kommen keine Kommentare rein.“
    Falsch. Bots posten über wp-comments-post.php und API. Schließe serverseitig (comment_status='closed').
  • „Pings sind harmless.“
    Nein. Trackbacks/Pingbacks sind Spam-Magnete. Schließen.

Komplettrezept zum Copy-&-Paste

Wenn du nur „mach dicht“ willst (Posts, Pages, Attachments; Defaults setzen; Spam putzen):

# 0) Backup
wp db export backup-before-closing-comments.sql

# 1) Bestehende Inhalte schließen
wp post list --post_type=post,page --post_status=publish,private,future,draft --format=ids \
| xargs -n100 wp post update --comment_status=closed --ping_status=closed

wp post list --post_type=attachment --format=ids \
| xargs -n100 wp post update --comment_status=closed --ping_status=closed

# 2) Zukünftig: standardmäßig keine Kommentare/Pings
wp option update default_comment_status closed
wp option update default_ping_status closed

# Optional: Netz für alte Posts
wp option update close_comments_for_old_posts 1
wp option update close_comments_days_old 1

# 3) Spam/Pending löschen
ids_spam=$(wp comment list --status=spam --format=ids);    [ -n "$ids_spam" ] && wp comment delete $ids_spam --force
ids_hold=$(wp comment list --status=hold --format=ids);    [ -n "$ids_hold" ] && wp comment delete $ids_hold --force
ids_trash=$(wp comment list --status=trash --format=ids);  [ -n "$ids_trash" ] && wp comment delete $ids_trash --force

# 4) Cache leeren
wp cache flush

Wenn du’s hart willst (inkl. UI-Abschaltung), ergänze die MU-Datei aus Schritt 3.


Rollback (falls du einen Bereich wieder öffnen musst)

Einzelnen Beitrag wieder öffnen:

wp post update 123 --comment_status=open --ping_status=closed

Hinweis: Öffne nur gezielt. Wenn die UI hart deaktiviert wurde (Hooks), musst du diese Stelle für spezifische Post Types konditional erlauben.


Qualitätscheckliste (vor dem Feierabend)

  1. Stichprobenprüfung: 10–20 Beiträge → comment_status = closed.
  2. Theme-Ansicht: Keine Kommentarformulare mehr sichtbar.
  3. Neuer Beitrag: Default = Kommentare aus.
  4. Anhänge: Anhangsseite ohne Formular.
  5. Spam-Ordner: Leer.
  6. Monitoring: Error Logs ruhig; Mails zu Kommentaren stoppen.
  7. Multisite (falls vorhanden): 2–3 Sites prüfen.

Warum WP-CLI statt Plugin?

  • Deterministisch: Du weißt genau, was geändert wurde (DB-Felder).
  • Rücksetzbar: Durch Backup/Einzel-Reopen.
  • Keine zusätzliche Abhängigkeit im Live-System.
  • Schnell: Zehntausende Posts unter einer Minute (je nach DB).

Häufige Fragen

Q: Muss ich zwingend Anhänge schließen?
A: Ja. Sonst bleibt eine offene Flanke, gerade bei Themes mit Anhangsvorlagen.

Q: Was ist mit REST API?
A: Kommentieren via REST erfordert ebenfalls comment_status='open'. Wenn closed, kommt nichts durch. Zusätzliche REST-Blockaden brauchst du nicht — außer du willst die API generell beschneiden.

Q: Reicht es, comments_open zu filtern?
A: Für die Oberfläche ja, für Bots nein. Deshalb DB-Status schließen und optional filtern.

Q: Kann ich das alles „nur für Kategorien X“ machen?
A: Ja, aber dann gehst du in WP-CLI-Skripting (z. B. erst IDs via wp term list und wp post list --category=ID). Beispiel:

wp post list --post_type=post --category=42 --format=ids \
| xargs -n100 wp post update --comment_status=closed --ping_status=closed

Q: Wie verhindere ich, dass Redakteure das wieder einschalten?
A: UI entfernen (Hooks) oder Rolle/Rechte anpassen. Z. B. per Capability-Management. Minimal: Hooks aus Schritt 3.


Bonus: Noch etwas härter — Kommentarendpunkte dichtmachen

Wer paranoid ist, kann wp-comments-post.php serverseitig blocken (Webserver-Level), wenn wirklich alles geschlossen ist und du keine Reviews o. ä. nutzt. Beispiel NGINX (nur Beispiel, mit Bedacht einsetzen):

location = /wp-comments-post.php { return 403; }

Aber: Wenn du irgendwo Kommentare brauchst (Shop-Reviews), ist das zu grob.


Fazit (und warum das Problem jetzt erledigt ist)

Mit 5–10 klaren WP-CLI-Befehlen hast du:

  • Bestehende Posts/Pages/Anhänge auf „Kommentare zu“ gestellt,
  • Pings abgeschaltet,
  • Zukünftige Kommentare verhindert,
  • optional UI und Spam entfernt,
  • und das alles ohne Plugin und ohne rühren im Backend.

Dein Spamproblem ist damit praktisch erledigt. Wenn in ein paar Wochen doch irgendwo noch was durchrutscht, war es ein Edge Case (CPT/Theme). Den erledigst du mit den gleichen Werkzeugen: gezielt post update und harte Hooks.

Empfohlene Minimal-Kombi für 99 % der Fälle:

  1. Massenschließen per WP-CLI (Posts, Pages, Attachments).
  2. Defaults setzen (default_comment_status, default_ping_status).
  3. Optional MU-Hook, um Formular/Anzeige sicher zu killen.
  4. Spam löschen, Cache flushen, Stichprobe prüfen.

Fertig.

Christoph Purin
Christoph Purin

Mein Name ist Christoph Purin und befasse mich mit IoT Geräten wie Raspberry, Arduino, ESP. Auch Aktivitäten wie die FFW, Quad-fahren, Amateurfunk zählen zu meinen bevorzugten Hobbys.
Dieser Blog, stellt eine Sammlung meiner Projekte dar, wie Dinge gelöst oder umgebaut werden können.

Artikel: 371

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert