**TL;DR.** I reindirizzamenti nell'ecommerce riguardano la preservazione dell'equità SEO attraverso le modifiche degli URL. Usa 301 per spostamenti permanenti (correzioni di slug, rebranding). Usa 410 per cancellazioni permanenti. Tieni traccia della cronologia degli slug per entità in modo che gli URL vecchi si reindirizzino automaticamente. Rileva e appiattisci le catene settimanalmente. Mostra le pagine scomparse con contenuti utili, non con un generico 404.

## Codici di stato HTTP per l'ecommerce

| Codice | Nome              | Significato                       | Caso d'uso                                          |
| ------ | ----------------- | --------------------------------- | --------------------------------------------------- |
| 200    | OK                | La pagina esiste                  | Pagina normale di prodotto/categoria                 |
| 301    | Spostato Permanentemente | Usa il nuovo URL per sempre        | Modifiche di slug, rebranding, migrazioni di dominio |
| 302    | Trovato           | Reindirizzamento temporaneo       | Test A/B, geo-reindirizzamenti, interruzioni temporanee |
| 307    | Reindirizzamento Temporaneo | Stesso di 302 ma preserva il metodo | Reindirizzamenti API con POST                       |
| 308    | Reindirizzamento Permanente | Stesso di 301 ma preserva il metodo | Reindirizzamenti API con POST                       |
| 404    | Non Trovato      | La pagina non esiste, potrebbe tornare | Errori di battitura, URL ricordati male            |
| 410    | Scomparso         | Rimosso permanentemente            | Prodotti dismessi, contenuti eliminati               |

Per l'ecommerce, la coppia comune è **301** per spostamenti e **410** per cancellazioni permanenti.

## Quando usare 301

Scenari comuni per 301:

| Evento                          | Modello                                              |
| ------------------------------- | ---------------------------------------------------- |
| Correzione dello slug del prodotto (errore di battitura) | `/products/leather-bagg` → `/products/leather-bag`   |
| Rebranding del prodotto         | `/products/leather-bag` → `/products/heritage-bag`    |
| Ristrutturazione della categoria | `/products/bags-leather` → `/categories/leather-bags` |
| Migrazione di dominio           | `oldbrand.com/...` → `newbrand.com/...`               |
| Modifica dell'URL locale       | `/products/bag` → `/en/products/bag`                  |
| Normalizzazione dello slash finale | `/products/bag/` → `/products/bag`                    |
| HTTP → HTTPS                   | `http://...` → `https://...`                          |

Implementazione in Next.js:

```ts
// next.config.ts
export default {
  async redirects() {
    return [
      {
        source: '/products/leather-bagg',
        destination: '/products/leather-bag',
        permanent: true,  // 301
      },
    ];
  },
};
```

Oppure tramite una tabella di reindirizzamento a runtime:

```ts
// src/middleware.ts or proxy.ts
const redirect = await getRedirect(request.url);
if (redirect) {
  return NextResponse.redirect(redirect.to, redirect.statusCode);
}
```

## Quando usare 410

Un 410 Gone è la risposta giusta quando:

- Un prodotto è stato dismesso permanentemente.
- Un post del blog è stato rimosso per motivi di accuratezza o legali.
- Una categoria è stata fusa con un'altra e il vecchio slug deve morire permanentemente.
- Una pagina è stata intenzionalmente eliminata e non tornerà.

```ts
// Pseudocodice
const gone = await isGonePath(storeId, request.url.pathname);
if (gone) {
  return new Response(renderGonePage(gone.suggestions), {
    status: 410,
    headers: { 'Content-Type': 'text/html' },
  });
}
```

La pagina 410 dovrebbe:

- Renderizzare `<meta name="robots" content="noindex">`.
- Suggerire prodotti simili (usa pgvector cosine sull'embedding dell'entità scomparsa).
- Fornire una casella di ricerca.
- Avere un chiaro messaggio "questo prodotto non è più disponibile".

## Cronologia degli slug per entità

Quando uno slug di prodotto cambia, il vecchio URL deve reindirizzarsi al nuovo URL. Memorizzare questo manualmente è fragile. Tieni traccia della cronologia degli slug per entità:

```sql
CREATE TABLE product_slug_history (
  id SERIAL PRIMARY KEY,
  product_id UUID NOT NULL,
  old_slug TEXT NOT NULL,
  new_slug TEXT NOT NULL,
  changed_at TIMESTAMPTZ DEFAULT NOW()
);
```

All'aggiornamento dello slug:

```ts
async function updateProductSlug(productId: string, newSlug: string) {
  const product = await db.products.findOne({ id: productId });
  const oldSlug = product.slug;

  if (oldSlug === newSlug) return;

  await db.products.update({ id: productId, slug: newSlug });
  await db.productSlugHistory.insert({ productId, oldSlug, newSlug });
  await db.storeRedirects.insert({
    from: `/products/${oldSlug}`,
    to: `/products/${newSlug}`,
    statusCode: 301,
  });
  await enqueueIndexNow([`/products/${oldSlug}`, `/products/${newSlug}`]);
}
```

