Eine URL in PHP zu kürzen ist ein HTTP-POST. Senden Sie den langen Link an die API eines URL-Kürzers mit der curl-Erweiterung, übergeben Sie Ihren API-Key als Bearer-Token und holen Sie den Kurzlink mit json_decode aus der Antwort. Dafür ist kein Composer-Paket erforderlich, obwohl Guzzle und der Laravel-HTTP-Client denselben Aufruf auf drei Zeilen verkürzen.
Die meisten PHP-Tutorials dazu sind Museumsstücke. Sie richten urlshortener/v1 ein, das Google nach dem Ende der Erstellung neuer Links im Jahr 2019 eingestellt hat; inaktive goo.gl-Links wurden am 25. August 2025 nicht mehr aufgelöst. Oder sie verwenden den bit.ly-v3-Endpunkt, der vor Jahren verschwunden ist. Der folgende Code zielt auf eine aktuelle API.
Dies ist die PHP-Version der allgemeinen Anleitung zum Kürzen einer URL. Wenn Sie noch keinen Dienst ausgewählt haben, behandelt die Übersicht zur kostenlosen URL-Kürzer-API die hier angenommene Anfrageform, das Authentifizierungsmodell und die Limits. Die Feldnamen stammen von Elido; die Struktur lässt sich übertragen.
Der schnellste Weg: ein curl-POST
Legen Sie den Schlüssel in der Umgebung ab, kodieren Sie den Body selbst und lesen Sie den Statuscode, bevor Sie der Nutzlast vertrauen:
<?php
$ch = curl_init('https://api.elido.app/v1/links');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('ELIDO_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'destination_url' => 'https://example.com/spring-sale?utm_source=newsletter',
]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($body === false || $status >= 400) {
throw new RuntimeException("shorten failed: HTTP {$status}");
}
$link = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
echo $link['short_url']; // https://s.elido.me/ab12cd
Zwei Zeilen gibt es dort wegen Fehlern, auf die jeder einmal stößt. CURLOPT_RETURNTRANSFER steht standardmäßig auf false. Ohne diese Option gibt curl das JSON direkt aus und liefert Ihnen true, was die eigentliche Erklärung hinter den meisten Fragen nach "my response is empty" ist. Und CURLOPT_POSTFIELDS wechselt zur Multipart-Formularcodierung, sobald Sie ihm ein Array übergeben. Erst json_encode macht daraus tatsächlich eine JSON-Anfrage.
Auch CURLINFO_RESPONSE_CODE ist wichtig. curl gibt problemlos einen Body wie {"error": "unauthorized"} zurück, ohne sich darüber zu beschweren.
Derselbe Aufruf in Guzzle oder Laravel
Wenn das Projekt bereits Guzzle einbindet, schrumpft der Boilerplate-Code:
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://api.elido.app',
'timeout' => 10,
'headers' => ['Authorization' => 'Bearer ' . getenv('ELIDO_API_KEY')],
]);
$response = $client->post('/v1/links', [
'json' => ['destination_url' => $destination],
'headers' => ['Idempotency-Key' => hash('sha256', $destination)],
]);
$short = json_decode((string) $response->getBody(), true)['short_url'];
Guzzle löst standardmäßig bei 4xx ClientException und bei 5xx ServerException aus. Das ist das Gegenteil von dem stillen Verhalten von curl und vom Verhalten von JavaScripts fetch. Fangen Sie die Exceptions ab oder setzen Sie http_errors auf false und prüfen Sie den Status selbst.
In Laravel kapselt der HTTP-Client Guzzle und bietet Wiederholungen kostenlos:
$short = Http::withToken(config('services.elido.key'))
->timeout(10)
->retry(3, 200, throw: false)
->post('https://api.elido.app/v1/links', ['destination_url' => $destination])
->json('short_url');
Dieses retry(3, 200) bedeutet drei Versuche mit einer Pause von 200 ms. Das reicht für eine vorübergehende Störung, aber nicht für ein Rate-Limit. Das ist der nächste Abschnitt.
Links innerhalb von WordPress kürzen
WordPress verwendet curl nicht direkt. Es hat wp_remote_post, das einen Transport für Sie auswählt und auch auf Hosts funktioniert, auf denen curl deaktiviert ist. Hängen Sie es an das Veröffentlichen und speichern Sie das Ergebnis als Beitragsmetadaten, damit Templates und Feeds es lesen können:
add_action('publish_post', function (int $post_id): void {
if (get_post_meta($post_id, '_elido_short_url', true)) {
return; // already shortened
}
$response = wp_remote_post('https://api.elido.app/v1/links', [
'timeout' => 10,
'headers' => [
'Authorization' => 'Bearer ' . ELIDO_API_KEY,
'Content-Type' => 'application/json',
'Idempotency-Key' => 'post-' . $post_id,
],
'body' => wp_json_encode(['destination_url' => get_permalink($post_id)]),
]);
if (is_wp_error($response)) {
error_log('shorten failed: ' . $response->get_error_message());
return;
}
$link = json_decode(wp_remote_retrieve_body($response), true);
update_post_meta($post_id, '_elido_short_url', $link['short_url']);
}, 10, 1);
ELIDO_API_KEY gehört in wp-config.php, nicht in eine Plugin-Datei, die in einem öffentlichen Repository landet, und nicht in eine Optionszeile, die jeder Administrator lesen kann. Beachten Sie den Idempotency-Key: post-123 ist stabil. Wenn der Hook beim erneuten Veröffentlichen zweimal ausgelöst wird, gibt der zweite Aufruf den bereits vorhandenen Link zurück, statt einen zweiten zu erzeugen.
Wenn Sie den Hook lieber nicht selbst pflegen möchten, vergleicht die WordPress-Anleitung zum Kürzen diesen Weg mit dem Plugin- und dem No-Code-Weg.
Möchten Sie diese Snippets unverändert ausführen? Erstellen Sie einen Schlüssel im kostenlosen Tarif, exportieren Sie ihn als ELIDO_API_KEY, und der curl-Block oben funktioniert unverändert.
Wiederholungen, Rate-Limits und Idempotenz
Unbeaufsichtigtes PHP - ein Cronjob, ein Queue-Worker, ein Bulk-Importer - muss einen 429- und einen vorübergehenden 5xx-Fehler überstehen. Es muss auch den unangenehmen Fall überstehen, dass der POST die API erreicht, die Antwort auf dem Rückweg aber verloren geht. Ihr Code sieht einen Timeout, wiederholt die Anfrage und erstellt für dasselbe Ziel einen zweiten Kurzlink, sofern ein Idempotency-Key ihn nicht daran hindert.
function shorten(string $destination, int $retries = 3): string
{
$key = hash('sha256', $destination); // stable across retries and re-runs
for ($attempt = 0; $attempt < $retries; $attempt++) {
[$body, $status, $retryAfter] = post_link($destination, $key);
if ($status === 429) {
sleep(max(1, (int) $retryAfter));
continue;
}
if ($status >= 500) {
sleep(2 ** $attempt); // exponential backoff
continue;
}
if ($status >= 400) {
throw new RuntimeException("shorten failed: HTTP {$status} {$body}");
}
return json_decode($body, true, 512, JSON_THROW_ON_ERROR)['short_url'];
}
throw new RuntimeException("shorten failed after {$retries} attempts");
}
Ein 401 oder 403 wird sich niemals von selbst beheben. Deshalb wird beim ersten Versuch eine Exception ausgelöst, statt dreimal zu warten. Ein 429 wartet so lange, wie der Retry-After-Header vorgibt. Nur bei 5xx wird die Wartezeit verdoppelt. Die Vertiefung zu Rate-Limits und Idempotenz erklärt die vollständige Begründung, einschließlich der Frage, warum ein Hash des Ziels meist ein besserer Schlüssel ist als eine zufällige UUID, wenn derselbe Batch erneut ausgeführt werden könnte.
Eine PHP-spezifische Falle: sleep() innerhalb einer Webanfrage hält den php-fpm-Worker die ganze Zeit fest. Wiederholungen gehören in CLI-Skripte, Queue-Jobs oder WP-Cron, nicht in die Anfrage, die eine Seite rendert.
Viele URLs ohne serielle Wartezeiten kürzen
Eine foreach-Schleife, die 500 URLs kürzt, verursacht 500 aufeinanderfolgende Roundtrips. Bei jeweils 120 ms ist das eine Minute reine Wartezeit. PHP hat keine asynchrone Laufzeitumgebung, aber parallele HTTP-Anfragen sind möglich, und Guzzles Pool hält eine feste Zahl von Anfragen gleichzeitig in der Bearbeitung:
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Request;
$requests = static function (array $urls) {
foreach ($urls as $url) {
yield new Request('POST', '/v1/links', [
'Content-Type' => 'application/json',
'Idempotency-Key' => hash('sha256', $url),
], json_encode(['destination_url' => $url]));
}
};
$short = [];
$pool = new Pool($client, $requests($urls), [
'concurrency' => 8,
'fulfilled' => function ($response, $i) use (&$short, $urls) {
$short[$urls[$i]] = json_decode((string) $response->getBody(), true)['short_url'];
},
'rejected' => function ($reason, $i) use (&$short, $urls) {
$short[$urls[$i]] = 'ERROR: ' . $reason->getMessage();
},
]);
$pool->promise()->wait();
Wenn Sie die Ergebnisse nach der ursprünglichen URL indizieren, bleiben Teilfehler sichtbar und können erneut ausgeführt werden. Ohne Composer erledigt curl_multi_init dieselbe Aufgabe mit mehr Verwaltungsaufwand: Handles hinzufügen, mit curl_multi_exec in einer Schleife arbeiten und jede Antwort einsammeln. Beginnen Sie mit einer Parallelität von etwa acht und lassen Sie die Antwort-Header erkennen, ob Sie sie reduzieren müssen.
Welche Methode Sie wählen sollten
Passen Sie das Werkzeug an den Host an. Shared-Hosting ohne Composer: der reine curl-Block. Eine Symfony- oder Laravel-Anwendung: Guzzle oder der HTTP-Client mit einmalig auf Client-Ebene konfigurierten Wiederholungen. WordPress: wp_remote_post in einem Hook zum Veröffentlichen. Ein nächtlicher Import von Tausenden Links: der Pool mit einem stabilen Idempotency-Key, damit ein erneuter Lauf kostenlos ist.
Egal, wofür Sie sich entscheiden, vier Dinge bleiben gleich: Der Schlüssel kommt aus der Umgebung, curl erhält ein explizites Timeout, der Statuscode wird vor dem Body gelesen, und alles, was zweimal ausgeführt werden könnte, trägt einen Idempotency-Key. Teams, die über das erste Skript hinauskommen, interessieren sich meist für die API und SDKs oder dafür, was die Plattform Entwicklern noch bietet - Klicks, Tags, Ablaufzeiten und Webhooks für Link-Ereignisse statt Polling.
Die Cornerstone-Reihe lesen
Dieser Beitrag gehört zum Engineering-Cluster. Beginnen Sie mit dem Leitfaden zur kostenlosen URL-Kürzer-API für Endpunktstruktur und Authentifizierung und lesen Sie anschließend den Beitrag zu Rate-Limits und Idempotenz, um sich unter Last korrekt zu verhalten. Die aktuelle Referenz sind die API-Dokumente. Wenn Sie goo.gl-Links geerbt haben, erklärt die Alternative zum Google URL Shortener, wohin diese jetzt führen.
Weitere Beiträge im Blog
Häufig gestellte Fragen
Wie kürze ich eine URL in PHP?
Senden Sie die lange URL per POST an die API eines URL-Kürzers, dabei Ihren API-Key als Bearer-Header und das Ziel in einem JSON-Body, dekodieren Sie anschließend die Antwort mit json_decode und lesen Sie das Feld short_url aus. Mit curl sind das ungefähr fünfzehn Zeilen mit curl_setopt_array, mit Guzzle oder dem Laravel-HTTP-Client dagegen drei, sofern das Projekt bereits einen davon verwendet.
Wie kürze ich eine URL in PHP ohne externe Bibliothek?
Verwenden Sie die curl-Erweiterung, die mit PHP ausgeliefert wird. curl_init, curl_setopt_array, curl_exec und json_decode decken den gesamten Ablauf ohne Composer-Abhängigkeit ab. Das ist auf Shared-Hosting wichtig, auf dem Sie Composer nicht ausführen können. Setzen Sie CURLOPT_RETURNTRANSFER auf true, sonst gibt curl_exec die Antwort aus, statt sie zurückzugeben.
Funktioniert die Google-URL-Shortener-API noch in PHP?
Nein. Google nahm ab 2019 keine neuen Links mehr über die URL-Shortener-API an und stellte den Dienst ein. Inaktive goo.gl-Links wurden ab dem 25. August 2025 nicht mehr aufgelöst. Jedes PHP-Tutorial auf Basis von urlshortener/v1 ist toter Code, und auch die gleichaltrigen bit.ly-v3-Beispiele sind verschwunden.
Kann ich eine URL in WordPress mit PHP kürzen?
Ja. Rufen Sie wp_remote_post aus einem Hook auf, der beim Veröffentlichen ausgelöst wird, und speichern Sie den zurückgegebenen Kurzlink mit update_post_meta, damit der Rest des Themes ihn lesen kann. Bewahren Sie den API-Key in wp-config.php auf, nicht in einer Plugin-Datei oder der Datenbank, und prüfen Sie is_wp_error für die Antwort, da WordPress ein WP_Error-Objekt zurückgibt, statt eine Exception auszulösen.
Wie kürze ich viele URLs auf einmal in PHP?
Senden Sie die Anfragen gleichzeitig mit einem begrenzten Pool, statt eine foreach-Schleife zu verwenden, die auf jede Antwort wartet. Guzzles Pool mit einer Parallelität von etwa acht ist der kürzeste Weg, curl_multi erledigt dasselbe ohne Composer, und ein aus jeder URL abgeleiteter Idempotency-Key verhindert, dass ein erneuter Lauf doppelte Links erstellt.
Warum gibt meine PHP-curl-Anfrage an einen URL-Kürzer 401 oder eine leere Antwort zurück?
Ein 401 bedeutet, dass der Authorization-Header fehlt oder falsch formatiert ist. Er muss als einzelner String im CURLOPT_HTTPHEADER-Array exakt 'Authorization: Bearer YOUR_KEY' lauten. Ein leerer Rückgabewert bedeutet meist, dass CURLOPT_RETURNTRANSFER nie gesetzt wurde: Der Body ging direkt an die Ausgabe und curl_exec gab stattdessen true zurück.
Elido testen
URL einfügen, kurzer Link in Sekunden
Kein Konto nötig. Link bleibt 30 Tage aktiv. Konto erstellen, um ihn dauerhaft zu behalten.
Kostenlos, keine Anmeldung erforderlich · 2 pro Tag