- Veröffentlicht am
- • Bits
Webmentions senden mit HTMLy
- Autor
-
-
- Benutzer
- Piehnat
- Beiträge dieses Autors
- Beiträge dieses Autors
-
Gestern hab ich ja im Blogartikel erklärt, wie ich den Empfang von Webmentions über webmention.io in HTMLy integriert habe. Heute kümmern wir uns um den Versand, weil Robert ganz direkt gefragt hat „Wie machst du das eigentlich mit dem Senden?“ Gute Frage.
Spoiler, HTMLy kann das von Haus aus nicht. Warum? Weil HTMLy ein minimalistisches Flat-File-Blogsystem ist. Kein Plugin-System, keine Hooks nach dem Speichern, kein „ich erledige das mal automatisch für dich“-Zauber. Ist ja auch irgendwie der Reiz daran, man muss es halt selber machen. Und genau das erledigen wir heute.
Vorweg, bevor wieder jemand den Klugscheißer-Modus aktiviert, weil ich nen falschen Fachbegriff verwende. Ja Bro, ich bin kein studierter Informatiker, kein Vollzeit-Dev und schon gar kein zertifizierter PHP-Guru. Ich bin einfach ein Dude, der irgendwann keinen Bock mehr auf Big Tech, Datenschnüffelei und Plattform-Abhängigkeit hatte. Also hab ich angefangen, mir den Kram selber beizubringen, learning by doing, Schritt für Schritt, Script für Script. Nicht jeder hat Zeit, Geld oder Möglichkeiten aufn Informatikstudium. Und das ist auch völlig okay. Also chill deine Basis und halt den Ball flach. Hier geht’s nicht um perfekte Codephilosophie, sondern um praktische Lösungen.
Warum der Versand fehlt (und das okay ist)
Webmentions funktionieren so: Wenn du jemand anderen verlinkst, dann solltest du ihm Bescheid sagen. Das geht per HTTP-POST an einen speziellen Webmention-Endpunkt. So nach dem Motto „Yo, ich hab dich erwähnt, hier ist der Link.“
HTMLy? Verlinken kann es, aber das Bescheid sagen? Fehlanzeige. Du musst den Versand also selbst auslösen. Automatisch würde das zb gehen, wenn man das Blogsystem auf GitHub hostet oder sich mit Cronjobs befreundet. Da ich aber aus chronischer Pleitigkeit auf nem minimalen Strato Space blogge und den nicht mit systemd oder Shellzugriff quälen kann, hab ich's simpel gelöst.
Mit einem kleinen PHP-Skript. Direkt im Blog. Läuft.
Schritt 1: Webmention-Send-Skript erstellen
Dieses Skript nutzt jetzt cURL statt file_get_contents()
. Warum? Weil viele Hoster (Strato z. B.) aus Sicherheitsgründen das Laden von URLs mit file_get_contents()
blockieren.
cURL ist robuster, schneller und sorgt dafür, dass das Script nicht ewig hängt oder einfach abbricht.
Außerdem habe ich einen Passwortschutz eingebaut. So kann nicht jeder einfach so Webmention-Sendungen auslösen. Ich muss beim Aufruf das Passwort als Parameter auth mitgeben und damit ich im Blick hab, wer das Script wann nutzt (und wer scheitert), schreibt das Script automatisch eine Logdatei namens webmention-log.txt ins Blogverzeichnis, ganz automatisch, kein Extra-Aufwand.
Und weil man ja keine böswilligen Bots einladen will, gibt’s eine kleine Verzögerung von 2 Sekunden bei jedem Aufruf, um brutale Zugriffswellen zu drosseln.
Was macht das Ding?
- Nimmt eine Artikel-URL (
source
) als Parameter - Es lädt die Seite via cURL
- Es findet alle < a href="..."> Links
- Prüft für jeden Link per cURL, ob ein Webmention-Endpunkt existiert (im HTTP-Header oder HTML)
- Wenn ja, sendet es die Webmention an die Zielseite
- Wenn nein, gibt es ne nette Meldung wie „Kein Endpunkt gefunden“
- Loggt alle Zugriffe (erfolgreich oder fehlgeschlagen) mit IP und Zeit in webmention-log.txt
- Verzögert den Scriptstart um 2 Sekunden als einfache Schutzmaßnahme
- Verlangt ein Passwort (auth) zur Nutzung
<?php
// Einfaches PHP-Webmention-Sender-Skript mit Passwortschutz, cURL, Delay & IP-Logging
// 🔐 Passwortschutz aktivieren
$authKey = 'mein-geheimer-code'; // <- Hier dein Passwort reinschreiben
if (!isset($_GET['auth']) || $_GET['auth'] !== $authKey) {
// Zugriff loggen
file_put_contents('webmention-log.txt', date('Y-m-d H:i:s') . " - FAILED access from IP: " . $_SERVER['REMOTE_ADDR'] . "\n", FILE_APPEND);
die('Unauthorized access');
}
// ✅ Erfolgreichen Zugriff loggen
file_put_contents('webmention-log.txt', date('Y-m-d H:i:s') . " - Access from IP: " . $_SERVER['REMOTE_ADDR'] . "\n", FILE_APPEND);
// 🕒 Anti-Brute-Force: kleine Verzögerung
sleep(2);
if (!isset($_GET['source']) || empty($_GET['source'])) {
die('Bitte die Quelle als URL-Parameter "source" angeben.');
}
$source = $_GET['source'];
// cURL-Funktion zum Laden von URLs
function curl_get_contents($url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // Redirects folgen
curl_setopt($ch, CURLOPT_USERAGENT, 'Webmention Sender by Piehnat'); // User-Agent setzen
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // Timeout 10 Sekunden
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
// 1. Lade den Inhalt der Quellseite via cURL
$html = curl_get_contents($source);
if (!$html) {
die("Fehler: Konnte Quelle $source nicht laden.");
}
// 2. Alle Links aus der Quelle extrahieren (einfach per Regex)
preg_match_all('/<a\s[^>]*href=["\']([^"\']+)["\']/i', $html, $matches);
$links = array_unique($matches[1]);
echo "<h2>Webmentions senden von:</h2>";
echo "<p>$source</p>";
if (empty($links)) {
die("Keine Links gefunden.");
}
echo "<h3>Gefundene Links:</h3><ul>";
// Hilfsfunktion: Webmention-Endpunkt finden (mit cURL)
function find_webmention_endpoint($url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_USERAGENT, 'Webmention Sender by Piehnat');
$headers = curl_exec($ch);
curl_close($ch);
if (!$headers) return false;
if (preg_match_all('/Link:\s*<([^>]+)>;\s*rel="?webmention"?/i', $headers, $matches)) {
return $matches[1][0];
}
$html = curl_get_contents($url);
if ($html && preg_match('/<link[^>]+rel=["\']?webmention["\']?[^>]*href=["\']?([^"\']+)["\']?/i', $html, $m)) {
return $m[1];
}
return false;
}
// Hilfsfunktion: Webmention senden
function send_webmention($source, $target, $endpoint) {
$data = http_build_query([
'source' => $source,
'target' => $target
]);
$opts = [
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
'content' => $data,
'ignore_errors' => true
]
];
$context = stream_context_create($opts);
$result = @file_get_contents($endpoint, false, $context);
return $result !== false;
}
// 3. Für jeden Link prüfen wir den Webmention-Endpunkt und senden, falls vorhanden
foreach ($links as $link) {
echo "<li>$link — ";
$endpoint = find_webmention_endpoint($link);
if ($endpoint) {
$ok = send_webmention($source, $link, $endpoint);
echo $ok ? "<strong>Webmention gesendet</strong>" : "<strong>Fehler beim Senden</strong>";
} else {
echo "<em>Kein Webmention-Endpunkt gefunden</em>";
}
echo "</li>";
}
echo "</ul>";
?>
Und das alles in klartextiger HTML-Ausgabe, damit ich direkt sehe, was lief und was nicht.
Beispiel-Aufruf im Browser:
https://piehnat.de/send-webmentions.php?source=https://piehnat.de/post/webmentions-zuruck-in-die-blog-zukunft&auth=dein-geheimer-code
Das auth
schützt das Skript vor Missbrauch. Einfach die URL deines neuen Artikels hinten anhängen und der Rest passiert automatisch. Kein Bullshit.
Schritt 2: Datei hochladen
Die Datei send-webmentions.php kommt per FTP einfach dahin, wo auch meine index.php liegt, also direkt ins Root-Verzeichnis des Blogs. Danach kannst du die Datei direkt im Browser aufrufen und loslegen.
Schritt 3: Komfort-Upgrade Webmention-Launcher
Weil ich aber faul bin und keinen Bock habe, die URL jedes Mal per Hand zusammenzubasteln, hab ich mir ein kleines HTML-Tool gebastelt.
Ein Feld für die Artikel-URL, ein Feld für das Passwort (auth), ein Button, fertig. Klick. Script wird aufgerufen.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<title>Webmention Sender</title>
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; }
label, input, button { display: block; width: 100%; margin-bottom: 1em; font-size: 1.2em; }
input { padding: 0.5em; }
button { padding: 0.7em; cursor: pointer; }
</style>
</head>
<body>
<h1>Webmention Sender</h1>
<label for="articleUrl">Artikel-URL:</label>
<input type="url" id="articleUrl" placeholder="https://piehnat.de/post/dein-artikel" required />
<label for="authCode">Auth-Code:</label>
<input type="password" id="authCode" placeholder="Dein geheimer Code" required />
<button id="sendBtn">Webmentions senden</button>
<script>
const scriptBaseUrl = "https://piehnat.de/send-webmentions.php";
document.getElementById('sendBtn').addEventListener('click', () => {
const articleUrl = document.getElementById('articleUrl').value.trim();
const authCode = document.getElementById('authCode').value.trim();
if (!articleUrl) {
alert('Bitte die Artikel-URL ausfüllen!');
return;
}
if (!authCode) {
alert('Bitte den Auth-Code ausfüllen!');
return;
}
const encodedUrl = encodeURIComponent(articleUrl);
const encodedAuth = encodeURIComponent(authCode);
const fullUrl = `${scriptBaseUrl}?source=${encodedUrl}&auth=${encodedAuth}`;
window.open(fullUrl, '_blank');
});
</script>
</body>
</html>
Als .html-Datei auf dem Rechner speichern und bei Bedarf starten.
So sieht das im Browser aus
Und so das Ergebnis
Und ja, ich weiß, das Script schickt auch Webmentions an eigene Seiten, Kategorien, Impressum, und sogar mailto:-Links werden versucht (die natürlich fehlschlagen). Kann man alles rausfiltern. Könnte man machen. Aber reicht für heute. Peace.
P.S. Noch mehr Webmentions-Lesestoff gibt es bei Jan.