Skip to content
Torna al blog
Tutorial

Codici HTTP: cheat sheet completo (1xx-5xx) con esempi

Riferimento completo dei codici HTTP da 1xx a 5xx con esempi reali, errori comuni (401 vs 403, 301 vs 302) e impatto SEO. Consulta il cheat sheet online ora.

14 min di lettura

Codici HTTP: cheat sheet completo (1xx-5xx) con esempi

Apri i DevTools e la scheda Network è mezza rossa. Il tuo endpoint risponde 502 in produzione, 200 in locale, e su Slack un collega ti chiede: «ma qui ci vuole un 401 o un 403?». I codici di stato HTTP sembrano semplici, tre cifre e cinque categorie, ma scegliere quello sbagliato fa trapelare informazioni, danneggia la SEO e rende infernali i turni di reperibilità.

Questa guida è un cheat sheet dei codici HTTP per chi sviluppa davvero. Trovi tre cose: (1) una tabella di riferimento rapido con tutti i codici che incontri sul campo, (2) matrici decisionali per le coppie che si confondono più spesso (301 vs 302, 401 vs 403, 404 vs 410, 502 vs 504) e (3) una sezione sugli strumenti che mostra come ispezionare i codici di stato da curl, fetch e Python requests. Ogni codice qui sotto si basa sulla RFC 9110, lo standard attuale della semantica HTTP, e sul registro IANA dei codici di stato HTTP.

Riferimento rapido: tutti i codici HTTP a colpo d’occhio

Ecco i codici che incontri in produzione, raggruppati per classe. Aggiungi questa tabella ai preferiti; il resto dell’articolo spiega quelli più insidiosi.

CodiceNomeQuando lo vedrai
100ContinueInvio di un POST con corpo grande con Expect: 100-continue
101Switching ProtocolsHandshake WebSocket, upgrade HTTP/2
103Early HintsIl server invia header Link prima della risposta vera e propria
200OKSuccesso predefinito per GET, PUT, PATCH
201CreatedPOST che crea una risorsa (restituisce Location)
202AcceptedJob asincrono in coda, lavoro ancora non concluso
204No ContentDELETE riuscito, PUT senza corpo da restituire
206Partial ContentRange request, seek di video, download riprendibile
301Moved PermanentlyURL vecchio dismesso, i motori di ricerca trasferiscono il link equity
302FoundRedirect temporaneo, l’URL originale resta canonico
303See OtherPattern Post/Redirect/Get dopo un POST di form
304Not ModifiedGET condizionale con ETag o If-Modified-Since corrispondente
307Temporary RedirectCome 302, ma metodo e corpo vengono preservati
308Permanent RedirectCome 301, ma metodo e corpo vengono preservati
400Bad RequestJSON malformato, campo obbligatorio mancante, schema non valido
401UnauthorizedCredenziali assenti o token scaduto
403ForbiddenAutenticato ma non autorizzato
404Not FoundLa risorsa non esiste (o la stai nascondendo)
405Method Not AllowedPOST su un endpoint solo GET (deve includere Allow)
408Request TimeoutIl client ha impiegato troppo per inviare la richiesta
409ConflictFallimento di optimistic lock, chiave duplicata
410GoneRisorsa cancellata definitivamente, non tornerà
415Unsupported Media TypeContent-Type errato, ad esempio XML su API JSON
422Unprocessable ContentSintassi valida, semantica no (errore di validazione)
425Too EarlyRischio di replay sui dati early-data di TLS 1.3
428Precondition RequiredIl server richiede If-Match per evitare aggiornamenti persi
429Too Many RequestsRate limit superato (deve includere Retry-After)
451Unavailable for Legal ReasonsDMCA, rimozione GDPR, geo-blocco
500Internal Server ErrorEccezione non gestita nel tuo codice
501Not ImplementedMetodo o funzionalità non supportata (raro in REST)
502Bad GatewayL’upstream ha restituito una risposta non valida
503Service UnavailableModalità manutenzione o sovraccarico
504Gateway TimeoutL’upstream non ha risposto in tempo
507Insufficient StorageA WebDAV è finito lo spazio su disco
508Loop DetectedRedirect infinito o ricorsione in WebDAV
511Network Authentication RequiredCaptive portal del WiFi di un hotel o aeroporto

