WordPress: llms.txt automatisch erstellen – mit einfachem PHP-Snippet

Ich habe nach einer unkomplizierten Möglichkeit gesucht, eine llms.txt für mehrere WordPress-Websites bereitzustellen. Gerade wenn man als Freelancer oder Agentur viele Kundenwebsites betreut, möchte man dafür nicht jedes Mal ein zusätzliches Plugin installieren oder Dateien manuell hochladen.

Also habe ich (ChatGPT 😉) ein kleines PHP-Snippet entwickelt, das genau das übernimmt.

Automatischer Fallback mit manueller Anpassung

Das Prinzip ist einfach:

  • Automatischer Fallback: Das Snippet erstellt unter /llms.txt automatisch eine Datei mit dem Website-Namen, der Beschreibung, den wichtigsten WordPress-Seiten und einem Link zur Sitemap.
  • Manuelle Anpassung: Unter Einstellungen → LLMs.txt lässt sich der automatisch generierte Inhalt jederzeit durch eigenen Markdown-Code ersetzen.
  • KI-Unterstützung: Wer eine individuelle llms.txt möchte, kann sich den Inhalt beispielsweise mit ChatGPT oder Claude erstellen lassen und anschließend einfach einfügen.

Das Snippet unterstützt außerdem die Sitemap-URLs von SEOPress, Yoast SEO und Rank Math.

Braucht man überhaupt eine llms.txt?

Ob und wie stark KI-Systeme eine llms.txt tatsächlich berücksichtigen, ist aktuell noch offen. Einen nachgewiesenen SEO-Vorteil gibt es bisher nicht.

Die Idee dahinter finde ich trotzdem sinnvoll: KI-Systemen eine strukturierte Übersicht über die wichtigsten Inhalte einer Website bereitstellen.

Mein Ansatz ist deshalb pragmatisch: Mit dem automatischen Fallback ist die Datei erst einmal vorhanden. Falls man später mehr Zeit investieren möchte, lässt sich der Inhalt individuell anpassen.

Das PHP-Snippet

Den vollständigen PHP-Code finden Sie hier:

/**
 * Dynamic LLMs.txt for WordPress
 *
 * Erstellt automatisch eine llms.txt unter /llms.txt.
 * Optional kann der Inhalt unter Einstellungen → LLMs.txt
 * vollständig überschrieben werden.
 *
 * Unterstützt SEOPress, Yoast SEO und Rank Math.
 *
 * Version: 1.1
 */

if (!defined('ABSPATH')) {
    exit;
}


/**
 * 1. LLMs.txt ausliefern
 */
add_action('template_redirect', function () {

    // Aktuellen URL-Pfad ermitteln
    $request_path = wp_parse_url(
        $_SERVER['REQUEST_URI'] ?? '',
        PHP_URL_PATH
    );

    $llms_path = wp_parse_url(
        home_url('/llms.txt'),
        PHP_URL_PATH
    );

    // Nur auf /llms.txt reagieren
    if (
        !is_string($request_path) ||
        untrailingslashit($request_path) !== untrailingslashit($llms_path)
    ) {
        return;
    }

    // Manuelle Eingabe abrufen
    $custom_content = get_option('custom_llms_txt_content', '');

    // Manuelle Eingabe oder automatischer Fallback
    if (
        is_string($custom_content) &&
        trim($custom_content) !== ''
    ) {
        $content = $custom_content;
    } else {
        $content = custom_generate_llms_txt();
    }

    // Eventuellen 404-Status entfernen
    global $wp_query;

    if (isset($wp_query)) {
        $wp_query->is_404 = false;
    }

    status_header(200);

    // Markdown als reine Textantwort ausliefern
    header('Content-Type: text/plain; charset=utf-8');
    header('X-Content-Type-Options: nosniff');

    // Keine unnötige Zwischenspeicherung
    header('Cache-Control: no-cache, must-revalidate');

    echo $content;

    exit;

}, 0);


/**
 * 2. Markdown-Text bereinigen
 */
