Basi della sicurezza
Supabase "permission denied for table": il grant mancante
Dal 30 ottobre una nuova tabella di Supabase risponde "permission denied for table" finché non concedi l'accesso. Il grant della mail è metà della soluzione.

In breve
- Dal 30 ottobre 2026 una nuova tabella del tuo progetto Supabase risponde "permission denied for table" finché qualcuno non le concede l'accesso. Le tabelle che hai già continuano a funzionare esattamente come oggi.
- Un grant decide se una richiesta arriva a una tabella. Row Level Security continua a decidere quali righe si porta via. Dare ad anon l'accesso a una tabella senza regole rende ogni riga leggibile da chiunque.
- Il cambiamento non chiude nulla che sia già aperto. Controlla cosa può leggere oggi uno sconosciuto, e di nuovo dopo ogni tabella che aggiungi.
Se la tua app gira su Supabase, probabilmente ti è arrivata una mail sul 30
ottobre. Dice che per le tabelle che hai già non cambia nulla, e subito dopo ti
consegna tre istruzioni SQL. Oppure è già novembre: hai chiesto a Lovable, Bolt
o Cursor una funzione nuova, la schermata nuova è rimasta vuota, e da qualche
parte nella console c'è un messaggio che dice permission denied for table.
Sono lo stesso cambiamento di Supabase, visto dai due lati della data.
Ecco la parte che la mail lascia fuori: l'SQL che ti mostra è metà della soluzione. Esegui quella metà da sola, sulla tabella sbagliata, e la tua app torna a funzionare mentre la tabella nuova consegna le sue righe a chiunque le chieda.
Cosa significa "permission denied for table" in Supabase?
Significa che il tipo di visitatore che fa la richiesta non ha alcun accesso a quella tabella, quindi il database l'ha rifiutata prima di guardare una sola riga.
Supabase assegna ogni richiesta a un ruolo, cioè il tipo di visitatore da
cui arriva. anon è qualcuno che non ha effettuato l'accesso. authenticated è
qualcuno che l'ha fatto. service_role è il tuo codice lato server che usa la
chiave segreta. Un grant è l'istruzione che dà a uno di quei ruoli l'accesso
a una tabella in Postgres, il database su cui gira Supabase. Niente grant,
niente accesso, qualunque altra cosa tu abbia scritto.
Quando manca il grant, Supabase risponde così, di solito come un 401 o un 403:
{
"code": "42501",
"message": "permission denied for table comments",
"hint": "Grant the required privileges to the current role with: GRANT SELECT ON public.comments TO anon;"
}
Il suggerimento è la parte utile. Nomina il ruolo che è stato respinto e l'istruzione esatta che lo farebbe entrare.
Pensa a ogni tabella come a una stanza. Il grant è la porta, e c'è una porta diversa per ogni ruolo. Row Level Security, le regole per riga che forse hai già incontrato, decide quali cassetti un visitatore può aprire una volta dentro. "Permission denied for table" vuol dire che qualcuno è fermo davanti a una porta chiusa. Non è mai arrivato fino alle tue regole.
Cosa cambia il 30 ottobre?
Le nuove tabelle nello schema public, la cartella di tabelle che il tuo
builder usa se non gli dici altro, cominciano ad arrivare con le porte chiuse.
Finora Supabase apriva automaticamente tutte e tre le porte su ogni nuova
tabella: leggere, aggiungere, modificare e cancellare, per anon,
authenticated e service_role allo stesso modo. Una tabella era raggiungibile
dalla tua app dal momento in cui esisteva, e le tue regole di Row Level Security
erano l'unica cosa fra uno sconosciuto e le sue righe. Il
changelog
di Supabase spiega il motivo senza giri di parole: agenti e piattaforme di IA
oggi creano tabelle senza che nessuno riveda la modifica, e i grant automatici
esponevano tabelle che "a developer forgot to protect", che qualcuno aveva
dimenticato di proteggere.
| Data | Cosa è successo o succede |
|---|---|
| 28 aprile 2026 | I nuovi progetti potevano rinunciare ai grant automatici al momento della creazione. |
| 30 maggio 2026 | "Niente grant automatici" ha cominciato a diventare il default per i nuovi progetti. |
| 30 ottobre 2026 | Anche i progetti esistenti smettono di riceverli. |
Se il tuo progetto è stato creato dopo la fine di maggio, potrebbe già funzionare così, perché Supabase ha esteso il nuovo default ai progetti nuovi nelle settimane successive a quella data.
Tre dettagli che conviene conoscere prima della data:
- Solo lo schema
public. Storage e l'accesso degli utenti tengono le loro tabelle in schemi propri,storageeauth, e Supabase dice che i loro grant e le loro impostazioni predefinite restano come sono. - Anche la chiave segreta viene respinta. I grant automatici coprivano anche
service_role, quindi una edge function (codice lato server che Supabase esegue per te) che usa la tua chiave segreta riceve lo stesso errore su una tabella nuova finchéservice_rolenon ha un grant tutto suo. - Una tabella cancellata e ricreata è una tabella nuova. I grant appartengono alla tabella stessa, e se ne vanno con lei quando viene cancellata. Se il tuo builder ricostruisce una tabella per modificarla, quella ricostruita parte con la porta chiusa.
La mia app si romperà il 30 ottobre?
Non quel giorno. Si rompe la prima volta che qualcosa crea una tabella nuova senza creare anche il grant.
Per la maggior parte di chi legge, quel qualcosa è il builder. Chiedi una
sezione commenti. Il builder scrive una migrazione, cioè il file di modifiche al
database che esegue per te, e la migrazione crea una tabella comments. Se
scrive anche i grant, questo errore non lo vedrai mai. Se non lo fa, la
schermata dei commenti non mostra nulla, oppure salvare un commento fallisce,
oppure compare un messaggio rosso, a seconda di come la tua app gestisce un
errore. Tutte le schermate che avevi già continuano a funzionare.
Supabase pubblica una agent skill per gli strumenti di programmazione con IA che include il passaggio del grant. Se il tuo builder la usi dipende dal tuo builder, quindi l'ipotesi prudente è che la prossima tabella che crea possa arrivare con la porta chiusa.
È sicuro eseguire il GRANT che suggerisce l'errore?
Solo quando la tabella ha già Row Level Security acceso e una regola scritta. Il grant decide chi passa dalla porta. Niente in lui decide quali cassetti apre.
La chiave con cui la tua app fa le sue richieste a Supabase viaggia dentro la
tua app, quindi un grant ad anon significa che qualsiasi visitatore, con o
senza accesso, può ora chiedere righe a quella tabella. Con una regola al suo
posto riceve le righe che la regola permette. Con Row Level Security spento
riceve ogni riga della tabella, e nella tua app non cambierà nulla di visibile.
La mail mostra tre grant e si ferma lì. Il changelog di Supabase mostra tre passaggi, e dice di trattarli come un'unica cosa ("treat these three steps as a unit"):
-- 1. chi può raggiungere la tabella
grant select on public.orders to authenticated;
grant select, insert, update, delete on public.orders to service_role;
-- 2. accendere le regole per riga
alter table public.orders enable row level security;
-- 3. la regola vera e propria
create policy "Customers read their own orders"
on public.orders
for select
to authenticated
using (auth.uid() = user_id);
In quell'esempio non c'è nessuna riga per anon, ed è voluto. Nessuno senza
accesso dovrebbe raggiungere una tabella di ordini, quindi la porta per gli
sconosciuti resta chiusa, e i clienti autenticati ricevono solo la lettura che
serve alla loro schermata. Poi la regola la restringe alle loro righe. Il
consiglio di Supabase è di
concedere a ogni ruolo il minimo che gli serve.
Un grant ad anon ha senso su una tabella le cui righe sono per tutti, come i
commenti sotto un post pubblico.
Se vuoi vedere da che parte di quel disegno stanno le tue tabelle, la nostra scansione gratuita fa alla tua app online la domanda che farebbe uno sconosciuto, conta le righe che riceverebbe senza scaricarne nessuna, e ci mette circa 20 secondi senza account: scansiona la tua app.
Perché le correzioni di Row Level Security non toccano questo errore
Perché il grant viene controllato per primo. Una richiesta respinta alla porta non arriva mai alle tue regole, quindi cambiare le regole non cambia nulla dell'errore.
Conta perché il codice è condiviso. 42501 è anche quello che Postgres
restituisce quando Row Level Security rifiuta un salvataggio, come in
new row violates row-level security policy.
Un assistente che si basa solo sul codice può ricorrere alle correzioni di
quell'altro errore: spegnere Row Level Security, oppure aggiungere una regola
using (true), che fa passare tutti. Nessuna delle due fa sparire questo
errore. Tutte e due restano lì quando il grant finalmente arriva, e allora la
porta è aperta su cassetti senza serratura.
Le parole del messaggio distinguono i due casi, anche se il numero non lo fa:
| Il messaggio dice | Quale serratura ha respinto | Cosa lo risolve |
|---|---|---|
permission denied for table | la porta (il grant) | un grant per il ruolo che il suggerimento nomina |
new row violates row-level security policy | le regole | una policy che permetta quella riga |
| nessun errore, e la lista è vuota | le regole | una policy di lettura per quel ruolo |
Cosa non risolve il 30 ottobre
Nessuna tabella che hai già. Ognuna conserva esattamente l'accesso che ha oggi, compresa una tabella che uno sconosciuto può leggere in questo momento.
Il cambiamento riguarda tabelle che non esistono ancora. Finora ogni nuova tabella arrivava con le porte aperte, quindi un'app costruita prima del 30 ottobre può averne una in cui nessuno ha mai montato le serrature: Row Level Security mai acceso, oppure acceso con una regola che fa entrare tutti. Il 30 ottobre lascia quelle stanze esattamente come sono.
Tre posti ti dicono a che punto sei:
- La pagina delle impostazioni Data API, a cui rimanda la mail. Nella dashboard si trova sotto Integrations, poi Data API, e mostra quali delle tue tabelle sono raggiungibili del tutto.
- Il Security Advisor, che secondo Supabase elenca le tabelle da rivedere prima del cambiamento.
- La vista da fuori. Nessuno dei primi due ti dice cosa riceve uno sconosciuto. Per quello chiedi alla tua app online come farebbe uno sconosciuto, ed è quello che fa la nostra scansione.
Come Reeve sorveglia le tabelle che aggiungi
Dopo il 30 ottobre, ogni tabella che il tuo builder aggiunge è una decisione nuova su chi passa dalla porta. Reeve controlla la risposta da fuori, e Monitor continua a controllarla.
- La scansione gratuita chiede a ogni tabella che la tua app nomina quante righe riceverebbe un visitatore senza accesso, legge il conteggio e si ferma lì senza scaricare nessuna riga. Circa 20 secondi, senza account: scansiona la tua app.
- Reeve Monitor ripassa tutti e nove i controlli ogni ora su un massimo di tre app e ti manda una mail il giorno in cui il tuo voto peggiora, così un deploy che ha aperto qualcosa non aspetta che sia tu ad andare a guardare.
- Care ripassa gli stessi controlli e tiene una copia cifrata del tuo database Supabase fuori dal tuo account Supabase, così la correzione di un assistente che ricostruisce una tabella ha una copia a cui tornare. Come si fa la copia e come si rimette al suo posto.
Cosa include ogni piano è sulla pagina dei prezzi.
Cosa fare prima del 30 ottobre
Cosa fare
- Lascia stare le tabelle che hai già se vuoi solo che la tua app continui a funzionare. Conservano i loro grant.
- Chiedi al tuo builder di scrivere il grant,
enable row level securitye una policy nella stessa migrazione di ogni nuova tabella, come un'unica modifica. - Quando compare l'errore, leggi quale ruolo nomina il suggerimento.
anonsignifica qualsiasi visitatore della tua app. - Non concedere nulla ad
anonsu una tabella finché Row Level Security non è acceso e non c'è una regola scritta. Le tabelle con persone o ordini di solito non hanno bisogno di alcun grant adanon. - Rifiuta qualsiasi correzione che spegne Row Level Security o aggiunge
using (true)per togliere un errore di permessi. Nessuna delle due lo tocca. - Controlla cosa può leggere uno sconosciuto dalle tabelle che hai già. Il 30 ottobre le lascia come sono.
Comincia dalla tabella che uno sconosciuto vorrebbe leggere di più, di solito quella con dentro delle persone, e scansiona la tua app per vedere cosa consegna oggi.
FAQ
La mia app Supabase smetterà di funzionare il 30 ottobre?
No. Ogni tabella che esiste il 30 ottobre conserva l'accesso che ha, e Supabase ha confermato che quei grant non verranno revocati. Cambia la prossima tabella creata dopo quella data. Nasce irraggiungibile dalla tua app finché qualcuno non le concede l'accesso, quindi la prima cosa a rompersi è la prima funzione nuova che ha bisogno di una tabella nuova.
Cosa significa "permission denied for table" in Supabase?
Significa che il tipo di visitatore che fa la richiesta non ha alcun accesso a quella tabella, quindi il tuo database l'ha rifiutata prima di guardare una sola riga. Arriva da Postgres, il database su cui gira Supabase, con il codice 42501, e di solito raggiunge la tua app come un 401 o un 403. Supabase aggiunge un suggerimento con l'istruzione GRANT esatta che farebbe entrare quel visitatore.
È sicuro eseguire il GRANT che suggerisce l'errore?
Solo quando la tabella ha già Row Level Security acceso e una regola scritta. Un grant ad anon permette a qualsiasi visitatore della tua app di chiedere righe alla tabella, perché la chiave che fa quelle richieste viaggia dentro la tua app. Con una regola al suo posto riceve le righe che la regola permette. Senza regola e con Row Level Security spento, le riceve tutte.
Questo cambiamento tocca Storage, l'accesso degli utenti o le mie edge function?
Storage e l'accesso degli utenti vivono in schemi propri, storage e auth, e Supabase dice che i loro grant e le loro impostazioni predefinite restano come sono. Le edge function sono un'altra storia. Una che usa la tua chiave segreta ha comunque bisogno di un grant su ogni tabella nuova, perché i grant automatici che spariscono coprivano service_role oltre ai due ruoli usati dai tuoi visitatori.
Posso riattivare il comportamento di prima?
Supabase documenta come farlo, con un'impostazione nella pagina Data API della dashboard oppure con SQL, e lo sconsiglia. Con quell'opzione attiva, ogni nuova tabella è raggiungibile da qualsiasi visitatore dal momento in cui esiste, e Row Level Security è l'unica cosa fra uno sconosciuto e le sue righe. È così che funzionavano tutti i progetti prima del cambiamento.