Il resto dell’articolo analizza ogni classe con matrici decisionali, anti-pattern e le conseguenze SEO degli errori.

Come funzionano i codici HTTP (anatomia delle 3 cifre)

Perché tre cifre?

I codici HTTP sono tre cifre decimali perché HTTP/0.9 aveva bisogno di un segnale a larghezza fissa, abbastanza piccolo da essere interpretato rapidamente da un parser e abbastanza ampio da lasciare spazio a nuovi codici. Tre cifre danno 900 valori possibili (100–999), molto più del necessario: il registro IANA ne usa oggi solo una sessantina.

La prima cifra è la classe. La seconda e la terza identificano il codice specifico dentro quella classe. Un client che non riconosce 418 deve gestirlo come un generico 4xx. La RFC 9110 §15 lo dice esplicitamente: i client devono trattare i codici non riconosciuti come l’x00 della loro classe.

Le cinque categorie a colpo d’occhio

ClasseSignificatoCorpo richiesto?Cacheable di default?
1xxInformativo, interim, segue altroNoNo
2xxSuccesso, richiesta compresa e accettataSpessoDipende dal metodo
3xxRedirect, serve un’azione ulterioreOpzionale301, 308 sì; 302, 307 no
4xxErrore client, colpa tua, sistema la richiestaSì (con spiegazione)Di solito no
5xxErrore server, colpa nostra, un retry può aiutareSì (con spiegazione)No

La colonna «cacheable di default» è importante. CDN e browser cachano 301 e 308 in modo aggressivo e per sempre: scegliere il codice di redirect sbagliato in produzione è difficile da annullare, perché gli utenti hanno il redirect cachato in locale. Torneremo su questo nella sezione SEO.

Se vuoi approfondire la struttura degli URL (su cui operano i codici di redirect), Codifica e decodifica URL ti guida tra percent-encoding, query string e la pipeline a livello di byte che determina cosa renda valido un URL.

1xx, informativi (quando li vedrai davvero)

La maggior parte degli sviluppatori passa anni senza vedere direttamente un 1xx. Sono risposte interim: il server sta dicendo al client «sono ancora qui, vai avanti». I DevTools del browser di solito li nascondono e la maggior parte delle librerie HTTP li accorpa nella risposta finale.

Per ciascun codice qui sotto, il riferimento MDN sui codici di stato HTTP è il più chiaro se vuoi una seconda opinione su una definizione.

100 Continue

Il client invia Expect: 100-continue negli header e attende prima di trasmettere un corpo di richiesta grande. Il server risponde 100 Continue se è disposto ad accettare il corpo, oppure un 4xx se la richiesta verrà comunque rifiutata. Così si risparmia banda sugli upload pesanti: non ha senso inviare 200 MB se il server li rifiuterà per un header mancante.

curl -v -H "Expect: 100-continue" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @big-file.bin \
  https://api.example.com/upload

Se non vedi < HTTP/1.1 100 Continue nell’output verboso, probabilmente il client ha rimosso l’header oppure il server non lo supporta.

101 Switching Protocols

L’handshake che trasforma una connessione HTTP in una connessione WebSocket o HTTP/2. Il client invia Upgrade: websocket, il server risponde 101 Switching Protocols e da quel momento la connessione parla un protocollo diverso. Lo vedi nella scheda Network di qualsiasi app di chat, dashboard live o strumento di collaborazione.

103 Early Hints

Un codice relativamente recente (RFC 8297, 2017) che permette al server di inviare header Link per i preload hint prima che la risposta principale sia pronta. Il browser inizia a recuperare CSS e JS mentre il server sta ancora generando la pagina. Nel 2026 Cloudflare, Fastly e Vercel supportano 103 in produzione: è l’alternativa attuale allo HTTP/2 server push (che è stato deprecato in Chrome).

HTTP/1.1 103 Early Hints
Link: </styles.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script

HTTP/1.1 200 OK
Content-Type: text/html
...

Anti-pattern check. Se il tuo client non vede mai i codici 1xx quando te li aspetti, di solito il colpevole è un reverse proxy. Le versioni più vecchie di nginx eliminano Expect: 100-continue e 103 Early Hints. Controlla la configurazione del proxy prima di dare per scontato che il server sia rotto.

2xx, successo (oltre il semplice 200)

