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:
- Keine neuen Kommentare auf bestehenden Inhalten.
- Keine Pings/Trackbacks mehr.
- Zukünftig sollen neue Inhalte standardmäßig keine Kommentare erlauben.
- Optional: UI/Template für Kommentare verstecken, damit auch kein Formular erscheint (selbst wenn mal etwas offen bliebe).
- Optional: Spam aufräumen (bestehende Spam-/wartende Kommentare löschen).
- 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
undping_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 überwp-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)
- Stichprobenprüfung: 10–20 Beiträge →
comment_status
=closed
. - Theme-Ansicht: Keine Kommentarformulare mehr sichtbar.
- Neuer Beitrag: Default = Kommentare aus.
- Anhänge: Anhangsseite ohne Formular.
- Spam-Ordner: Leer.
- Monitoring: Error Logs ruhig; Mails zu Kommentaren stoppen.
- 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:
- Massenschließen per WP-CLI (Posts, Pages, Attachments).
- Defaults setzen (
default_comment_status
,default_ping_status
). - Optional MU-Hook, um Formular/Anzeige sicher zu killen.
- Spam löschen, Cache flushen, Stichprobe prüfen.
Fertig.