Bases de la sécurité
Supabase "RLS disabled in public" : ce que l'alerte rate
Supabase signale "RLS disabled in public" comme une erreur. Elle ne dit rien de la politique de lecture qui laisse votre table tout aussi ouverte.

En bref
- "RLS disabled in public" veut dire une seule chose : une table de votre schéma public a Row Level Security désactivé, donc n'importe qui disposant de l'adresse de votre projet et de votre clé publiable peut la lire.
- Elle ne dit rien d'une table où vous avez activé le réglage puis écrit une politique qui laisse tout le monde lire. Une règle existe pour les politiques toujours vraies, et elle laisse les politiques de lecture de côté volontairement.
- Réglez les tables signalées, puis vérifiez les autres depuis l'extérieur, parce que l'advisor lit vos réglages et ne demande jamais à votre base ce qu'un inconnu reçoit vraiment.
Vous avez ouvert le Security Advisor dans votre tableau de bord Supabase, ou quelqu'un vous a collé sa sortie sous les yeux, et c'est là, en rouge : RLS disabled in public. En dessous, une ligne par table, dans les mots de Supabase :
Table public.profiles is public, but RLS has not been enabled.
Voici la partie que guide après guide raconte de travers : vider cette liste ne veut pas dire que personne ne peut lire vos données. L'advisor a une règle distincte pour une politique qui laisse entrer tout le monde, et cette règle saute exactement la forme qu'écrit un constructeur d'IA quand il débloque votre app. La liste que vous venez de traiter et la liste des tables qu'un inconnu peut lire sont donc deux listes différentes, et l'une d'elles n'est imprimée nulle part dans votre tableau de bord.
Que veut dire "RLS disabled in public" ?
Une table de votre schéma public a Row Level Security désactivé, et la conséquence est que quiconque dispose de l'adresse de votre projet peut lire chacune de ses lignes.
Les deux moitiés de cette phrase méritent d'être dépliées. Le schéma public est l'endroit où va une table quand personne ne dit le contraire, et c'est la partie de votre base que Supabase publie sur le web : chaque projet répond aux requêtes à une adresse qui lui est propre, et la clé nécessaire pour lui parler se trouve dans le code que votre site envoie à chaque visiteur. Row Level Security est l'interrupteur qui décide si vos règles sont consultées avant que des lignes soient remises. Éteint, il n'y a rien à consulter, donc la réponse est toujours oui.
C'est cette combinaison qui fait que ceci est signalé comme une erreur et non comme un avertissement. L'advisor classe ses trouvailles, et voici le haut de l'échelle.
Voyez l'advisor comme un inspecteur avec une planchette. Il lit vos papiers soigneusement et il le fait bien. Il n'essaie jamais la poignée. Rien dans ce panneau ne résulte d'une requête que quelqu'un aurait faite à votre base.
Activer RLS va-t-il casser mon app ?
Oui, tout de suite, et c'est le réglage qui fonctionne.
Le correctif que Supabase vous donne tient en une ligne, et vous l'exécutez dans le SQL Editor :
alter table public.profiles enable row level security;
La documentation de Supabase est directe sur la suite : les données deviennent inaccessibles via l'API avec une clé publiable tant qu'aucune politique n'est définie. Vos listes reviennent donc vides, vos écrans restent blancs, et l'erreur dans l'advisor est remplacée par une entrée plus discrète disant que la table a RLS activé mais aucune politique.
C'est la porte fermée avec encore personne sur la liste. Ce que font ensuite la plupart des gens, c'est ajouter une politique qui laisse tout le monde lire, parce que c'est ce qui fait revenir les écrans.
La faille que l'alerte ne cherche pas
Une table avec Row Level Security activé et une politique de lecture dont la
condition est USING (true) remet exactement les mêmes lignes exactement au
même inconnu. L'advisor ne la signale pas.
Ce n'est pas un oubli. Supabase a bien une règle pour les politiques toujours vraies, et elle laisse les politiques de lecture de côté volontairement. Sa propre description de la règle le dit :
SELECT policies with `USING (true)` are intentionally excluded as this
pattern is often used deliberately for public read access.
Dans le cas général, la règle a raison. Un catalogue produits, une liste d'articles publiés, une carte de lieux : tout cela est fait pour être lu par n'importe qui, et le signaler entraînerait chaque développeur de la plateforme à ignorer le panneau. Ce qu'aucun linter ne peut savoir, c'est si la table qu'il regarde contient des lieux ou des clients.
Et une politique qui laisse tout le monde lire est le moyen le plus rapide de
remettre une app cassée en marche, ce qui explique pourquoi un constructeur
d'IA s'en saisit. Demandez à Cursor ou à Lovable de réparer les écrans vides et
FOR SELECT USING (true) est une réponse fréquente. Votre app charge, l'erreur
disparaît, le panneau se tait, et la table se lit comme avant.
| Ce que l'advisor peut voir | Comment il le signale | Ce que reçoit un inconnu avec votre clé publiable |
|---|---|---|
| RLS désactivé | Erreur | Toutes les lignes |
| RLS activé, aucune politique | Info | Rien |
RLS activé, FOR SELECT USING (true) | Rien du tout | Toutes les lignes |
RLS activé, FOR ALL USING (true) | Avertissement | Toutes les lignes, et il peut les changer |
RLS activé, USING (auth.uid() = user_id) | Rien du tout | Seulement les siennes |
Les deux lignes où rien du tout n'est signalé sont la paire sur laquelle s'arrêter. L'une est une table que personne hors de votre app ne peut toucher. L'autre est une table que tout le monde peut lire. Votre tableau de bord est aussi silencieux sur les deux.
Comment cette politique en est arrivée là, et par quoi la remplacer table par table, fait l'objet de Row Level Security est activé et votre table reste publique. Si la même politique doit aussi laisser votre app enregistrer, l'erreur que vous croisez ensuite est new row violates row-level security policy.
Pourquoi la table créée par une migration ne vous a jamais alerté
Parce que le Table Editor active Row Level Security pour vous et que SQL ne le fait pas.
Supabase documente la coupure sans détour : les tables créées avec le Table
Editor du tableau de bord ont RLS activé par défaut, et les tables créées en SQL
brut doivent l'avoir activé explicitement. Une table que vous avez fait naître à
la souris démarre protégée. Une table arrivée par un fichier de migration, un
supabase db push, un extrait dans le SQL Editor ou une instruction que votre
constructeur d'IA a exécutée pour vous démarre ouverte.
Cette seconde voie est celle par laquelle un constructeur d'IA crée une table. Il écrit le SQL et l'exécute pour vous, donc vous n'avez jamais vu la case et jamais vu qu'elle était décochée.
L'habitude qui referme cela consiste à mettre la ligne dans la migration, juste à côté de ce qu'elle protège :
create table public.profiles (
id uuid primary key references auth.users,
full_name text
);
alter table public.profiles enable row level security;
Comment vérifier les tables que l'advisor a laissées passer
Posez à votre base la question que pose un inconnu : envoyez une requête depuis l'extérieur, avec la clé publiable livrée dans votre app, et regardez ce qui revient.
C'est la différence autour de laquelle tourne tout l'article. L'advisor lit votre configuration. Une requête lit vos lignes. Ce sont deux questions différentes, et une table peut réussir la première et échouer à la seconde, ce que fait précisément une politique de lecture permissive.
Nous avons posé cette requête à grande échelle. Entre le 12 et le 14 août 2026, nous avons passé neuf vérifications externes sur 30 998 apps en production publiées depuis Lovable, Base44, Replit, v0 et Bolt. Sur les 3 680 apps adossées à Supabase où la vérification a pu aboutir, 2 096 avaient au moins une table qui a répondu à une requête anonyme avec des lignes. Cela fait 57 %, et c'est une part des apps dont nous avons pu obtenir une réponse nette, pas de tout ce que nous avons scanné. Parmi les apps faites avec Bolt, le chiffre était de 27 sur 35, un échantillon assez petit pour se lire comme une direction et non comme un taux. Le jeu de données complet est publié, et de quoi ces 57 % sont une part reprend le décompte.
Vous pouvez faire vous-même cette requête sur une table avec un navigateur et votre propre clé publiable. Si vous préférez ne pas y aller table par table, notre scan gratuit interroge votre app en production depuis l'extérieur et vous dit quelles tables ont répondu. Il prend une vingtaine de secondes et ne demande pas de compte : scanner votre app.
Ai-je besoin d'une sauvegarde avant de changer des politiques RLS ?
Pour toute table dans laquelle votre app écrit, oui. Pour une table en lecture seule que vous ne faites que resserrer, le risque est que votre app reste blanche, pas que des données disparaissent.
Deux choses distinctes méritent d'être séparées ici, parce qu'une seule concerne la réparation.
Ce qu'une politique permissive a déjà autorisé. Si la règle sur la table
était FOR ALL USING (true) et non FOR SELECT, alors quiconque l'a trouvée
pouvait modifier et supprimer des lignes en plus de les lire, et la resserrer
aujourd'hui ne change rien à hier. Cette version apparaît en général comme un
message de support à propos de données qui ont changé toutes seules, ou comme
une table soudain vide.
La réparation elle-même. Réécrire les politiques sur une douzaine de tables est une modification d'une base en production, écrite par les mêmes outils qui ont produit le problème. Une migration qui supprime une politique et la recrée de travers est un mardi ordinaire, et le chemin du retour est une copie de l'état d'il y a une heure.
Sur un plan payant Supabase, la copie d'hier soir est dans la console. Sur le plan gratuit, il n'y a rien où retomber, parce que le plan gratuit ne prend aucune sauvegarde automatique. Si c'est votre cas, prenez-en une avant de toucher à une politique : comment sauvegarder une base Supabase sur le plan gratuit en est la version en dix minutes.
Ce dont Reeve Care garde une copie
De votre base Supabase, copiée selon un calendrier, gardée hors de votre compte Supabase, chiffrée, et relue avant que la date affichée sur votre tableau de bord ne bouge. Les fichiers que vos utilisateurs ont téléversés voyagent avec elle dès que vous connectez une clé Storage.
Deux limites, dites d'avance. Les sauvegardes sont réservées à Supabase : si vos données vivent ailleurs, nous le disons plutôt que de vous vendre un abonnement qui surveille une boîte vide. Et la clé Storage est demandée à part, parce que Supabase n'émet pas de clé en lecture seule pour les fichiers, donc celle qui copie vos téléversements peut aussi écrire. Celle qui copie votre base ne le peut pas. La connecter est facultatif, et la base est sauvegardée dans tous les cas.
La restauration est la partie qui compte pour cet article. Reposer une vieille copie par-dessus une base en production est le bouton le plus effrayant du produit, alors Care prend d'abord une copie de l'état actuel et ne rejoue qu'ensuite celle que vous avez choisie. La restauration a son propre retour en arrière.
L'autre moitié de Care est celle que cet article ne cesse de désigner. Une politique qui s'est relâchée pendant une migration ne se trouve pas en regardant, alors la même vérification externe repasse selon un calendrier et vous prévient quand la réponse change. La disponibilité, un rapport mensuel et le scan tiennent dans le même abonnement.
Ce qu'une copie ne fait pas, c'est écrire vos politiques à votre place, et aucune sauvegarde ne referme une table ouverte. Ces tables restent votre chantier. Ce que la copie change, c'est ce qui arrive quand la réparation part de travers. Ce que Reeve sauvegarde sur Supabase, à quelle fréquence, et ce que fait une restauration dessine le cycle entier, et les offres et leurs prix sont sur la page tarifs.
Que faire cette semaine
Que faire
- Videz d'abord les entrées "RLS disabled in public". Ce sont les tables où rien n'est consulté du tout, et le correctif est une ligne
alter tablechacune. - Ouvrez ensuite Authentication → Policies et lisez la condition de chaque politique qui a survécu. Un
USING (true)sur une politique de lecture est invisible pour l'advisor et grand ouvert pour un inconnu. - Décidez table par table si publier son contenu sur une page vous mettrait à l'aise. C'est la question à laquelle le linter ne peut pas répondre pour vous, et avec une politique de lecture permissive c'est la seule qui compte.
- Mettez
alter table ... enable row level security;dans chaque migration qui crée une table, et relancez l'advisor après chacune. La valeur par défaut du tableau de bord ne vaut que pour les tables créées à la souris. - Prenez une copie de votre base avant de réécrire des politiques sur une table en production, et regardez sur quel plan Supabase vous êtes pour savoir si vous en avez déjà une.
Commencez par la table qui vous gênerait le plus en page publique. La liste de sécurité en 10 minutes couvre cela avec le reste de ce qui mérite vérification sur une app fraîchement lancée, et le guide de sécurité Supabase passe en revue ce qui reste ouvert par ailleurs.
FAQ
"RLS disabled in public" est-ce une erreur ou un avertissement ?
Une erreur, et c'est le niveau le plus élevé qu'utilise l'advisor. L'entrée indique "Table public.<name> is public, but RLS has not been enabled." Les deux entrées voisines sont plus discrètes : une table avec le réglage activé et aucune politique est signalée en INFO, et une politique dont la condition est toujours vraie est signalée en WARN. Ces niveaux disent la confiance du linter dans ce qu'il peut voir de votre configuration, pas l'ampleur de votre problème.
Activer Row Level Security va-t-il casser mon app ?
Immédiatement, oui, et c'est le réglage qui fait son travail. Supabase documente que les données deviennent inaccessibles via l'API avec une clé publiable tant qu'aucune politique n'est définie. Dès que vous exécutez la ligne, vos listes reviennent vides et vos écrans restent blancs. L'app revient quand vous ajoutez une politique disant qui peut voir quelles lignes. L'erreur à éviter est d'en ajouter une qui autorise tout le monde, parce que cette version fait aussi remarcher l'app.
J'ai activé RLS et plus rien ne charge. Que s'est-il passé ?
Rien n'est cassé. Avec Row Level Security activé et aucune politique écrite, Postgres, le moteur de base de données sous Supabase, refuse toutes les requêtes, y compris celles de votre propre app, et l'advisor remplace son erreur par une entrée INFO disant que la table a RLS activé mais aucune politique. Écrivez une politique pour les lignes que votre app doit montrer, en commençant par celle qui compare le visiteur connecté à la colonne propriétaire de la ligne.
Ma table n'est pas signalée et pourtant tout le monde peut la lire. Pourquoi ?
Le plus probable est que Row Level Security soit activé et qu'une politique de lecture ait pour condition USING (true). L'advisor a bien une règle pour les politiques toujours vraies, et elle laisse volontairement de côté les politiques de lecture, parce que l'accès public en lecture est une chose raisonnable pour un catalogue produits ou une liste d'articles publiés. Rien dans votre tableau de bord ne sait si votre table contient des lieux ou des clients, donc cette vérification vous revient.
Ai-je besoin de Row Level Security si mon app ne parle qu'à mon propre serveur ?
Si le navigateur ne parle réellement jamais à Supabase et qu'aucune clé publiable ne figure dans le code que votre site envoie aux visiteurs, alors la Data API n'est pas une porte d'entrée et les politiques ne sont pas ce qui protège ces tables. C'est rare dans une app faite avec Lovable, Bolt ou v0, parce que ces constructeurs branchent le navigateur directement sur Supabase par défaut. Ouvrez votre propre site, regardez si l'URL du projet et la clé publiable y figurent, et laissez cette réponse décider.