Restituire 200 OK per tutto è il code-smell più comune nelle API REST. La famiglia 2xx porta con sé informazioni semantiche che rendono i client più intelligenti e le cache più efficienti.

200 OK

Il default. Una GET restituisce la risorsa, una PUT restituisce la risorsa aggiornata (oppure 204), una PATCH restituisce la risorsa modificata. Se non hai motivi per usare un codice più specifico, usa 200.

201 Created

Una POST che crea una nuova risorsa dovrebbe restituire 201 insieme a un header Location che punta alla nuova risorsa. È così che i client RESTful scoprono l’URL canonico della cosa che hanno appena creato.

HTTP/1.1 201 Created
Location: /api/users/42
Content-Type: application/json

{"id": 42, "name": "Ada Lovelace"}

202 Accepted

Il server ha accettato la richiesta ma non ha finito l’elaborazione. Usalo per il lavoro asincrono: il client dovrebbe fare polling, sottoscrivere un webhook o controllare un endpoint di stato. Abbinalo a un job ID nel corpo.

204 No Content

Successo, nessun corpo. Tipico per DELETE (la risorsa non c’è più, cosa restituiresti?) e per le operazioni PUT in cui il client conosce già il nuovo stato. I browser non cambiano la pagina corrente se l’invio di un form risponde con 204: utile per azioni fire-and-forget nelle single-page app.

206 Partial Content

Restituito per le range request: il client ha chiesto i byte da 1000 a 2000 con un header Range: bytes=1000-2000 e il server ha risposto con quella sola fetta. Streaming video, download riprendibili e sincronizzazione di file via HTTP si basano su 206.

Decisione: 200 vs 201 vs 204 per le POST

ScenarioCodiceCorpo
POST crea una nuova risorsa201 CreatedNuova risorsa (o solo ID) + Location
POST avvia lavoro asincrono, risultato non pronto202 AcceptedJob ID, URL di polling
POST è un’azione senza risorsa (es. /login)200 OKRisultato dell’azione (token, stato)
POST riuscita ma risposta vuota204 No Content(nessuno)

Se sei indeciso tra 200 e 201, chiediti: «il server ha creato una risorsa che ora ha un proprio URL?». Se sì, 201. Se no, 200.

3xx, redirect (301 vs 302 vs 307 vs 308)

I redirect sono la classe usata peggio. Le differenze tra 301, 302, 307 e 308 si riducono a tre domande ortogonali: il trasferimento è permanente, il metodo viene preservato, la risposta è cacheable.

301 Moved Permanently

La risorsa si è spostata e non tornerà. I motori di ricerca trasferiscono il link equity al nuovo URL. Browser e CDN cachano 301 a tempo indefinito: se redirezioni /old su /new con un 301 e poi cambi idea, gli utenti con il redirect in cache continueranno ad andare su /new per sempre (o finché non svuotano la cache).

Storicamente i browser potevano riscrivere il metodo della richiesta su un 301 (POST → GET): è proprio per questo che HTTP/1.1 ha introdotto 308.

302 Found

Redirect temporaneo. L’URL originale resta canonico: i motori di ricerca dovrebbero continuare a indicizzare l’originale. Usalo per il routing degli A/B test, le pagine di manutenzione o i flussi «accedi per continuare».

Come per 301, storicamente i browser riscrivevano POST in GET su 302. Se devi redirezionare una POST mantenendola POST, usa invece 307.

303 See Other

Riscrive sempre il metodo in GET. Il pattern Post/Redirect/Get: il form fa POST su /submit, il server risponde 303 con Location: /thank-you, il browser fa una GET /thank-you. Ricaricando la pagina di ringraziamento il form non viene reinviato. È esattamente lo scopo per cui 303 è stato pensato.

304 Not Modified

La risposta condizionale. Il client invia If-None-Match: "abc123" (o If-Modified-Since), il server controlla se la risorsa è cambiata e, se non lo è, risponde 304 senza corpo. Il browser usa la sua copia in cache. È così che CDN e livelli di caching mantengono veloce il tuo sito.

307 Temporary Redirect

Come 302, ma il metodo non deve cambiare. POST resta POST, il corpo viene preservato. Usalo quando vuoi un redirect temporaneo su una richiesta non-GET.

308 Permanent Redirect

