Bases de la sécurité
Supabase "permission denied for table" : le grant manquant
Dès le 30 octobre, une nouvelle table Supabase répond "permission denied for table" tant que l'accès n'est pas accordé. Le grant de l'e-mail est la moitié.

En bref
- À partir du 30 octobre 2026, une nouvelle table de votre projet Supabase répond "permission denied for table" tant que personne ne lui a accordé d'accès. Les tables que vous avez déjà continuent de fonctionner exactement comme aujourd'hui.
- Un grant décide si une requête atteint la table ou non. Row Level Security décide toujours quelles lignes elle récupère. Accorder à anon l'accès à une table sans règles rend chaque ligne lisible par n'importe qui.
- Le changement ne ferme rien de ce qui est déjà ouvert. Vérifiez aujourd'hui ce qu'un inconnu peut lire, puis de nouveau après chaque table ajoutée.
Si votre app tourne sur Supabase, vous avez probablement reçu un e-mail à propos
du 30 octobre. Il dit que rien ne change pour les tables que vous avez déjà,
puis il vous tend trois instructions SQL. Ou alors nous sommes déjà en novembre :
vous avez demandé une nouvelle fonctionnalité à Lovable, Bolt ou Cursor, le
nouvel écran est resté vide, et quelque part dans la console se trouve un
message qui dit permission denied for table. C'est le même changement
Supabase, vu de part et d'autre de la date.
Voici ce que l'e-mail ne dit pas : le SQL qu'il vous montre n'est que la moitié de la solution. Exécutez cette moitié seule, sur la mauvaise table, et votre app remarche pendant que la nouvelle table donne ses lignes à quiconque les demande.
Que signifie "permission denied for table" dans Supabase ?
Cela signifie que le type de visiteur qui fait la requête n'a reçu aucun accès à cette table, si bien que la base a refusé avant même de regarder une seule ligne.
Supabase range chaque requête dans un rôle, c'est-à-dire le type de
visiteur dont elle vient. anon, c'est quelqu'un qui n'est pas connecté.
authenticated, quelqu'un qui l'est. service_role, c'est votre propre code
serveur qui utilise la clé secrète. Un grant est l'instruction qui donne à
l'un de ces rôles l'accès à une table dans Postgres, la base de données sur
laquelle tourne Supabase. Pas de grant, pas d'accès, quoi que vous ayez écrit
par ailleurs.
Quand le grant manque, Supabase répond ainsi, en général sous forme de 401 ou de 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;"
}
L'indication est la partie utile. Elle nomme le rôle qui a été refusé et l'instruction exacte qui le laisserait entrer.
Imaginez chaque table comme une pièce. Le grant est la porte, et il y a une porte distincte pour chaque rôle. Row Level Security, les règles par ligne que vous avez peut-être déjà croisées, décide quels tiroirs un visiteur peut ouvrir une fois entré. "Permission denied for table" veut dire que quelqu'un se tient devant une porte fermée. Il n'est jamais arrivé jusqu'à vos règles.
Qu'est-ce qui change le 30 octobre ?
Les nouvelles tables du schéma public, le dossier de tables qu'utilise votre
builder sauf indication contraire, arrivent désormais avec leurs portes
fermées.
Jusqu'ici, Supabase ouvrait automatiquement les trois portes de chaque nouvelle
table : lire, ajouter, modifier et supprimer, pour anon, authenticated et
service_role à la fois. Une table était accessible depuis votre app dès
qu'elle existait, et vos règles Row Level Security étaient la seule chose entre
un inconnu et ses lignes. Le
changelog
de Supabase donne la raison sans détour : des agents et des plateformes d'IA
créent aujourd'hui des tables sans que personne ne relise le changement, et les
grants automatiques exposaient des tables que "a developer forgot to protect",
qu'un développeur avait oublié de protéger.
| Date | Ce qui s'est passé ou se passe |
|---|---|
| 28 avril 2026 | Les nouveaux projets pouvaient refuser les grants automatiques à leur création. |
| 30 mai 2026 | L'absence de grants automatiques a commencé à devenir la règle pour les nouveaux projets. |
| 30 octobre 2026 | Les projets existants cessent eux aussi de les recevoir. |
Si votre projet a été créé après la fin mai, il fonctionne peut-être déjà ainsi, car Supabase a déployé le nouveau réglage par défaut sur les nouveaux projets au fil des semaines qui ont suivi cette date.
Trois détails méritent d'être connus avant la date :
- Seulement le schéma
public. Storage et la connexion rangent leurs tables dans des schémas à eux,storageetauth, et Supabase indique que leurs grants et leurs réglages par défaut restent inchangés. - La clé secrète est refusée aussi. Les grants automatiques couvraient
également
service_role. Une edge function (du code serveur que Supabase exécute pour vous) qui utilise votre clé secrète reçoit donc la même erreur sur une nouvelle table, jusqu'à ce queservice_roleait son propre grant. - Une table supprimée puis recréée est une nouvelle table. Les grants appartiennent à la table elle-même et disparaissent avec elle quand elle est supprimée. Si votre builder reconstruit une table pour la modifier, la table reconstruite commence porte fermée.
Mon app va-t-elle casser le 30 octobre ?
Pas ce jour-là. Elle casse la première fois que quelque chose crée une nouvelle table sans créer aussi le grant.
Pour la plupart des lecteurs, ce quelque chose, c'est leur builder. Vous
demandez une section de commentaires. Le builder écrit une migration, le
fichier de modifications de base de données qu'il exécute pour vous, et la
migration crée une table comments. S'il écrit aussi les grants, vous ne verrez
jamais cette erreur. Sinon, l'écran des commentaires n'affiche rien, ou
l'enregistrement d'un commentaire échoue, ou un message rouge apparaît, selon la
façon dont votre app gère les erreurs. Tous les écrans que vous aviez déjà
continuent de fonctionner.
Supabase publie un agent skill pour les outils de code par IA qui inclut l'étape du grant. Que votre builder l'utilise ou non dépend de votre builder. Mieux vaut donc supposer que la prochaine table qu'il crée peut arriver porte fermée.
Le GRANT proposé par l'erreur est-il sans risque ?
Seulement une fois que la table a Row Level Security activé et une règle écrite pour elle. Le grant décide qui passe la porte. Il ne décide en rien des tiroirs qu'on ouvre ensuite.
La clé avec laquelle votre app envoie ses requêtes à Supabase est livrée dans
votre app. Un grant à anon veut donc dire que n'importe quel visiteur,
connecté ou non, peut désormais demander des lignes à cette table. Avec une
règle en place, il obtient les lignes que la règle autorise. Avec Row Level
Security désactivé, il obtient toutes les lignes de la table, et rien dans votre
app n'aura l'air différent.
L'e-mail montre trois grants et s'arrête là. Le changelog de Supabase montre trois étapes, et dit de "treat these three steps as a unit", de les traiter comme un tout :
-- 1. qui peut atteindre la table
grant select on public.orders to authenticated;
grant select, insert, update, delete on public.orders to service_role;
-- 2. activer les règles par ligne
alter table public.orders enable row level security;
-- 3. la règle elle-même
create policy "Customers read their own orders"
on public.orders
for select
to authenticated
using (auth.uid() = user_id);
Il n'y a aucune ligne pour anon dans cet exemple, et c'est voulu. Personne de
déconnecté ne devrait atteindre une table de commandes, donc la porte des
inconnus reste fermée, et les clients connectés n'obtiennent que la lecture
dont leur écran a besoin. La règle réduit ensuite cela à leurs propres lignes.
Supabase conseille lui-même de
n'accorder à chaque rôle que le strict nécessaire.
Un grant à anon a sa place sur une table dont les lignes sont faites pour tout
le monde, comme les commentaires sous un article public.
Si vous voulez voir de quel côté de cette image se trouvent vos tables, notre scan gratuit pose à votre app en ligne la question qu'un inconnu poserait, compte les lignes qu'il recevrait sans en récupérer aucune, et prend environ 20 secondes, sans compte : scannez votre app.
Pourquoi les correctifs Row Level Security ne touchent pas cette erreur
Parce que le grant est vérifié en premier. Une requête refusée à la porte n'atteint jamais vos règles, et modifier les règles ne change donc rien à l'erreur.
C'est important parce que le code est partagé. 42501, c'est aussi ce que
renvoie Postgres quand Row Level Security refuse un enregistrement, comme dans
new row violates row-level security policy.
Un assistant qui se fie au seul code peut se tourner vers les correctifs de
cette autre erreur : désactiver Row Level Security, ou ajouter une règle
using (true), qui laisse passer tout le monde. Aucun des deux ne fait
disparaître cette erreur. Les deux restent en place une fois le grant enfin
ajouté, et la porte s'ouvre alors sur des tiroirs sans serrure.
Les mots du message distinguent les deux, même si le numéro ne le fait pas :
| Le message dit | Quelle serrure a refusé | Ce qui corrige |
|---|---|---|
permission denied for table | la porte (le grant) | un grant pour le rôle que nomme l'indication |
new row violates row-level security policy | les règles | une policy qui autorise cette ligne |
| aucune erreur, et la liste est vide | les règles | une policy de lecture pour ce rôle |
Ce que le 30 octobre ne corrige pas
Aucune des tables que vous avez déjà. Chacune garde exactement l'accès qu'elle a aujourd'hui, y compris une table qu'un inconnu peut lire en ce moment.
Le changement concerne des tables qui n'existent pas encore. Jusqu'ici, chaque nouvelle table arrivait portes ouvertes, et une app construite avant le 30 octobre peut donc en avoir une où personne n'a jamais posé les serrures : Row Level Security jamais activé, ou activé sous une règle qui laisse entrer tout le monde. Le 30 octobre laisse ces pièces exactement comme elles sont.
Trois endroits vous montrent où vous en êtes :
- La page de réglages de la Data API, vers laquelle pointe l'e-mail. Dans le tableau de bord, elle se trouve sous Integrations, puis Data API, et elle liste lesquelles de vos tables sont accessibles.
- Le Security Advisor, qui, selon Supabase, liste les tables à revoir avant le changement.
- La vue de l'extérieur. Aucun des deux premiers ne vous dit ce qu'un inconnu récupère. Pour cela, il faut interroger votre app en ligne comme le ferait un inconnu, et c'est ce que fait notre scan.
Comment Reeve surveille les tables que vous ajoutez
Après le 30 octobre, chaque table que votre builder ajoute est une nouvelle décision sur qui passe la porte. Reeve vérifie la réponse depuis l'extérieur, et Monitor continue de la vérifier.
- Le scan gratuit demande à chaque table que nomme votre app combien de lignes recevrait un visiteur sans connexion, lit ce nombre et s'arrête là, sans récupérer une seule ligne. Environ 20 secondes, sans compte : scannez votre app.
- Reeve Monitor lance les neuf vérifications toutes les heures sur jusqu'à trois apps et vous envoie un e-mail le jour où votre note se dégrade. Un déploiement qui a ouvert quelque chose n'attend donc pas que vous veniez regarder.
- Care lance les mêmes vérifications et conserve une copie chiffrée de votre base Supabase en dehors de votre compte Supabase. Si le correctif d'un assistant reconstruit une table, il existe une copie à laquelle revenir. Comment la copie est faite et remise en place.
Ce que couvre chaque formule est sur la page des tarifs.
Que faire avant le 30 octobre
Que faire
- Laissez tranquilles les tables que vous avez déjà si tout ce que vous voulez, c'est que votre app continue de fonctionner. Elles gardent leurs grants.
- Demandez à votre builder d'écrire, pour chaque nouvelle table, le grant,
enable row level securityet une policy dans la même migration, comme un seul changement. - Quand l'erreur apparaît, lisez quel rôle nomme l'indication.
anonveut dire chaque visiteur de votre app. - N'accordez rien à
anonsur une table tant que Row Level Security n'est pas activé et qu'aucune règle n'est écrite. Les tables qui contiennent des personnes ou des commandes n'ont en général besoin d'aucun grant pouranon. - Refusez tout correctif qui désactive Row Level Security ou ajoute
using (true)pour faire disparaître une erreur de permission. Aucun des deux ne la touche. - Vérifiez ce qu'un inconnu peut lire dans les tables que vous avez déjà. Le 30 octobre les laisse telles quelles.
Commencez par la table qu'un inconnu aimerait le plus lire, souvent celle qui contient des personnes, et scannez votre app pour voir ce qu'elle livre aujourd'hui.
FAQ
Mon app Supabase va-t-elle cesser de fonctionner le 30 octobre ?
Non. Chaque table qui existe le 30 octobre garde les accès qu'elle a, et Supabase a confirmé que ces grants ne seront pas retirés. Ce qui change, c'est la prochaine table créée après cette date. Elle est d'abord injoignable depuis votre app jusqu'à ce que quelqu'un lui accorde un accès. La première chose à casser sera donc la première nouvelle fonctionnalité qui a besoin d'une nouvelle table.
Que signifie "permission denied for table" dans Supabase ?
Cela signifie que le type de visiteur qui fait la requête n'a reçu aucun accès à cette table. Votre base a donc refusé avant même de regarder une seule ligne. Le message vient de Postgres, la base de données sur laquelle tourne Supabase, avec le code 42501, et arrive en général dans votre app sous forme de 401 ou de 403. Supabase y ajoute une indication qui nomme exactement l'instruction GRANT qui laisserait entrer ce visiteur.
Puis-je exécuter sans risque le GRANT que suggère l'erreur ?
Seulement une fois que la table a Row Level Security activé et une règle écrite pour elle. Un grant à anon permet à chaque visiteur de votre app de demander des lignes à la table, parce que la clé qui fait ces requêtes est livrée dans votre app. Avec une règle en place, ils obtiennent les lignes que la règle autorise. Sans règle et avec Row Level Security désactivé, ils les obtiennent toutes.
Ce changement touche-t-il Storage, la connexion ou mes edge functions ?
Storage et la connexion vivent dans leurs propres schémas, storage et auth, et Supabase indique que leurs grants et leurs réglages par défaut restent inchangés. Pour les edge functions, c'est différent. Une fonction qui utilise votre clé secrète a quand même besoin d'un grant sur toute nouvelle table, parce que les grants par défaut qui disparaissent couvraient aussi service_role, en plus des deux rôles qu'utilisent vos visiteurs.
Puis-je réactiver l'ancien comportement ?
Supabase documente une façon de le faire, par un réglage sur la page Data API du tableau de bord ou en SQL, et le déconseille. Une fois réactivé, chaque nouvelle table est accessible à chaque visiteur dès qu'elle existe, et Row Level Security est la seule chose entre un inconnu et ses lignes. C'est ainsi que fonctionnaient tous les projets avant le changement.