La tabella della cronologia degli slug ti consente di risalire all'evoluzione dell'URL di un prodotto (utile per il supporto e per archivi legali/compliance).

## Catene di reindirizzamento

Una catena: `A → B → C`.

Perché le catene sono dannose:

- Ogni salto perde il 5–10% di equità dei link (Google ha confermato questo).
- Ogni salto aggiunge 100–500ms di latenza.
- Tre o più salti aumentano la probabilità che Google smetta di tracciare.

Rilevamento: HEAD-traccia ogni reindirizzamento settimanalmente.

```ts
async function traceRedirect(url: string, hops: number = 0): Promise<{ finalUrl: string; chainLength: number }> {
  if (hops > 5) return { finalUrl: url, chainLength: hops }; // guardia di loop

  const res = await fetch(url, { method: 'HEAD', redirect: 'manual' });
  if (res.status >= 300 && res.status < 400) {
    const location = res.headers.get('location');
    if (!location) return { finalUrl: url, chainLength: hops };
    return traceRedirect(location, hops + 1);
  }
  return { finalUrl: url, chainLength: hops };
}
```

Appiattisci le catene: riscrivi la sorgente per puntare direttamente all'URL finale.

```ts
// Prima: A → B → C
await db.redirects.update({ from: '/a', to: '/b' });  // esistente
// Dopo l'appiattimento:
await db.redirects.update({ from: '/a', to: '/c' });  // riscritto
```

## Loop di reindirizzamento

`A → B → A` è fatale. Rileva al momento della scrittura:

```ts
async function writeRedirect(from: string, to: string) {
  const wouldLoop = await detectLoop(from, to);
  if (wouldLoop) {
    throw new Error(`Il reindirizzamento da ${from} a ${to} crea un loop`);
  }
  await db.redirects.insert({ from, to, statusCode: 301 });
}
```

## Ping IndexNow

Dopo aver scritto un reindirizzamento:

```ts
await enqueueIndexNow([oldUrl, newUrl]);
```

Il motore recupera nuovamente il vecchio URL, vede il 301 e aggiorna il suo indice.

## Migliori pratiche

- Mantieni la tabella dei reindirizzamenti compatta. Nel tempo, dozzine di piccole modifiche agli slug creano dozzine di reindirizzamenti. Appiattisci periodicamente le catene.
- Non reindirizzare intere categorie alla homepage. Questo è un 'soft 404' per Google. Reindirizza invece alla categoria equivalente più vicina.
- Evita di reindirizzare a pagine noindex. Contraddice lo scopo di preservare l'equità.
- Documenta l'intento del reindirizzamento. Una colonna di commento nella tabella dei reindirizzamenti aiuta il futuro te a capire il perché.

## Come Ordiko gestisce i reindirizzamenti

- Tabella `storeRedirects` con colonne `from`, `to`, `statusCode`, `tenant`.
- Cronologia degli slug per entità: `productSlugHistory`, `categorySlugHistory`, ecc.
- Scrittura automatica di 301 al cambiamento dello slug.
- `productLifecycle.archive()` scrive una riga 410 in `storeRedirects`.
- Il layer dei percorsi scomparsi rende `/products/[slug]/gone` con raccomandazioni di similarità.
- Il task settimanale di Trigger.dev `redirect-chain-verify.task.ts` HEAD-traccia ogni reindirizzamento e segnala catene e obiettivi rotti.
- Riscrittura "Appiattisci catena" con un clic da A → C direttamente.
- Ping IndexNow su ogni creazione di reindirizzamento.

## FAQ

**Qual è la differenza pratica tra 301 e 302?**
301 = permanente. Trasmette quasi tutta l'equità dei link. Memorizza pesantemente il reindirizzamento. Usa per modifiche di slug, migrazioni di dominio. 302 = temporaneo. Trasmette meno equità. Non memorizza in modo aggressivo. Usa per test A/B o interruzioni temporanee. Per la maggior parte dei reindirizzamenti ecommerce, 301 è corretto.

**Qual è la differenza tra 404 e 410?**
404 = 'non trovato, potrebbe tornare'. Google potrebbe mantenere l'URL nel suo indice per un po' nel caso in cui ritorni. 410 = 'scomparso, rimosso permanentemente'. Google elimina l'URL più rapidamente. Per prodotti dismessi permanentemente, 410 è una migliore igiene SEO.

**Quanti salti di reindirizzamento sono troppi?**
Due salti sono il limite pratico. Ogni salto perde il 5–10% di equità dei link e aggiunge latenza. Tre o più salti sono un segnale. Rilevali e appiattiscili automaticamente.

**Come gestisce Ordiko la cronologia degli slug?**
Ogni tabella di entità ha una corrispondente tabella di cronologia degli slug (ad es., product_slug_history). Al cambiamento dello slug, viene inserita una nuova riga con il vecchio slug; un reindirizzamento 301 viene scritto automaticamente in storeRedirects. Il task settimanale redirect-chain-verify di Trigger.dev HEAD-traccia i reindirizzamenti e segnala catene e obiettivi rotti.