Come 301, ma il metodo non deve cambiare. La scelta più sicura per i redirect permanenti su API che accettano POST/PUT.

Matrice decisionale: quale codice di redirect?

Permanente (cache per sempre)Temporaneo (non cachare)
Il metodo può diventare GET301 Moved Permanently302 Found
Il metodo deve restare lo stesso308 Permanent Redirect307 Temporary Redirect

Caso speciale: se vuoi specificamente POST → GET (il pattern Post/Redirect/Get), usa 303 See Other.

Per le pagine HTML con la navigazione del browser, 301 e 302 di solito vanno bene perché GET resta GET. Per API e form, preferisci 308 e 307 per evitare riscritture inattese del metodo.

4xx, errori del client (scegliere quello giusto)

4xx significa che il client ha sbagliato qualcosa. Più ricco è il tuo vocabolario 4xx, più semplice è usare la tua API: i client possono ramificarsi sul codice invece di fare il parse di stringhe d’errore.

400 Bad Request

Errore di sintassi generico. JSON malformato, campo obbligatorio mancante a livello strutturale, richiesta che il server non riesce nemmeno a interpretare. Se la richiesta viene parsata ma fallisce la validazione di business, preferisci 422.

401 Unauthorized vs 403 Forbidden

La coppia più confusa di tutto HTTP. La distinzione è semplice una volta vista:

  • 401 Unauthorized: la richiesta non porta un’autenticazione valida. Il server non sa chi sei. Reinviare le credenziali (o rinfrescare il token) potrebbe risolvere. La risposta deve includere un header WWW-Authenticate come da RFC 9110 §15.5.2.
  • 403 Forbidden: il server sa chi sei e si rifiuta comunque. Reinviare la richiesta non aiuta. Servono credenziali diverse o permessi diversi.
Cosa vediCosa significa
401 con WWW-Authenticate: BearerToken assente, scaduto o non valido
403 dopo un login riuscitoSei autenticato, ma questo utente non può accedere a questa risorsa
401 dopo un login riuscitoBug, probabilmente volevi 403

Anti-pattern: 403-as-404. Alcuni siti restituiscono 403 quando un utente non autenticato richiede /admin/dashboard. Così facendo si rivela l’esistenza di /admin/dashboard. GitHub aggira il problema restituendo 404 per i repository privati di cui non sei membro: dal tuo punto di vista la risorsa «non esiste». È una scelta deliberata di occultamento delle informazioni, non un bug.

404 Not Found vs 410 Gone

Entrambi dicono «questa risorsa non c’è». La differenza sta nella permanenza e nella SEO.

  • 404 Not Found: potrebbe esistere, potrebbe no, potrebbe tornare. I motori di ricerca continueranno a controllare.
  • 410 Gone: c’era, è stata rimossa volontariamente, non tornerà. I motori la tolgono dall’indice molto più in fretta.

Se cancelli una pagina prodotto e vuoi farla sparire ora dall’indice di Google, 410 è la scelta giusta. Se un URL è solo temporaneamente rotto, 404 va bene.

405 Method Not Allowed

L’URL esiste ma non accetta questo metodo. La risposta deve includere un header Allow con la lista dei metodi supportati.

HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, OPTIONS
Content-Type: application/json

{"error": "POST is not allowed on this endpoint"}

Dimenticare l’header Allow è la violazione di contratto numero 1 nelle API REST scritte a mano.

408 Request Timeout

Il client ha iniziato a inviare una richiesta e poi è andato in silenzio. Il server ha rinunciato. Diverso da 504 Gateway Timeout, che riguarda l’upstream: 408 significa «tu, il client, hai impiegato troppo».

409 Conflict

La richiesta è in conflitto con lo stato corrente. Uso più comune: optimistic locking. Il client invia If-Match: "etag-v3" e l’ETag corrente del server è "etag-v4", quindi l’aggiornamento viene rifiutato con 409.

410 Gone

Vedi sopra: cancellazione permanente. Utile per rimuovere dagli indici di ricerca i record cancellati in soft-delete.

415 Unsupported Media Type

Il client ha inviato un corpo che il server non capisce. Una POST di XML su un’API solo JSON ottiene 415. La risposta dovrebbe suggerire i tipi accettabili.

422 Unprocessable Content