function custom_llms_txt_escape($text) {

    $text = wp_strip_all_tags((string) $text);

    // Zeilenumbrüche und zusätzliche Leerzeichen entfernen
    $text = preg_replace('/\s+/u', ' ', $text);

    // Markdown-Sonderzeichen maskieren
    $text = str_replace(
        array('\\', '[', ']', '#'),
        array('\\\\', '\[', '\]', '\#'),
        trim($text)
    );

    return $text;
}


/**
 * 3. Prüfen, ob eine Seite auf Noindex steht
 */
function custom_llms_txt_is_noindex($post_id) {

    // Passwortgeschützte Inhalte ausschließen
    if (post_password_required($post_id)) {
        return true;
    }

    // SEOPress: Individuelle Noindex-Einstellung
    if (defined('SEOPRESS_VERSION')) {

        $noindex = get_post_meta(
            $post_id,
            '_seopress_robots_index',
            true
        );

        if ($noindex === 'yes') {
            return true;
        }
    }

    // Yoast SEO
    if (defined('WPSEO_VERSION')) {

        $noindex = get_post_meta(
            $post_id,
            '_yoast_wpseo_meta-robots-noindex',
            true
        );

        if ((string) $noindex === '1') {
            return true;
        }
    }

    // Rank Math
    if (defined('RANK_MATH_VERSION')) {

        $robots = get_post_meta(
            $post_id,
            'rank_math_robots',
            true
        );

        if (
            is_array($robots) &&
            in_array('noindex', $robots, true)
        ) {
            return true;
        }
    }

    return false;
}


/**
 * 4. XML-Sitemap-URL ermitteln
 */
function custom_llms_txt_get_sitemap_url() {

    // SEOPress
    if (defined('SEOPRESS_VERSION')) {
        return home_url('/sitemaps.xml');
    }

    // Yoast SEO und Rank Math
    if (
        defined('WPSEO_VERSION') ||
        defined('RANK_MATH_VERSION')
    ) {
        return home_url('/sitemap_index.xml');
    }

    // WordPress Standard-Sitemap
    return home_url('/wp-sitemap.xml');
}


/**
 * 5. Automatische LLMs.txt generieren
 */
function custom_generate_llms_txt() {

    // Allgemeine WordPress-Informationen
    $site_name = custom_llms_txt_escape(
        get_bloginfo('name')
    );

    $site_description = custom_llms_txt_escape(
        get_bloginfo('description')
    );

    $site_url = home_url('/');

    // Titel
    $content = "# {$site_name}\n\n";

    // Beschreibung
    if (!empty($site_description)) {

        $content .= "> {$site_description}\n\n";

    } else {

        $content .= "> Official website of {$site_name}.\n\n";
    }

    // Startseite
    $content .= "## Main Website\n\n";
    $content .= "- [Homepage]({$site_url})\n\n";

    // Veröffentlichte Seiten abrufen
    $pages = get_posts(array(
        'post_type'           => 'page',
        'post_status'         => 'publish',
        'posts_per_page'      => 50,
        'orderby'             => array(
            'menu_order' => 'ASC',
            'title'      => 'ASC',
        ),
        'no_found_rows'       => true,
        'suppress_filters'    => false,
    ));

    // ID der statischen Startseite
    $front_page_id = (int) get_option('page_on_front');

    $page_links = array();

    foreach ($pages as $page) {

        // Startseite nicht doppelt ausgeben
        if ((int) $page->ID === $front_page_id) {
            continue;
        }

        // Noindex und passwortgeschützte Seiten ausschließen
        if (custom_llms_txt_is_noindex($page->ID)) {
            continue;
        }

        // Titel und URL
        $title = custom_llms_txt_escape(
            get_the_title($page->ID)
        );

        $url = get_permalink($page->ID);

        if (empty($title) || empty($url)) {
            continue;
        }

        $page_links[] = "- [{$title}]({$url})";
    }

    // Seiten ausgeben
    if (!empty($page_links)) {

        $content .= "## Pages\n\n";

        $content .= implode("\n", $page_links);

        $content .= "\n\n";
    }

    // XML-Sitemap
    $sitemap_url = custom_llms_txt_get_sitemap_url();

    $content .= "## Additional Resources\n\n";

    $content .= "- [XML Sitemap]({$sitemap_url})\n";

    return $content;
}


