7 min di letturaIngegneria

Come accorciare un URL in PHP con curl, Guzzle o WordPress

Accorcia un URL in PHP con un breve blocco curl, la stessa chiamata in Guzzle o Laravel, una versione WordPress con wp_remote_post, oltre a retry e accorciamento in blocco.

Ana Kowalska
Marketing solutions engineering
Come accorciare un URL in PHP: una richiesta POST curl invia un URL di destinazione a un'API di accorciamento e decodifica il link breve dalla risposta JSON, accanto ai percorsi Guzzle e WordPress

Accorciare un URL in PHP significa fare una sola richiesta HTTP POST. Invia il link lungo all'API di un accorciatore con l'estensione curl, passa la tua chiave API come token Bearer e usa json_decode per estrarre il link breve dalla risposta. Non serve alcun pacchetto Composer, anche se Guzzle e il client HTTP di Laravel riducono la stessa chiamata a tre righe.

La maggior parte dei tutorial PHP su questo argomento è da museo. Collegano urlshortener/v1, che Google ha ritirato dopo aver interrotto la creazione di nuovi link nel 2019, con i link goo.gl inattivi che hanno smesso di funzionare il 25 agosto 2025, oppure usano l'endpoint bit.ly v3 scomparso anni fa. Il codice qui sotto si rivolge a un'API attuale.

Questa è la versione PHP della guida generale su come accorciare un URL. Se non hai ancora scelto un servizio, la panoramica delle API gratuite per accorciare gli URL illustra la forma della richiesta, il modello di autenticazione e i limiti ipotizzati qui. I nomi dei campi sono quelli di Elido; la struttura è trasferibile.

Il modo più rapido: una richiesta POST con curl

Metti la chiave nell'ambiente, codifica tu il corpo e leggi il codice di stato prima di fidarti del payload:

<?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

Due righe sono lì perché contengono bug in cui tutti incappano almeno una volta. CURLOPT_RETURNTRANSFER è impostato su false per impostazione predefinita, quindi senza di esso curl stampa il JSON direttamente nell'output e ti restituisce true: è il vero motivo dietro alla maggior parte delle domande del tipo "la mia risposta è vuota". Inoltre, CURLOPT_POSTFIELDS passa alla codifica multipart dei dati nel momento in cui gli passi un array, quindi è json_encode a trasformare effettivamente la richiesta in JSON.

Anche CURLINFO_RESPONSE_CODE è importante. curl è perfettamente disposto a restituire un corpo pieno di {"error": "unauthorized"} senza lamentarsi.

Una richiesta POST PHP con curl invia un URL di destinazione e un token Bearer all'endpoint dei link dell'accorciatore, l'API restituisce HTTP 201 con uno short_url letto da json_decode e un ciclo di retry attende progressivamente in caso di risposte 429 e 5xx

La stessa chiamata con Guzzle o Laravel

Se il progetto include già Guzzle, il codice ripetitivo si riduce:

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 lancia ClientException sui 4xx e ServerException sui 5xx per impostazione predefinita, il contrario del silenzio di curl e del comportamento di fetch in JavaScript. Intercettale, oppure imposta http_errors su false e controlla tu lo stato.

In Laravel, il client HTTP avvolge Guzzle e ti offre i retry gratuitamente:

$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');

Quel retry(3, 200) equivale a tre tentativi con una pausa di 200 ms. È sufficiente per un problema transitorio, ma non per un limite di frequenza: è questo il tema della sezione successiva.

WordPress non usa curl direttamente. Dispone di wp_remote_post, che sceglie un trasporto per te e funziona sugli hosting in cui curl è disabilitato. Collegalo alla pubblicazione, quindi salva il risultato nei metadati del post, così template e feed possono leggerlo:

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 appartiene a wp-config.php, non a un file del plugin che finisce in un repository pubblico e nemmeno in una riga delle opzioni che ogni amministratore può leggere. Nota la chiave di idempotenza: post-123 è stabile, quindi se l'hook scatta due volte dopo una ripubblicazione, la seconda chiamata restituisce il link già esistente invece di crearne un secondo.

Se preferisci non mantenere l'hook, la guida all'accorciatore WordPress confronta questo approccio con quello del plugin e con i percorsi senza codice.

Vuoi eseguire questi snippet così come sono? Crea una chiave nel piano gratuito, esportala come ELIDO_API_KEY e il blocco curl qui sopra funzionerà senza modifiche.

Retry, limiti di frequenza e idempotenza

Il PHP non presidiato - un job cron, un worker di coda, un importatore in blocco - deve sopravvivere a un 429 e a un 5xx transitorio. Deve anche sopravvivere al caso scomodo in cui la richiesta POST raggiunge l'API ma la risposta si perde durante il ritorno. Il tuo codice vede un timeout, riprova e crea un secondo link breve per la stessa destinazione, a meno che una chiave di idempotenza non lo impedisca.

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");
}

Un 401 o un 403 non si risolveranno mai da soli, quindi il codice lancia un'eccezione al primo tentativo invece di dormire tre volte. Un 429 attende per il tempo richiesto dall'header Retry-After. Solo i 5xx ricevono il backoff raddoppiato. L'approfondimento su limiti di frequenza e idempotenza contiene il ragionamento completo, compreso il motivo per cui un hash della destinazione è di solito una chiave migliore di un UUID casuale quando lo stesso batch potrebbe essere eseguito di nuovo.

Una trappola specifica di PHP: sleep() dentro una richiesta web occupa un worker php-fpm per tutto il tempo. I retry appartengono agli script CLI, ai job di coda o a WP-Cron, non alla richiesta che esegue il rendering di una pagina.