La richiesta viene parsata correttamente, ma fallisce la validazione semantica. Nel 2022 la RFC 9110 ha finalmente promosso questo codice da WebDAV alla specifica core. Usa 422 per gli errori di validazione:

{
  "error": "validation_failed",
  "details": [
    {"field": "email", "message": "must be a valid email"},
    {"field": "age", "message": "must be at least 13"}
  ]
}

Se la tua API è indecisa tra 400 e 422, la regola pratica è: 400 per «non riesco nemmeno a interpretarlo», 422 per «l’ho interpretato e non ha senso».

425 Too Early

Inviato quando il server non vuole rischiare di processare una richiesta che potrebbe essere un replay di early-data TLS 1.3. Rilevante soprattutto per CDN e reverse proxy.

428 Precondition Required

Il server pretende che tu invii If-Match o If-Unmodified-Since per evitare il problema dell’aggiornamento perso. Usato nelle API di editing collaborativo.

429 Too Many Requests

Rate limit superato. La risposta deve includere Retry-After (in secondi o come data HTTP) in modo che i client educati possano fare back-off.

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json

{"error": "rate_limited", "limit": 100, "window": "1m"}

Il numero è una citazione di Bradbury. Il caso d’uso non è fantascientifico: takedown DMCA, rimozioni per il diritto all’oblio del GDPR e geo-blocchi a livello nazionale giustificano tutti 451. La risposta dovrebbe includere un header Link che punti all’autorità legale che impone il blocco, secondo la RFC 7725.

418 I’m a Teapot (l’easter egg)

Sì, esiste davvero. La RFC 2324 (pesce d’aprile 1998) e l’IETF lo hanno mantenuto a registro perché troppi prodotti lo avevano implementato per scherzo. Non spedire 418 in un’API reale: la maggior parte di reverse proxy e load balancer lo gestisce male.

Matrice decisionale: quale 4xx?

SituazioneCodice
Corpo malformato o non parsabile400
Nessuna autenticazione / token scaduto401
Autenticato ma non autorizzato403
L’URL non esiste (o lo stai nascondendo)404
L’URL esisteva, è stato rimosso volontariamente410
Metodo HTTP errato405 (con Allow)
Content-Type errato415
Conflitto di optimistic lock409
Errore di validazione (parse ok, validazione no)422
Rate limit superato429 (con Retry-After)
Bloccato per ragioni legali451

5xx, errori del server (cosa è davvero rotto)

5xx è «colpa nostra». Chi è di reperibilità si interessa soprattutto di quale 5xx lo ha svegliato alle 3 di notte, perché il codice ti dice quale livello indagare per primo.

500 Internal Server Error

Il jolly. Quasi sempre significa che un’eccezione non gestita è risalita fino all’handler di default del framework. Non ti dice nulla sulla causa, ed è per questo che qui un logging strutturato conta più del codice di stato.

501 Not Implemented

Il server non supporta proprio quel metodo. Diverso da 405 (questo metodo non è ammesso su questo URL): 501 dice «questo server non sa nemmeno cosa significhi PROPFIND». Raro nelle API REST.

502 Bad Gateway

Un reverse proxy o load balancer ha ricevuto una risposta non valida dall’upstream. L’upstream ha risposto, ma con immondizia: protocollo errato, header malformati, connessione persa a metà risposta. Se vedi 502 dalla tua CDN, l’origine probabilmente sta crashando o restituisce corpi troncati.

503 Service Unavailable

Il server non sta volutamente servendo richieste in questo momento. Usalo per finestre di manutenzione o risposte di sovraccarico controllato. Dovrebbe includere Retry-After.

504 Gateway Timeout

Il reverse proxy ha aspettato l’upstream e l’upstream non ha mai risposto in tempo. L’upstream è lento o bloccato; diverso da 502, dove l’upstream ha risposto con immondizia.

502 vs 504: la diagnosi di reperibilità

Cosa vediPrima cosa da controllare
502 Bad GatewayL’upstream risponde con dati non validi: controlla i log dell’origine per crash, risposte malformate, mismatch di protocollo
504 Gateway TimeoutL’upstream è bloccato: controlla CPU dell’origine, query DB, chiamate API a valle e il proxy_read_timeout del proxy