/**
 * 6. Einstellungsseite registrieren
 */
add_action('admin_menu', function () {

    add_options_page(
        'LLMs.txt',
        'LLMs.txt',
        'manage_options',
        'custom-llms-txt',
        'custom_llms_txt_settings_page'
    );

});


/**
 * 7. Einstellung registrieren
 */
add_action('admin_init', function () {

    register_setting(
        'custom_llms_txt_settings',
        'custom_llms_txt_content',
        array(
            'type'              => 'string',
            'sanitize_callback' => 'custom_llms_txt_sanitize',
            'default'           => '',
        )
    );

});


/**
 * 8. Markdown-Eingabe bereinigen
 */
function custom_llms_txt_sanitize($input) {

    if (!is_string($input)) {
        return '';
    }

    // Zeilenumbrüche vereinheitlichen
    $input = str_replace(
        array("\r\n", "\r"),
        "\n",
        $input
    );

    // Null-Bytes entfernen
    $input = str_replace("\0", '', $input);

    return trim($input);
}


/**
 * 9. Einstellungsseite ausgeben
 */
function custom_llms_txt_settings_page() {

    if (!current_user_can('manage_options')) {
        return;
    }

    $content = get_option('custom_llms_txt_content', '');

    $llms_url = home_url('/llms.txt');

    $is_custom = (
        is_string($content) &&
        trim($content) !== ''
    );

    ?>

    <div class="wrap">

        <h1>LLMs.txt</h1>

        <p>
            Die LLMs.txt wird automatisch aus den
            WordPress-Inhalten generiert.
            Alternativ kann eine individuelle Markdown-Version
            hinterlegt werden.
        </p>

        <p>
            <strong>Aktuelle Datei:</strong>
            <a
                href="<?php echo esc_url($llms_url); ?>"
                target="_blank"
                rel="noopener noreferrer"
            >
                <?php echo esc_html($llms_url); ?>
            </a>
        </p>

        <p>
            <strong>Aktiver Modus:</strong>

            <?php if ($is_custom) : ?>

                Individuelle LLMs.txt

            <?php else : ?>

                Automatisch generierte LLMs.txt

            <?php endif; ?>
        </p>

        <hr>

        <form method="post" action="options.php">

            <?php settings_fields('custom_llms_txt_settings'); ?>

            <h2>Individuelle LLMs.txt</h2>

            <p>
                Hier kann eine eigene LLMs.txt im
                Markdown-Format hinterlegt werden.
            </p>

            <p>
                <strong>Hinweis:</strong>
                Wenn das Feld leer bleibt, wird automatisch
                die Standardversion ausgegeben.
                Eine manuelle Eingabe ersetzt die
                automatische Version vollständig.
            </p>

            <textarea
                name="custom_llms_txt_content"
                id="custom_llms_txt_content"
                rows="20"
                class="large-text code"
                spellcheck="false"
                placeholder="# Website Name

> Website description

## Main Pages

- [Homepage](https://example.com/)
- [Contact](https://example.com/contact/)"
            ><?php echo esc_textarea($content); ?></textarea>

            <?php submit_button('Änderungen speichern'); ?>

        </form>

    </div>

    <?php
}

Das Snippet lässt sich beispielsweise über das kostenlose Plugin Code Snippets einbinden. Nach der Aktivierung ist die Datei direkt unter ihre-domain.de/llms.txt erreichbar.

Keine zusätzliche Konfiguration, kein weiteres spezialisiertes Plugin und trotzdem die Möglichkeit, den Inhalt jederzeit selbst anzupassen.

Schreiben Sie einen Kommentar

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