Accorciare in blocco senza attese seriali

Un ciclo foreach che accorcia 500 URL effettua 500 round trip consecutivi. A 120 ms ciascuno è un minuto di pura attesa. PHP non ha un runtime asincrono, ma dispone di HTTP concorrente, e Pool di Guzzle mantiene in volo un numero fisso di richieste:

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();
Tre percorsi PHP verso lo stesso endpoint dell'accorciatore: curl puro sull'hosting condiviso, Guzzle o Laravel in un'applicazione e wp_remote_post dentro WordPress, tutti con la chiave API letta dall'ambiente

Indicizzare i risultati con l'URL originale mantiene visibile ogni errore parziale e permette di eseguire nuovamente l'operazione. Senza Composer, curl_multi_init svolge lo stesso lavoro con più gestione manuale: aggiungi gli handle, esegui il ciclo su curl_multi_exec e raccogli ogni risposta. Inizia con una concorrenza intorno a otto e lascia che gli header della risposta ti dicano quando ridurla.

Quale approccio scegliere

Abbina lo strumento all'hosting. Hosting condiviso senza Composer: il blocco curl puro. App Symfony o Laravel: Guzzle o il client HTTP, con i retry configurati una volta a livello del client. WordPress: wp_remote_post in un hook di pubblicazione. Import notturno di migliaia di link: il pool, con una chiave di idempotenza stabile che rende gratuita una riesecuzione.

Qualunque sia la tua scelta, quattro cose restano uguali: la chiave proviene dall'ambiente, curl riceve un timeout esplicito, il codice di stato viene letto prima del corpo e tutto ciò che potrebbe essere eseguito due volte porta una chiave di idempotenza. I team che superano il primo script di solito vogliono le API e gli SDK o scoprire cos'altro offre la piattaforma agli sviluppatori: clic, tag, scadenza, webhook per gli eventi dei link invece del polling.

Leggi la serie principale

Questo articolo appartiene al cluster engineering. Inizia con la guida all'API gratuita per accorciare gli URL per la struttura dell'endpoint e l'autenticazione, poi passa all'articolo su limiti di frequenza e idempotenza per comportarti bene sotto carico. Il riferimento aggiornato è la documentazione dell'API e, se hai ereditato link goo.gl, l'articolo sull'alternativa a Google URL Shortener spiega dove portano oggi.

Correlati sul blog

Domande frequenti

Come posso accorciare un URL in PHP?

Invia l'URL lungo con una richiesta POST all'API di un accorciatore, passando la chiave API come header Bearer e la destinazione in un corpo JSON, quindi usa json_decode sulla risposta e leggi il campo short_url. Con curl sono circa quindici righe, oppure tre con Guzzle o con il client HTTP di Laravel se il progetto ne usa già uno.

Come posso accorciare un URL in PHP senza una libreria esterna?

Usa l'estensione curl inclusa in PHP. curl_init, curl_setopt_array, curl_exec e json_decode coprono l'intero flusso senza dipendenze Composer, un aspetto importante sull'hosting condiviso dove non puoi eseguire Composer. Imposta CURLOPT_RETURNTRANSFER su true, altrimenti curl_exec stampa la risposta invece di restituirla.

L'API Google URL Shortener funziona ancora in PHP?

No. Google ha smesso di accettare nuovi link tramite l'API URL Shortener nel 2019 e ha ritirato il servizio; i link goo.gl rimasti inattivi hanno smesso di risolversi il 25 agosto 2025. Qualsiasi tutorial PHP basato su urlshortener/v1 è codice morto, così come gli esempi bit.ly v3 della stessa epoca.

Posso accorciare un URL in WordPress con PHP?

Sì. Chiama wp_remote_post da un hook che scatta alla pubblicazione, quindi salva il link breve restituito con update_post_meta, così il resto del tema può leggerlo. Conserva la chiave API in wp-config.php invece che in un file del plugin o nel database, e controlla is_wp_error sulla risposta perché WordPress restituisce un oggetto WP_Error invece di lanciare un'eccezione.

Come posso accorciare molti URL contemporaneamente in PHP?

Invia le richieste in contemporanea con un pool a dimensione limitata invece di usare un ciclo foreach che attende ogni risposta. Pool di Guzzle con una concorrenza di circa otto è la strada più breve, curl_multi fa lo stesso senza Composer, e una Idempotency-Key derivata da ciascun URL impedisce a una riesecuzione di creare link duplicati.

Perché la mia richiesta PHP curl a un accorciatore di URL restituisce 401 o una risposta vuota?

Un 401 significa che l'header Authorization manca o è malformato: deve essere esattamente 'Authorization: Bearer YOUR_KEY' come singola stringa nell'array CURLOPT_HTTPHEADER. Un valore restituito vuoto di solito significa che CURLOPT_RETURNTRANSFER non è mai stato impostato, quindi il corpo è finito direttamente nell'output e curl_exec ha restituito true invece.

Prova Elido

Incolla un URL, ottieni un link breve

Senza registrazione. Il link vive 30 giorni. Iscriviti per conservarlo.

Gratis, nessuna registrazione richiesta · 2 al giorno

Prova Elido

Accorciatore di URL ospitato nell'UE: domini personalizzati, analisi approfondite e API aperta. Piano gratuito - senza carta di credito.

Tag
how to shorten a url in php
php url shortener
shorten url php curl
url shortener api php
wordpress shorten url php
bulk shorten urls php

Continua a leggere