Confusione comune: una query al database che impiega 60 secondi viene fuori come 504 se il proxy va in timeout a 30 secondi, ma come 500 se l’app server va in timeout a 90 secondi e solleva un’eccezione. Stessa causa principale, codice diverso, riga di log diversa: addestra le tue dashboard a mostrarli entrambi.

507 Insufficient Storage

Specifico di WebDAV. Disco pieno sul server. Se lo vedi da un’API non-WebDAV, qualcuno ne sta forzando il significato.

508 Loop Detected

Ricorsione infinita nelle operazioni PROPFIND di WebDAV. Molto raro.

511 Network Authentication Required

Codici da captive portal: il WiFi di un hotel o aeroporto invia 511 per dire al browser «prima devi accedere al portale». La risposta include un Location che punta alla pagina del portale.

Matrice di troubleshooting: quale livello controllare per primo

CodiceAppProxyDBRete
500Forse (errore DB non gestito)
502Sì (upstream malformato)Forse (TCP reset)
503Sì (flag di manutenzione)Sì (rate-limit reject)
504Sì (handler lento)Sì (config di timeout)Sì (query lenta)Sì (DNS, perdita pacchetti)

Anti-pattern comuni dei codici HTTP

Questi cinque errori spiegano la maggior parte del codice scadente che mi capita di rivedere.

1. Avvolgere gli errori in un 200 OK

HTTP/1.1 200 OK
{"success": false, "error": "user_not_found"}

Tutti gli strumenti di monitoraggio, le CDN e le cache pensano ora che la richiesta sia andata a buon fine. La logica di retry fallisce. I load balancer status-code-aware instradano traffico difettoso verso backend «sani». Questo pattern arriva da JSON-RPC ed è stato ereditato da GraphQL: GraphQL lo fa perché i successi parziali richiedono una segnalazione di errore per campo, ed è legittimo. REST non ha scuse: usa 4xx per gli errori del client, 5xx per quelli del server e metti il dettaglio strutturato nel corpo.

2. Mescolare 401 e 403

Se i tuoi 401 e 403 non sono coerenti, un attaccante può sondare la tua API per scoprire quali risorse esistono. Scegli una policy: o restituisci 404 per «non puoi vederlo» (l’approccio di GitHub per i repo privati) oppure restituisci 403 in modo coerente. L’incoerenza fa trapelare informazioni.

3. Nascondere 403 dietro 404

A volte è corretto, spesso è un bug. GitHub che restituisce 404 per i repo privati è deliberato: l’esistenza stessa del repo è un’informazione sensibile. Ma se la tua API restituisce 404 per «questo account utente è sospeso», gli utenti legittimi non riescono a capire se hanno digitato male l’username o se sono stati sospesi. Documenta la tua policy esplicitamente e applicala con coerenza.

4. Usare 500 come catch di default

I framework lo rendono facile, e il problema è proprio quello. Ogni eccezione non gestita diventa 500 e il tuo sistema di alerting non distingue tra «il database è giù» e «l’utente ha passato un UUID malformato». Cattura gli errori di validazione e solleva 400 o 422. Cattura NotFound dal tuo ORM e solleva 404. Riserva 500 ai fallimenti davvero inattesi, e quando lo sollevi, logga un request ID per correlare.

5. Catene di redirect lunghe

Ogni hop costa un round trip. Se /old/intermediate/canonical, sono due lookup DNS aggiuntivi e due handshake TCP aggiuntivi (caso peggiore). Google declassa esplicitamente la priorità di crawl per le catene più lunghe di 3 hop, e i browser limitano le catene di redirect a circa 20 per prevenire i loop. Comprimi le catene alla sorgente: la config della CDN o la mappa di redirect dell’applicazione.

Codici HTTP e SEO

I motori di ricerca trattano i codici di stato come segnali autorevoli su quale URL mantenere, eliminare o trasferire. Sbagli e le tue posizioni si muovono.

301 Moved Permanently trasferisce il PageRank: Google considera il nuovo URL come destinazione canonica di tutti i segnali che puntavano al vecchio URL. 302 Found non trasferisce il link equity (o lo fa lentamente, a seconda delle euristiche di Google). Se hai rinominato un URL per sempre, usa 301. Se redirezioni un ospite verso /login, usa 302.

404 vs 410 vs Soft 404

