Bases de la sécurité
"Row-level security policy for table objects" à l'upload
"New row violates row-level security policy for table objects" signifie que votre upload n'a pas de règle insert. Rendre le bucket public n'en ajoute aucune.

En bref
- "New row violates row-level security policy for table objects" signifie que votre upload est arrivé jusqu'à Supabase Storage et qu'aucune règle sur storage.objects ne l'a laissé entrer. Le fichier n'a pas été stocké, et le reste du bucket est intact.
- Storage garde ses règles sur une seule table pour tous les buckets de votre projet, et c'est pour cela que le message nomme une table que vous n'avez jamais créée.
- Rendre le bucket public ne règle rien. Supabase vérifie les uploads dans les deux cas, et public décide seulement qui peut ouvrir un fichier dont il a déjà l'adresse.
- Un upload a besoin d'une règle insert sur storage.objects. Si votre code enregistre avec upsert, il lui faut aussi select et update.
Votre application envoie un fichier. Cela marchait dans l'aperçu de votre builder, ou cela marchait la semaine dernière, et maintenant chaque tentative revient avec une phrase sur la row-level security :
new row violates row-level security policy for table "objects"
L'essentiel de cette phrase vous paraîtra familier si vous êtes déjà passé par
là sur l'une de vos tables. Ce qui change, c'est le dernier mot, parce que
objects n'est pas une table que vous avez créée.
Voici la partie que guide après guide se trompe : la première solution que vous trouverez est de rendre le bucket public, et elle ne fait rien pour les uploads. La documentation de Supabase le dit en une ligne. Ce bouton laisse l'upload refusé et ouvre les fichiers qui étaient déjà là.
Ce que signifie "new row violates row-level security policy for table objects"
Votre upload est arrivé jusqu'à Supabase Storage, Storage a demandé aux règles
posées sur une table appelée storage.objects si ce fichier avait le droit
d'entrer, n'en a trouvé aucune qui réponde oui, et a refusé.
La ligne du message existe vraiment. Storage garde une ligne dans
storage.objects pour chaque fichier qu'il détient, avec le nom du fichier, le
bucket où il se trouve et qui l'a déposé. Écrire un fichier, c'est écrire cette
ligne, donc les règles posées sur cette table décident si l'upload a lieu du
tout. Rien n'est perdu : aucun fichier n'a été stocké, et le reste du bucket est
exactement comme il y a une minute.
La formulation vient de Postgres, la base de données sous Supabase, et c'est
pour cela qu'elle se lit comme de la mécanique. Elle porte le code 42501, le
même que pour
un enregistrement refusé dans l'une de vos propres tables.
Pourquoi le message nomme "objects" et pas votre bucket
Parce que Storage garde les règles de tous les buckets de votre projet sur cette seule table.
Un bucket range des fichiers. Il n'a pas de règles à lui, et aucun éditeur de
policies n'y est rattaché. storage.objects est l'endroit où vivent les règles,
pour tous vos buckets à la fois, et bucket_id y est une colonne. Une règle qui
dit "seulement dans le bucket avatars" s'écrit donc comme une condition sur
cette colonne.
C'est aussi pourquoi la règle de table que vous avez écrite la semaine dernière
n'a pas aidé. Une règle sur profiles est une règle sur les lignes de
profiles, et un fichier est une ligne de storage.objects.
Dois-je rendre le bucket public pour pouvoir uploader ?
Non. Deux questions différentes se cachent derrière un bucket, et le bouton n'en tranche qu'une. Qui peut sortir un fichier, c'est public qui le décide. Qui peut en déposer un, c'est le sujet de votre erreur, et la documentation de Supabase est explicite : le réglage ne va pas jusque-là. Sur la page décrivant les deux sortes de bucket, lue le 11 octobre 2026 :
Le contrôle d'accès reste appliqué aux autres types d'opérations, y compris l'upload, la suppression, le déplacement et la copie.
Ce que public fait réellement est dit tout aussi clairement sur cette page : quiconque détient l'adresse d'un fichier peut l'ouvrir sans se connecter. Le réglage n'est rien de plus.
Le basculer vous met donc dans le seul état que personne ne veut. L'upload reste refusé, parce que l'upload n'a jamais été ce que le réglage contrôlait, et les fichiers qui étaient déjà dans le bucket peuvent maintenant être ouverts par n'importe qui ayant leur adresse. Ce qu'un bucket public vous coûte vraiment est une autre question que cette erreur, et elle vaut la lecture avant de toucher à l'interrupteur.
Ce qu'un bucket vérifie vraiment à l'upload
Une règle, et elle s'appelle insert.
La page de Supabase sur le contrôle d'accès énonce clairement la valeur par
défaut : sans policies, Storage n'autorise aucun upload dans un bucket, et vous
autorisez les opérations une par une en les écrivant sur storage.objects. Puis
elle nomme celle dont vous avez besoin :
Par exemple, la seule policy RLS nécessaire pour uploader des objets consiste à accorder la permission
INSERTsur la tablestorage.objects.
Il y a une seconde moitié à cela, et c'est la raison la plus fréquente pour laquelle une règle insert ne fait pas disparaître l'erreur :
Pour autoriser l'écrasement de fichiers via la fonctionnalité
upsert, vous devrez accorder en plus les permissionsSELECTetUPDATE.
Si votre upload passe upsert: true, qui demande à Storage de remplacer un
fichier de même nom quand il y en a déjà un, alors une règle insert seule ne
suffit pas. Remplacer un fichier, ce sont trois questions au lieu d'une :
pouvez-vous ajouter cette ligne, pouvez-vous voir celle qui est là, et
pouvez-vous la modifier.
| Ce que votre application demande à Storage | Ce qu'il lui faut sur storage.objects |
|---|---|
| uploader un nouveau fichier | une règle insert |
remplacer un fichier de même nom (upsert) | insert, plus select et update |
| ouvrir un fichier dans un bucket privé | une règle select |
| lister ce que contient un bucket | une règle select |
| supprimer un fichier | une règle delete |
Vous n'avez pas à écrire les cinq. Écrivez celles que votre application fait réellement, ce qui pour la plupart des uploads est la première ligne et parfois la deuxième.
La règle qui laisse votre application uploader, et elle seule
Nommez le bucket, et dites pour qui la règle vaut.
Le point de départ de la documentation limite un upload à un bucket et aux visiteurs connectés :
create policy "Allow authenticated uploads"
on storage.objects
for insert
to authenticated
with check (bucket_id = 'my_bucket_id');
to authenticated est la partie à ne pas sauter. Elle signifie que la règle
s'applique aux visiteurs connectés ; retirez-la et la règle s'applique à tout le
monde, inconnus compris.
Pour tout ce qui appartient à une personne en particulier, la version que Supabase publie met les fichiers de chaque personne dans un dossier portant son nom :
create policy "Allow authenticated uploads"
on storage.objects
for insert
to authenticated
with check (
bucket_id = 'my_bucket_id' and
(storage.foldername(name))[1] = (select auth.jwt()->>'sub')
);
storage.foldername(name) découpe le chemin d'un fichier stocké en ses
dossiers, donc [1] est le premier. auth.jwt()->>'sub' est l'id de la
personne connectée qui fait la requête. Lues ensemble, les deux lignes disent
que vous pouvez déposer un fichier dans le dossier portant votre nom, dans ce
bucket, et nulle part ailleurs.
Ce sont les exemples de Supabase et non les nôtres, lus le 11 octobre 2026, avec
my_bucket_id laissé là où ils l'ont laissé pour que vous voyiez quelle partie
est le nom de votre propre bucket.
Si vous préférez voir le résultat de l'extérieur, notre scan gratuit demande à votre projet en ligne lesquels de vos buckets remettront leur contenu à un inconnu. Il prend une vingtaine de secondes et ne demande aucun compte : scannez votre application.
Ce qu'une règle de lecture remet sans qu'on le lui demande
Lister un bucket et y télécharger reposent sur le même privilège, donc une règle écrite pour faire marcher vos téléchargements rend aussi le contenu du bucket listable.
La page de Supabase sur les fonctions d'aide le dit directement : un seul
privilège SQL comme SELECT est utilisé par plusieurs actions de Storage. La
page sur le contrôle d'accès met ensuite en garde sur le cas précis, sous un
exemple qui ouvre à tous un bucket d'avatars :
Le filtre
allow_any_operation()est ici essentiel, car sans lui les utilisateurs pourraient lister le contenu du bucket.
C'est l'écart que nous mesurons de l'extérieur. Nous avons scanné 8 435 applications en ligne qui nomment un projet Supabase. La vérification des buckets a obtenu une réponse sur 4 703 d'entre elles, et 792 de celles-là ont répondu à notre demande de listage avec les noms de ce qu'elles contenaient, sans personne de connecté. C'est environ une sur six parmi celles que nous pouvions interroger.
Un nom suffit souvent, parce qu'un bucket qui liste épargne à un inconnu la
peine de deviner des noms de fichiers. invoice-2026-03-hannah.pdf dit à qui
appartient le fichier avant que quiconque l'ouvre.
La correction que Supabase documente consiste à dire à quelle action de Storage la règle s'applique :
create policy "Allow users to list their own objects"
on storage.objects
for select
to authenticated
using (
storage.allow_only_operation('object.list')
and owner_id = (select auth.uid()::text)
);
storage.allow_only_operation et sa jumelle storage.allow_any_operation sont
la façon dont une règle select se restreint à l'une des actions qui partagent
le privilège. Sans l'une des deux, une règle que vous avez écrite pour que votre
application affiche une image est aussi une règle qui lira tout le contenu du
bucket.
Qu'un bucket listable soit un problème pour votre application dépend de ce qu'il contient, et c'est une question à laquelle le résultat du scan ne peut pas répondre à votre place.
Quand public est la bonne réponse
Quand les fichiers sont faits pour être vus par quiconque les demande, et c'est une vraie catégorie dont Supabase donne ses propres exemples.
Les cas d'usage que Supabase cite pour un bucket public sont les photos de profil, les médias publics et le contenu d'articles de blog. Ce sont des fichiers que votre application montre à un visiteur non connecté, et les servir depuis un bucket public est en plus plus rapide, parce que les deux sortes de bucket sont mises en cache différemment.
Pour l'autre sorte, la documentation nomme deux façons de sortir un fichier d'un bucket privé : un téléchargement portant le token de la personne connectée, ou un lien signé valable un temps limité. Les deux laissent la décision à vos règles au lieu de la laisser à celui qui a trouvé l'adresse.
Garder le bucket correct après aujourd'hui
Deux choses bougent une fois les règles correctes : quelqu'un bascule le bouton en poursuivant un bug sans rapport, et un fichier disparaît.
Reeve Monitor relance les neuf vérifications extérieures pour vous :
- les neuf vérifications toutes les heures, sur trois applications au plus, listage de bucket compris
- si l'application répond, toutes les 60 secondes
- un message quand un résultat change, pour qu'un bucket ouvert cette nuit n'attende pas que vous regardiez
- un rapport mensuel de ce qui a été vu
Reeve Care garde une copie de ce qui s'y trouve :
- une copie chiffrée de votre base Supabase chaque nuit, rangée là où votre projet ne peut pas l'atteindre
- chaque copie vérifiée avant de compter, en comptant les lignes de chaque table
- vos fichiers uploadés aussi, dès que vous connectez un accès Storage
- une restauration en un clic quand vous en avez besoin
- tout ce que fait Monitor
Les deux sont sur la page de tarifs, qui est parfois en dessous du tarif affiché ici et jamais au-dessus.
Ce qu'il faut faire aujourd'hui
Que faire
- Lisez le message comme un refus. Le fichier n'a pas été stocké, le bucket est inchangé, et il n'y a rien à récupérer.
- Ajoutez une règle
insertsurstorage.objectsqui nomme votre bucket dansbucket_idet porte la clauseTOque vous vouliez. - Si votre upload passe
upsert: true, ajoutezselectetupdate, sinon la règle insert seule ne fera pas disparaître l'erreur. - Laissez le bouton public où il est pendant ce temps. Il n'a rien à dire sur les uploads et il a bien son mot à dire sur qui peut ouvrir ce qui est déjà stocké.
- Relisez chaque règle
selectsurstorage.objectset demandez-vous ce qu'elle autorise en plus du téléchargement pour lequel vous l'avez écrite. Le listage partage ce privilège.
Commencez par le bucket d'où venait l'erreur, puis regardez les autres buckets du même projet, parce qu'une règle écrite largement une fois a le plus souvent été collée deux fois. La checklist de sécurité en 10 minutes couvre ce qui reste habituellement ouvert dans une application fraîchement lancée, et le guide de sécurité Supabase passe en revue le reste de ce qu'un inconnu peut atteindre.
FAQ
Pourquoi mon upload Supabase échoue-t-il sur une erreur de row-level security ?
Parce que Supabase Storage a demandé aux règles posées sur une table appelée storage.objects si votre fichier avait le droit d'entrer, et n'a trouvé aucune règle qui réponde oui. Storage écrit une ligne dans cette table pour chaque fichier qu'il conserve, et Row Level Security s'applique à cette ligne exactement comme à une ligne de n'importe quelle autre table. Aucun fichier n'a été stocké et rien de ce qui se trouvait déjà dans le bucket n'a changé. Le message porte le code Postgres 42501, le même que lorsqu'un enregistrement dans l'une de vos tables est refusé.
Dois-je rendre mon bucket public pour pouvoir uploader ?
Non, et le rendre public n'aidera pas. Supabase documente que le contrôle d'accès reste appliqué à l'upload, à la suppression, au déplacement et à la copie, quel que soit le réglage du bucket. Public change une seule chose : si quelqu'un qui détient l'adresse d'un fichier peut ouvrir ce fichier sans se connecter. Le bouton laisse donc votre upload refusé et rend les fichiers déjà présents ouvrables par quiconque a leur adresse.
De quelles policies un bucket a-t-il besoin ?
Un upload a besoin d'une règle insert sur storage.objects. Si votre code remplace des fichiers de même nom, ce que fait upsert, Supabase indique qu'il faut aussi select et update. Ouvrir un fichier dans un bucket privé demande une règle select, et lister le contenu du bucket aussi, parce que ces deux actions de Storage reposent sur le même privilège SQL. Supprimer demande une règle delete. Rien n'oblige à écrire les quatre, seulement celles que votre application fait réellement.
Un bucket public est-il dangereux ?
Cela dépend entièrement de ce qu'il contient. Public est le bon réglage pour des photos de profil, des logos et tout ce que votre application montre à un visiteur non connecté, et c'est exactement ce que Supabase donne comme cas d'usage. C'est le mauvais réglage pour des factures, des exports ou quoi que ce soit appartenant à une personne en particulier, et pour cela vous voulez un bucket privé avec un lien signé. La question n'a jamais été de savoir si public est mauvais.
Comment vérifier si mon bucket est listable ?
Demandez-le de l'extérieur, sans compte connecté, exactement comme le ferait un inconnu. Le listage est accordé par une règle select et non par le bouton public, donc ni le bouton du dashboard ni un coup d'œil à votre liste de policies ne répond à la question. Notre scan gratuit le fait sur votre projet en ligne et vous dit lesquels de vos buckets ont répondu avec les noms de ce qu'ils contenaient.