Google distingue tre stati di «mancante»:

  • 404 Not Found: Google ricontrolla periodicamente e tiene l’URL nell’indice ancora per un po’.
  • 410 Gone: Google elimina l’URL più velocemente, spesso entro un singolo ciclo di crawl.
  • Soft 404: il termine di Google per una pagina che restituisce 200 OK ma mostra un messaggio di «non trovato». Google lo individua dai pattern del contenuto e lo tratta comunque come 404, ma intanto hai sprecato una richiesta di crawl e magari diluito i tuoi contenuti reali.

Se stai pulendo un indice obsoleto, restituisci veri 410 per gli URL rimossi in modo permanente.

5xx e crawl budget

Il crawler di Google riduce il proprio ritmo quando un sito restituisce 5xx persistenti. Il report Crawl Stats di Search Console lo mostra: un picco prolungato di errori 5xx può tagliare il tuo crawl budget per giorni, il che significa che le pagine nuove impiegheranno più tempo a essere indicizzate. Tratta il tasso di 5xx come una metrica SEO, non solo come una metrica di affidabilità.

Un 200 OK che in realtà è rotto

Restituire 200 OK con una pagina di errore (l’anti-pattern del soft-404) è il peggior caso possibile per la SEO. Google indicizza il messaggio di errore, lo posiziona per niente e capisce solo dopo molto tempo che la pagina è rotta. Restituisci sempre il codice di stato corretto dal server, anche se la tua single-page app mostra una UI di errore curata.

Come ispezionare i codici HTTP (strumenti)

Non puoi sistemare ciò che non vedi. Ogni sviluppatore in attività dovrebbe padroneggiare almeno tre di questi strumenti.

Pannello Network dei DevTools del browser

Chrome, Firefox e Safari mostrano una colonna Status nella scheda Network. Fai clic destro sull’intestazione della colonna per aggiungere Status Text se non è visibile. Trucchi utili:

  • Preserve log: mantiene le voci tra una navigazione e l’altra in modo da vedere l’intera catena di redirect.
  • Filtra per status: digita status-code:5xx (Chrome) per vedere solo gli errori del server.
  • Replay XHR: clic destro su una richiesta → Replay XHR per rieseguirla senza ricaricare la pagina.

Per i redirect, espandi la richiesta per vedere ogni hop e il codice di stato a ciascun passaggio.

curl (la risposta universale)

curl mostra tutto. Tre pattern che risolvono il 90% del debugging:

# Solo il codice di stato
curl -o /dev/null -s -w "%{http_code}\n" https://api.example.com/users/1

# Solo gli header (richiesta HEAD, segui i redirect)
curl -I -L https://example.com

# Verboso completo con header di richiesta e di risposta
curl -v https://api.example.com/users/1

Quando costruisci URL di test con caratteri speciali nelle query string, usa --data-urlencode per lasciare a curl la gestione dell’encoding, oppure incolla l’URL nel nostro Codificatore e Decodificatore URL per verificare quali byte finiranno davvero sulla rete.

# curl codifica per te il valore della query
curl -G "https://api.example.com/search" \
  --data-urlencode "q=hello world & friends"

# Sends: GET /search?q=hello%20world%20%26%20friends

JavaScript fetch

La proprietà Response.status contiene il codice intero. Response.ok è true per ogni 2xx.

const res = await fetch('https://api.example.com/users/1');

console.log(res.status);      // 200
console.log(res.statusText);  // "OK"
console.log(res.ok);          // true

if (!res.ok) {
  if (res.status === 401) {
    // refresh token and retry
  } else if (res.status === 429) {
    const retryAfter = Number(res.headers.get('Retry-After')) || 1;
    await new Promise(r => setTimeout(r, retryAfter * 1000));
  } else if (res.status >= 500) {
    throw new Error(`Server error: ${res.status}`);
  }
}

In axios la stessa logica vive negli interceptor:

import axios from 'axios';

axios.interceptors.response.use(
  response => response,
  error => {
    const status = error.response?.status;
    if (status === 401) {
      // redirect to login
    }
    return Promise.reject(error);
  }
);

Python requests

import requests

r = requests.get('https://api.example.com/users/1')

print(r.status_code)  # 200
print(r.reason)       # 'OK'

# Raises requests.exceptions.HTTPError for 4xx/5xx
r.raise_for_status()

# Manual handling
if r.status_code == 429:
    retry_after = int(r.headers.get('Retry-After', '1'))
    time.sleep(retry_after)
elif 500 <= r.status_code < 600:
    raise RuntimeError(f'Server error: {r.status_code}')

raise_for_status() è l’idioma Python per «fallisci a voce alta su 4xx/5xx». Usalo negli script in cui vuoi che gli errori sollevino eccezioni invece di ramificare su status_code.

Postman e Bruno

Entrambi permettono di fare assert sui codici di stato dentro uno script di test:

// Postman/Bruno test script
pm.test("Status is 201", () => {
  pm.response.to.have.status(201);
});

pm.test("Has Location header", () => {
  pm.expect(pm.response.headers.get('Location')).to.match(/^\/users\/\d+$/);
});

Eseguili in CI contro lo staging per intercettare le violazioni di contratto prima della produzione.

FAQ

Qual è la differenza tra 401 e 403?

401 Unauthorized significa che il server non sa chi sei: le tue credenziali sono mancanti, scadute o non valide. 403 Forbidden significa che il server sa chi sei e si rifiuta comunque. Se inviare credenziali diverse potrebbe risolvere il problema, usa 401. Altrimenti usa 403.

Quando usare 301 invece di 302?

Usa 301 quando lo spostamento è permanente: il vecchio URL non tornerà mai più e vuoi che i motori di ricerca trasferiscano il link equity al nuovo URL. Usa 302 per redirect temporanei in cui l’URL originale è ancora canonico (flussi di login, A/B testing, pagine di manutenzione). Per le API, preferisci 308 e 307 perché preservano il metodo della richiesta.

Cosa significa un errore 502 Bad Gateway?

502 significa che un reverse proxy o load balancer ha ricevuto una risposta non valida dal server upstream. L’upstream ha risposto, ma con immondizia: protocollo errato, header malformati o connessione interrotta. È diverso da 504 Gateway Timeout, dove l’upstream non ha risposto affatto. Primo posto da controllare: i log del server di origine, in cerca di crash o risposte troncate.

Cos’è un «soft 404»?

Un «soft 404» è una pagina che restituisce 200 OK ma in realtà mostra un messaggio «non trovato». Google li individua in modo euristico e li tratta comunque come 404. Sprecano crawl budget e possono diluire i tuoi contenuti reali. Restituisci sempre veri codici di stato 404 o 410 dal server, anche se la tua single-page app mostra una UI di errore curata.

Quando usare 422 invece di 400?

Usa 400 Bad Request quando il server non riesce nemmeno a interpretare la richiesta: JSON malformato, campi strutturali mancanti, errori di sintassi. Usa 422 Unprocessable Content quando la richiesta viene parsata correttamente ma non supera la validazione di business: formato di email non valido, valore fuori range, campi semanticamente incoerenti. La regola breve: 400 per la sintassi, 422 per la semantica.

Come si risponde a un 429 Too Many Requests?

Leggi l’header Retry-After (un numero di secondi o una data HTTP) e fai back-off per almeno quel tempo prima di riprovare. Se Retry-After manca, usa exponential backoff con jitter partendo da circa 1 secondo. Non riprovare mai subito: è così che ti fai bannare.

I codici informativi 1xx sono ancora usati nel 2026?

Sì, ma la maggior parte è invisibile al codice applicativo. 100 Continue e 101 Switching Protocols sono funzionalità di base di HTTP/1.1. 103 Early Hints viene usato sempre più da Cloudflare, Fastly e Vercel per inviare i preload hint prima della risposta principale, e migliora visibilmente il Largest Contentful Paint. La maggior parte delle librerie HTTP accorpa i 1xx nella risposta finale, quindi di solito li vedi solo nei DevTools o in curl -v.

Il 418 «I’m a teapot» è davvero un codice di stato?

Sì, sorprendentemente. La RFC 2324 era un pesce d’aprile del 1998, ma abbastanza prodotti l’avevano implementata da convincere l’IETF a tenerla a registro nella RFC 7168. Non spedire 418 in produzione: molti reverse proxy e load balancer non lo gestiscono correttamente, e fuori dallo scherzo non serve a nulla.

Articoli correlati

Vedi tutti gli articoli