[{"data":1,"prerenderedAt":1278},["ShallowReactive",2],{"blog-uk-invalid-claim-missing-sub-claim":3,"blog-index-uk":753},{"id":4,"title":5,"body":6,"category":711,"cover":712,"coverAlt":713,"description":714,"draft":715,"extension":716,"faq":717,"image":733,"keywords":734,"meta":741,"navigation":742,"ogTitle":743,"path":744,"published":745,"seo":746,"stem":747,"tldr":748,"updated":745,"__hash__":752},"blog_uk\u002Fblog\u002Finvalid-claim-missing-sub-claim.md","Supabase \"invalid claim: missing sub claim\": ніхто не ввійшов",{"type":7,"value":8,"toc":698},"minimark",[9,13,24,36,41,44,59,77,83,86,97,103,107,110,118,128,134,141,147,173,181,185,188,202,210,218,225,235,259,264,268,271,282,331,342,345,349,352,361,367,383,389,403,407,410,429,436,446,450,453,568,587,591,597,606,620,630,647,651,683],[10,11,12],"p",{},"У вашому застосунку є Supabase Edge Function, яка щось робить для людини, що ним\nкористується: наприклад, зберігає її налаштування чи викликає OpenAI від її\nімені. Поки ви її тестували, вона працювала. Потім лог функції починає\nзаповнюватися цим рядком, а застосунок показує помилку або взагалі нічого:",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","AuthApiError: invalid claim: missing sub claim\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26,27,30,31,35],{},"Пошукайте \"invalid claim: missing sub claim\", і вам порадять перегенерувати\nключі, вимкнути Verify JWT або підставити ключ ",[21,28,29],{},"service_role",". Кожна з цих порад\nвважає токен зламаним. ",[32,33,34],"strong",{},"З токеном усе було гаразд."," Supabase перевірив його\nпідпис і прийняв його. У ньому бракувало лише чийогось імені.",[37,38,40],"h2",{"id":39},"що-означає-invalid-claim-missing-sub-claim","Що означає \"invalid claim: missing sub claim\"?",[10,42,43],{},"Supabase Auth спитали, який користувач викликає; він визнав токен справжнім і\nне знайшов у ньому жодного користувача.",[10,45,46,47,50,51,58],{},"Токени, які читає Supabase Auth, є JWT: три шматки тексту, розділені крапками, де\nсередній шматок містить дані, які можна прочитати. Кожен запис у цих даних\nназивають claim. ",[21,48,49],{},"sub",", скорочено від subject, це claim, у якому зберігається id\nкористувача, і\n",[52,53,57],"a",{"href":54,"rel":55},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fauth\u002Fjwt-fields",[56],"nofollow","довідник Supabase про JWT","\nописує його як ID користувача, UUID.",[10,60,61,62,65,66,69,70,72,73,76],{},"Повідомлення записане у власному вихідному коді Supabase Auth, у функції\n",[21,63,64],{},"maybeLoadUserOrSession"," у файлі ",[21,67,68],{},"internal\u002Fapi\u002Fauth.go",". Коли ця функція\nзапускається, підпис токена вже перевірено, і перевірку він пройшов. Далі вона\nшукає ",[21,71,49],{},", і коли claim порожній, зупиняється з HTTP 403, кодом помилки\n",[21,74,75],{},"bad_jwt"," і цим реченням. Відповідь, яку отримує ваш код, виглядає так:",[14,78,81],{"className":79,"code":80,"language":19},[17],"{\"code\":403,\"error_code\":\"bad_jwt\",\"msg\":\"invalid claim: missing sub claim\"}\n",[21,82,80],{"__ignoreMap":23},[10,84,85],{},"Уявіть рецепцію, якій ставлять одне-єдине питання: хто цей відвідувач? Ви\nподаєте бейдж. Рецепція спершу перевіряє на ньому печатку, і печатка справжня.\nПотім читає рядок з імʼям, а рядок з імʼям порожній. Рецепції нема чим\nвідповісти, тож вона відмовляє, і відмова вказує на порожній рядок.",[10,87,88,89,92,93,96],{},"Таку відповідь може отримати будь-який виклик, якому потрібна людина, що\nввійшла. ",[21,90,91],{},"getUser()"," і ",[21,94,95],{},"updateUser()"," це два, де ви найімовірніше її побачите, а\nдодавання другого фактора чи привʼязка ще одного способу входу проходять через\nту саму перевірку.",[98,99],"diagram",{"alt":100,"caption":101,"src":102},"Два бейджі прямують до однієї рецепції. Обидва мають справжню печатку, й обидва проходять перевірку печатки. Верхній бейдж, позначений authenticated, має обличчя та імʼя, і рецепція відповідає людиною та 200. Нижній, позначений anon, має порожні пунктирні фото й рядок для імені, і рецепція відповідає 403.","Печатку перевіряють першою, й обидва бейджі її проходять. Не проходить друге питання: чиє імʼя на ньому.","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fthe-name-line-1600x720.png",[37,104,106],{"id":105},"ключ-називає-ваш-застосунок-токен-сесії-називає-людину","Ключ називає ваш застосунок. Токен сесії називає людину.",[10,108,109],{},"Ключ API вашого проєкту і є тим бейджем з порожнім рядком для імені, і зробили\nйого таким навмисно.",[10,111,112,117],{},[52,113,116],{"href":114,"rel":115},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fgetting-started\u002Fapi-keys",[56],"Документація Supabase про ключі API","\nкаже це одним рядком: за словами Supabase, ключ API розрізняє не користувачів, а\nзастосунки. Ключ повідомляє Supabase, який застосунок викликає. Хто саме цим\nзастосунком користується, окреме питання, і на нього відповідає токен, який\nSupabase Auth видає кожній людині, коли вона входить.",[10,119,120,121,124,125,127],{},"Довідник Supabase про JWT наводить обидва. Ключ ",[21,122,123],{},"anon"," старішого проєкту\nрозшифровується ось так, і ",[21,126,49],{}," у ньому немає ніде:",[14,129,132],{"className":130,"code":131,"language":19},[17],"{\n  \"iss\": \"supabase\",\n  \"ref\": \"abcdefghijklmnopqrst\",\n  \"role\": \"anon\",\n  \"iat\": 1640991600,\n  \"exp\": 1640995200\n}\n",[21,133,131],{"__ignoreMap":23},[10,135,136,137,140],{},"Токен доступу людини, що ввійшла, містить її id, роль ",[21,138,139],{},"authenticated"," і сесію,\nдо якої він належить (тут скорочено до рядків, що мають значення):",[14,142,145],{"className":143,"code":144,"language":19},[17],"{\n  \"sub\": \"123e4567-e89b-12d3-a456-426614174000\",\n  \"role\": \"authenticated\",\n  \"session_id\": \"123e4567-e89b-12d3-a456-426614174000\",\n  \"email\": \"user@example.com\"\n}\n",[21,146,144],{"__ignoreMap":23},[10,148,149,150,152,153,156,157,159,160,92,163,166,167,172],{},"Ключ ",[21,151,29],{}," має ту саму першу форму, з ",[21,154,155],{},"\"role\": \"service_role\"",", і теж\nбез ",[21,158,49],{},". Новіші ключі ",[21,161,162],{},"sb_publishable_",[21,164,165],{},"sb_secret_"," взагалі не є JWT, але\n",[52,168,171],{"href":169,"rel":170},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ftroubleshooting\u002Fauth-error-401-invalid-claim-missing-sub--AFwMR",[56],"сторінка Supabase з усунення цієї помилки","\nкаже, що Auth їх розпізнає і що claim sub у них теж немає. Тож будь-який ключ\nвашого проєкту, старий чи новий, публічний чи секретний, дає це повідомлення,\nколи його передають Auth так, ніби це людина. Та сама сторінка прямо називає\nпомилку: випадково використати ключ API Supabase замість токена доступу Auth.",[10,174,175,176,180],{},"Якщо ключі та їхні префікси для вас нові,\n",[52,177,179],{"href":178},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","які ключі API можна тримати у фронтенді","\nпояснює, як відрізнити публічний від секретного.",[37,182,184],{"id":183},"де-ключ-надсилають-замість-сесії","Де ключ надсилають замість сесії",[10,186,187],{},"Сюди ведуть три шляхи, і першим іде Edge Function.",[10,189,190,193,194,197,198,201],{},[32,191,192],{},"1. Edge Function, яку викликають зі сторінки, де ніхто не ввійшов."," Коли\nsupabase-js має сесію, він кладе токен людини в заголовок ",[21,195,196],{},"Authorization",". Коли\nсесії немає, покласти туди йому більше нічого, тож зі старим ключем він\nнадсилає в цьому заголовку ключ вашого проєкту. Його файл ",[21,199,200],{},"fetch.ts"," так і\nчитається: токен сесії, якщо він є, а інакше ключ.",[10,203,204,205,209],{},"Перевірка платформи Verify JWT пропускає такий запит. Старий ключ це JWT,\nпідписаний вашим проєктом, а Supabase документує, що перевірка приймає й новіші\nключі: саме цю шпарину докладно пояснює\n",[52,206,208],{"href":207},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","як перенести секрет в Edge Function",".\nДалі ваша функція передає заголовок до Auth, і це роблять ось ці рядки:",[14,211,216],{"className":212,"code":214,"language":215,"meta":23},[213],"language-ts","const supabase = createClient(\n  Deno.env.get('SUPABASE_URL')!,\n  Deno.env.get('SUPABASE_ANON_KEY')!,\n  { global: { headers: { Authorization: req.headers.get('Authorization')! } } },\n)\nconst { data: { user } } = await supabase.auth.getUser()\n","ts",[21,217,214],{"__ignoreMap":23},[10,219,220,221,224],{},"Auth читає ключ, знаходить порожній рядок з імʼям і відповідає 403. Якщо функція\nповертає цю невдачу, ваш застосунок показує ",[21,222,223],{},"Edge Function returned a non-2xx status code",", а це повідомлення чекає в лозі функції.",[10,226,227,230,231,234],{},[32,228,229],{},"2. Виклик до того, як вхід завершився."," Сторінка Supabase з усунення проблем\nназиває і цей випадок: користувач почав реєструватися чи входити й не завершив\nцього, коли викликали ",[21,232,233],{},"getUser",". Одна з форм цього: реєстрація, яка ще чекає на\nлист із підтвердженням. Сесії ще немає, тож те, що дійшло до Auth, не було\nтокеном людини.",[10,236,237,240,241,244,245,248,249,252,253,255,256,258],{},[32,238,239],{},"3. Ваш власний сервер передає Auth не те значення."," Маршрут, який викликає\n",[21,242,243],{},"getUser(token)"," з ключем у ",[21,246,247],{},"token",", або автоматизація в n8n, Make чи Postman,\nяка звертається до ",[21,250,251],{},"\u002Fauth\u002Fv1\u002Fuser"," з ключем у заголовку ",[21,254,196],{},".\nСторінка Supabase радить для цього випадку переконатися, що токен, переданий у\n",[21,257,91],{},", є токеном доступу людини.",[98,260],{"alt":261,"caption":262,"src":263},"Сторінка, де ніхто не ввійшов, надсилає два однакові порожні бейджі: один позначений apikey, другий Authorization. Обидва проходять перевірку платформи з галочкою й доходять до Edge Function. Функція передає бейдж Authorization на рецепцію, яка відповідає 403 і хрестиком.","Коли ніхто не ввійшов, сторінка надсилає той самий ключ в обох заголовках. Кожна перевірка до Auth його пропускає, і лише питання Auth знаходить рядок з імʼям порожнім.","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fthe-same-key-twice-1600x760.png",[37,265,267],{"id":266},"як-побачити-що-отримала-ваша-функція","Як побачити, що отримала ваша функція",[10,269,270],{},"Порівняйте два заголовки запиту у своєму браузері.",[10,272,273,274,277,278,281],{},"Відкрийте опублікований застосунок, натисніть F12, оберіть вкладку ",[32,275,276],{},"Network"," і\nповторіть те, що не вдалося. Клацніть запит, адреса якого містить\n",[21,279,280],{},"\u002Ffunctions\u002Fv1\u002F"," і назву вашої функції, і прочитайте його заголовки запиту:",[283,284,285,302,313],"ul",{},[286,287,288,301],"li",{},[32,289,290,292,293,296,297,300],{},[21,291,196],{}," містить ",[21,294,295],{},"Bearer",", а далі той самий рядок, що й ",[21,298,299],{},"apikey",".","\nСторінці не було чого надсилати як сесію, тож вона надіслала ключ двічі. Ви на\nшляху 1 або 2.",[286,303,304,312],{},[32,305,306,308,309,300],{},[21,307,196],{}," містить інший довгий рядок, що починається з ",[21,310,311],{},"eyJ"," Токен\nлюдини пішов. Функція чи ваш сервер загубили його дорогою, тож ви на шляху 3.",[286,314,315,321,322,326,327,330],{},[32,316,317,318,320],{},"Заголовка ",[21,319,196],{}," немає."," Тоді ви побачите не це повідомлення. Коли\nVerify JWT увімкнено, платформа відхиляє запит з 401 ще до запуску вашого\nкоду, і\n",[52,323,325],{"href":324},"\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code","допис про помилку non-2xx","\nрозбирає це. Коли вимкнено, Auth відповідає ",[21,328,329],{},"This endpoint requires a valid Bearer token",", першим рядком таблиці нижче.",[10,332,333,334,337,338,300],{},"Якщо ж повідомлення в консолі браузера і звучить як ",[21,335,336],{},"Auth session missing!",",\nжоден запит до Auth не дійшов: supabase-js не знайшла сесії й зупинилася, ще не\nнадіславши його, і про це\n",[52,339,341],{"href":340},"\u002Fblog\u002Fauth-session-missing","окрема стаття",[10,343,344],{},"Читайте значення там, де вони є. Не вставляйте жодне з них на сайт, який\nпропонує його розшифрувати: ваш ключ і так публічний, але токен доступу людини\nдає будь-кому, хто його має, змогу діяти від її імені, доки він не спливе.",[37,346,348],{"id":347},"виправлення-питати-лише-тоді-коли-хтось-увійшов","Виправлення: питати лише тоді, коли хтось увійшов",[10,350,351],{},"Вирішіть, що функція має робити для відвідувача, який не ввійшов, і впишіть це\nрішення у функцію.",[10,353,354,357,358,360],{},[32,355,356],{},"Якщо нею мають користуватися лише ті, хто ввійшов,"," потрібні дві зміни.\nСторінка викликає функцію лише після того, як хтось увійшов. А функція\nвідповідає чітким 401, коли ",[21,359,91],{}," не знаходить користувача, щоб випадковий\nвиклик зі сторінки без входу отримав зрозумілу відмову замість збою:",[14,362,365],{"className":363,"code":364,"language":215,"meta":23},[213],"const { data: { user } } = await supabase.auth.getUser()\nif (!user) {\n  return new Response(JSON.stringify({ error: 'Sign in first' }), {\n    status: 401,\n    headers: { ...corsHeaders, 'Content-Type': 'application\u002Fjson' },\n  })\n}\n",[21,366,364],{"__ignoreMap":23},[10,368,369,370,374,375,378,379,382],{},"Залиште заголовки CORS і в цій відповіді, інакше браузер сховає її від вашого\nзастосунку, і замість неї ви натрапите на\n",[52,371,373],{"href":372},"\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function","\"Failed to send a request to the Edge Function\"",".\nSupabase також має обгортку ",[21,376,377],{},"withSupabase({ auth: 'user' })",", яка робить ту саму\nперевірку до запуску вашого обробника;\n",[52,380,381],{"href":207},"допис про Edge Function"," показує, як її\nналаштувати.",[10,384,385,388],{},[32,386,387],{},"Якщо нею мають користуватися й відвідувачі без входу,"," питайте Auth лише на\nтому шляху, якому потрібна людина. Сприймайте відмову там як \"це відвідувач\" і\nпродовжуйте з тим, що відвідувачеві дозволено, замість того щоб дати помилці\nзупинити запит.",[10,390,391,394,395,398,399,402],{},[32,392,393],{},"На власному сервері"," надсилайте токен доступу людини з браузера, як описує\nсторінка Supabase: покладіть його в заголовок ",[21,396,397],{},"Authorization: Bearer"," свого\nзапиту, прочитайте на сервері й викличте ",[21,400,401],{},"supabase.auth.getUser(token)"," з цим\nзначенням. Ключ вашого проєкту в цей виклик не потрапляє ніколи.",[37,404,406],{"id":405},"два-виправлення-після-яких-стає-гірше","Два виправлення, після яких стає гірше",[10,408,409],{},"Обидва радять для цієї помилки, й обидва лишають вас у гіршому стані, ніж сама\nпомилка.",[10,411,412,415,416,418,419,421,422,424,425,300],{},[32,413,414],{},"Підставити секретний ключ."," Це не може допомогти, бо ",[21,417,29],{}," і\n",[21,420,165],{}," теж не мають ",[21,423,49],{},", тож Auth відповідає так само. Шкода в тому, куди\nпотрапляє ключ. Попросіть конструктор \"взяти сервісний ключ\", щоб обійти\nпомилку, і він цілком може покласти його на сторінку, яку завантажують ваші\nвідвідувачі, де ключ читає й змінює кожен рядок вашої бази, хоч би що казали\nваші правила. Якщо це вже сталося,\n",[52,426,428],{"href":427},"\u002Fblog\u002Frotate-supabase-service-role-key","спершу замініть його",[10,430,431,432,300],{},"Цей промах видно ззовні. Наше безкоштовне сканування читає JavaScript вашого\nопублікованого застосунку так, як прочитав би сторонній, і називає кожен ключ,\nякий знаходить, зокрема й те, чи має він там бути. Це займає близько 20 секунд і\nне потребує облікового запису:\n",[52,433,435],{"href":434},"\u002Fsecurity-scanner","проскануйте свій застосунок",[10,437,438,441,442,445],{},[32,439,440],{},"Прибрати перевірку й брати id користувача із запиту."," Функція, що читає\n",[21,443,444],{},"userId"," з тіла запиту, вірить будь-кому, хто його надішле. Якщо ця функція ще й\nзаписує з секретним ключем, будь-хто, хто відкриє вашу сторінку й прочитає, як\nвикликається функція, може діяти як будь-який ваш користувач, і ніщо у ваших\nлогах не скаже, що це були не вони. Коли перевірка лишається на місці,\nсторонній, що спробує той самий виклик, отримає цю помилку й більше нічого.",[37,447,449],{"id":448},"інші-повідомлення-тієї-самої-перевірки","Інші повідомлення тієї самої перевірки",[10,451,452],{},"Перевірки Auth ідуть у сталому порядку, і в кожної своє речення, тож\nповідомлення каже, як далеко пройшов токен.",[454,455,456,472],"table",{},[457,458,459],"thead",{},[460,461,462,466,469],"tr",{},[463,464,465],"th",{},"Повідомлення",[463,467,468],{},"Статус і код",[463,470,471],{},"Що сталося",[473,474,475,494,509,523,538,553],"tbody",{},[460,476,477,482,488],{},[478,479,480],"td",{},[21,481,329],{},[478,483,484,485],{},"401 ",[21,486,487],{},"no_authorization",[478,489,490,491,493],{},"Заголовок ",[21,492,397],{}," не прийшов зовсім.",[460,495,496,501,506],{},[478,497,498],{},[21,499,500],{},"invalid JWT: unable to parse or verify signature, …",[478,502,503,504],{},"403 ",[21,505,75],{},[478,507,508],{},"Печатка не та: пошкоджений токен, токен з іншого проєкту або старий ключ.",[460,510,511,516,520],{},[478,512,513],{},[21,514,515],{},"invalid claim: missing sub claim",[478,517,503,518],{},[21,519,75],{},[478,521,522],{},"Печатка правильна, а рядок з імʼям порожній: ключ API.",[460,524,525,530,535],{},[478,526,527],{},[21,528,529],{},"invalid claim: sub claim must be a UUID",[478,531,532,533],{},"400 ",[21,534,75],{},[478,536,537],{},"Імʼя є, але не у формі id користувача Supabase.",[460,539,540,545,550],{},[478,541,542],{},[21,543,544],{},"User from sub claim in JWT does not exist",[478,546,503,547],{},[21,548,549],{},"user_not_found",[478,551,552],{},"Токен називає користувача, якого вже видалили.",[460,554,555,560,565],{},[478,556,557],{},[21,558,559],{},"Session from session_id claim in JWT does not exist",[478,561,503,562],{},[21,563,564],{},"session_not_found",[478,566,567],{},"Токен пережив свою сесію, наприклад після виходу на іншому пристрої.",[10,569,570,571,573,574,577,578,582,583,300],{},"Кожен рядок походить з одного файлу, ",[21,572,68],{},". Версія рядка з\nнеправильною печаткою, яку повертає сама ваша база даних, ",[21,575,576],{},"PGRST301",", має\n",[52,579,581],{"href":580},"\u002Fblog\u002Fsupabase-invalid-jwt","окремий допис",". Рядок про сесію варто читати разом\nіз дописом ",[52,584,586],{"href":585},"\u002Fblog\u002Fsupabase-users-logged-out","чому користувачів Supabase розлогінює",[37,588,590],{"id":589},"щоб-перевірка-лишилася-після-наступної-зміни","Щоб перевірка лишилася після наступної зміни",[10,592,593,596],{},[32,594,595],{},"Перевірка того, хто викликає, може зникнути, коли ви наступного разу\nпопросите конструктор прибрати якусь помилку",", а два найшвидші способи\nзаглушити цю описані вище.",[10,598,599],{},[32,600,601,605],{},[52,602,604],{"href":603},"\u002Fwebsite-monitoring","Reeve Monitor"," щогодини заново сканує ваш опублікований застосунок:",[283,607,608,611,614,617],{},[286,609,610],{},"усі девʼять перевірок щогодини, для трьох застосунків",[286,612,613],{},"лист того дня, коли нове сканування оцінить ваш застосунок гірше за попереднє; секретний ключ у вашому JavaScript сам обмежує оцінку рівнем D",[286,615,616],{},"чи застосунок доступний, кожні 60 секунд",[286,618,619],{},"щомісячний звіт про те, що він бачив",[10,621,622],{},[32,623,624,625,629],{},"Функція, яка вірить надісланому їй id користувача, може змінити рядки\nбудь-кого з ваших користувачів для будь-кого, хто її викликає.\n",[52,626,628],{"href":627},"\u002Fsupabase-backups","Reeve Care"," тримає копію вашої бази Supabase з часу до того,\nяк це станеться.",[283,631,632,635,638,641,644],{},[286,633,634],{},"зашифрована копія за розкладом, у власному сховищі Reeve в ЄС, поза вашим обліковим записом Supabase",[286,636,637],{},"кожна копія перевірена, перш ніж її зарахують, підрахунком рядків у кожній таблиці",[286,639,640],{},"відновлення в один клік, яке зберігає те, що є зараз, перш ніж замінити",[286,642,643],{},"ваші завантажені файли теж, щойно ви приєднаєте доступ до Storage",[286,645,646],{},"усе, що робить Monitor",[37,648,650],{"id":649},"що-зробити-зараз","Що зробити зараз",[652,653,654],"key-takeaways",{},[283,655,656,665,671,677,680],{},[286,657,658,659,661,662,664],{},"Відкрийте запит, що не вдався, на вкладці Network. Якщо ",[21,660,196],{}," має те саме значення, що й ",[21,663,299],{},", на момент надсилання ніхто не ввійшов.",[286,666,667,668,670],{},"Викликайте функції, яким потрібна людина, лише після входу, і нехай функція відповідає 401, коли ",[21,669,91],{}," не знаходить користувача.",[286,672,673,674,676],{},"На власному сервері передавайте в ",[21,675,91],{}," токен доступу людини. У ключі вашого проєкту користувача немає.",[286,678,679],{},"Лишіть секретний ключ там, де він є. Він теж нікого не називає, а на сторінці читає й змінює кожен рядок.",[286,681,682],{},"Залиште перевірку того, хто викликає. Функція, що бере id користувача із запиту, вірить будь-кому.",[10,684,685,686,688,689,692,693,697],{},"Якщо ваш застосунок показує ",[21,687,223],{},", поки\nце повідомлення лежить у лозі, функція передає відмову далі, і\n",[52,690,691],{"href":324},"той допис"," читає відповідь\nзвідти. ",[52,694,696],{"href":695},"\u002Fchecklist","Десятихвилинний чекліст безпеки"," охоплює, що ще новий\nзастосунок зазвичай лишає відкритим.",{"title":23,"searchDepth":699,"depth":699,"links":700},3,[701,703,704,705,706,707,708,709,710],{"id":39,"depth":702,"text":40},2,{"id":105,"depth":702,"text":106},{"id":183,"depth":702,"text":184},{"id":266,"depth":702,"text":267},{"id":347,"depth":702,"text":348},{"id":405,"depth":702,"text":406},{"id":448,"depth":702,"text":449},{"id":589,"depth":702,"text":590},{"id":649,"depth":702,"text":650},"Основи безпеки","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fcover-1200x630.png","Бейдж на шнурку зі справжньою печаткою й порожнім рядком для імені, перед двома бейджами, на яких є імʼя та обличчя.","\"invalid claim: missing sub claim\": Supabase Auth отримав справжній токен без жодного користувача, ключ проєкту замість сесії.",false,"md",[718,721,724,727,730],{"q":719,"a":720},"Що означає \"invalid claim: missing sub claim\" у Supabase?","Це означає, що Supabase Auth спитали, який користувач викликає, і дали йому токен, у якому немає жодного користувача. Сам токен був справжній і пройшов перевірку підпису. У ньому бракувало claim sub, поля, у якому зберігається id користувача. Саме так виглядають ключі API вашого проєкту, тому звична причина полягає в ключі, надісланому туди, де має бути токен користувача, що ввійшов.",{"q":722,"a":723},"Це те саме, що \"Auth session missing!\"?","Ні. \"Auth session missing!\" походить від supabase-js у браузері, який не знаходить жодної сесії й навіть не надсилає запит. \"invalid claim: missing sub claim\" повертає Supabase Auth, який отримав запит і знайшов ключ там, де мав бути токен користувача. Перше зазвичай означає сторінку, де ніхто не ввійшов; друге означає функцію чи сервер, що передали ключ далі.",{"q":725,"a":726},"Чи допоможе вимкнути Verify JWT?","Ні. Verify JWT це перевірка, яку Supabase робить для Edge Function до того, як запуститься ваш код, а це повідомлення приходить пізніше, від Supabase Auth, коли ваш код питає, хто викликає. Якщо вимкнути перевірку платформи, до вашого коду почнуть доходити ще й запити зовсім без токена, а помилка залишиться саме там, де була.",{"q":728,"a":729},"Чи спричиняє цю помилку анонімний вхід?","Ні. Анонімний вхід створює справжнього користувача з власним id, тож його токен має claim sub і is_anonymous зі значенням true. Ключ anon це інша річ, попри схожу назву: ключ проєкту без жодного користувача всередині, і саме він дає це повідомлення.",{"q":731,"a":732},"Чому на одних сторінках написано 401, а на інших 403?","Сторінка Supabase з усунення проблем для цього повідомлення має 401 у заголовку. Поточний код Supabase Auth відповідає 403 з кодом помилки bad_jwt. Якщо ваш код вирішує, що робити, лише за статусом, перевіряйте також код помилки й повідомлення.","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fcard-800x500.png",[735,515,736,737,738,739,740],"invalid claim missing sub claim supabase","AuthApiError invalid claim missing sub claim","bad_jwt supabase","supabase getuser 403","помилка getuser supabase edge function","supabase anon key getuser",{},true,"\"invalid claim: missing sub claim\" у Supabase","\u002Fblog\u002Finvalid-claim-missing-sub-claim","2026-11-04",{"title":5,"description":714},"blog\u002Finvalid-claim-missing-sub-claim",[749,750,751],"\"invalid claim: missing sub claim\" означає, що Supabase Auth відповідає 403 з кодом bad_jwt. Його спитали, хто викликає, токен, який він отримав, був справжній, і в ньому не було нікого.","Сторінка Supabase з усунення проблем називає поширену причину: ключ API, надісланий туди, де має бути токен доступу користувача, що ввійшов. Ключ каже, який застосунок викликає, і жодної людини в ньому немає.","Викликайте функцію лише тоді, коли хтось увійшов, і нехай вона відповідає 401, коли ніхто не ввійшов. Підставити секретний ключ нічого не змінить, бо цей ключ теж нікого не називає.","QR-QBM-diuYRAk1ymJT3NfPwFsRXZgwrV0-BQ1mdfG4",[754,760,766,772,778,783,784,789,795,801,807,814,820,826,831,837,843,849,855,861,867,873,878,884,890,895,901,907,913,919,925,931,937,943,949,955,960,966,972,978,984,990,996,1002,1008,1014,1019,1025,1031,1037,1043,1049,1055,1061,1067,1073,1079,1085,1091,1097,1103,1109,1115,1121,1126,1132,1138,1144,1150,1156,1162,1168,1174,1180,1186,1192,1198,1204,1210,1216,1222,1228,1234,1240,1245,1251,1257,1263,1268,1274],{"path":755,"title":756,"description":757,"published":758,"category":711,"image":759,"draft":715},"\u002Fblog\u002Fmax-client-connections-reached","Supabase \"max client connections reached\": пояснення","Supabase \"max client connections reached\": у пулера немає місця ще для одного клієнта. Ліміт визначає порт у вашому рядку підключення.","2026-11-09","\u002Fblog\u002Fmax-client-connections-reached\u002Fcard-800x500.png",{"path":761,"title":762,"description":763,"published":764,"category":711,"image":765,"draft":715},"\u002Fblog\u002Funsupported-provider-provider-is-not-enabled","Помилка Supabase: \"Unsupported provider: provider is not enabled\"","\"Unsupported provider: provider is not enabled\": вхід через Google вимкнено у вашому проєкті Supabase. Як увімкнути та виправити redirect_uri_mismatch.","2026-11-08","\u002Fblog\u002Funsupported-provider-provider-is-not-enabled\u002Fcard-800x500.png",{"path":767,"title":768,"description":769,"published":770,"category":711,"image":771,"draft":715},"\u002Fblog\u002Fviolates-foreign-key-constraint","Помилка Supabase \"violates foreign key constraint\" під час вставки","Supabase \"insert or update on table violates foreign key constraint\": ваш рядок посилається на рядок, якого немає. Який, чому і як це виправити.","2026-11-07","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcard-800x500.png",{"path":773,"title":774,"description":775,"published":776,"category":711,"image":777,"draft":715},"\u002Fblog\u002Fnull-value-violates-not-null-constraint","Помилка Supabase \"null value in column violates not-null constraint\"","Supabase \"null value in column violates not-null constraint\": обовʼязковий стовпець прийшов порожнім. Звідки взявся null і як це виправити.","2026-11-06","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcard-800x500.png",{"path":340,"title":779,"description":780,"published":781,"category":711,"image":782,"draft":715},"Supabase \"Auth session missing!\": куди поділася сесія","\"Auth session missing!\" означає, що supabase-js не знайшла жодної сесії й навіть не спитала Supabase. Куди поділася сесія і як це виправити.","2026-11-05","\u002Fblog\u002Fauth-session-missing\u002Fcard-800x500.png",{"path":744,"title":5,"description":714,"published":745,"category":711,"image":733,"draft":715},{"path":372,"title":785,"description":786,"published":787,"category":711,"image":788,"draft":715},"\"Failed to send a request to the Edge Function\": справжня причина","\"Failed to send a request to the Edge Function\": браузер не отримав відповіді, яку можна прочитати. Його консоль каже, чому, і чи ваша функція запускалася.","2026-11-03","\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function\u002Fcard-800x500.png",{"path":790,"title":791,"description":792,"published":793,"category":711,"image":794,"draft":715},"\u002Fblog\u002Fsupabase-auth-redirects-to-localhost","Чому Supabase перенаправляє посилання з листів на localhost:3000","Якщо Supabase перенаправляє на localhost:3000, Site URL так і не змінили або вашої адреси немає в Redirect URLs. Як виправити і якої маски уникати.","2026-11-02","\u002Fblog\u002Fsupabase-auth-redirects-to-localhost\u002Fcard-800x500.png",{"path":796,"title":797,"description":798,"published":799,"category":711,"image":800,"draft":715},"\u002Fblog\u002Femail-link-is-invalid-or-has-expired","Supabase \"Email link is invalid or has expired\" на новому посиланні","На посиланні, надісланому хвилину тому, \"Email link is invalid or has expired\" у Supabase зазвичай означає, що поштовий фільтр використав його першим.","2026-11-01","\u002Fblog\u002Femail-link-is-invalid-or-has-expired\u002Fcard-800x500.png",{"path":802,"title":803,"description":804,"published":805,"category":711,"image":806,"draft":715},"\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout","Supabase \"canceling statement due to statement timeout\": пояснення","Supabase \"canceling statement due to statement timeout\": запит перевищив ліміт своєї ролі, 3 секунди для відвідувача. Спершу перевірте політику RLS.","2026-10-31","\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout\u002Fcard-800x500.png",{"path":808,"title":809,"description":810,"published":811,"category":812,"image":813,"draft":715},"\u002Fblog\u002Fdatabase-error-deleting-user","\"Database error deleting user\" у Supabase і що зникає разом","\"Database error deleting user\" у Supabase: ваша база відмовилася видаляти. Причина в логах, а виправлення вирішує, що ще буде видалено.","2026-10-30","Backups","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fcard-800x500.png",{"path":815,"title":816,"description":817,"published":818,"category":711,"image":819,"draft":715},"\u002Fblog\u002Fstripe-webhook-no-signatures-found","\"No signatures found matching the expected signature\" у Supabase","Вебхук Stripe в Edge Function Supabase пише \"No signatures found matching the expected signature\"? Причин дві. Не вимикайте перевірку.","2026-10-29","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fcard-800x500.png",{"path":821,"title":822,"description":823,"published":824,"category":711,"image":825,"draft":715},"\u002Fblog\u002Fsupabase-invalid-login","\"Invalid login credentials\" у Supabase: три причини","Supabase відповідає \"Invalid login credentials\" на неправильний пароль, невідому пошту й акаунт Google. Як зрозуміти, що саме.","2026-10-28","\u002Fblog\u002Fsupabase-invalid-login\u002Fcard-800x500.png",{"path":324,"title":827,"description":828,"published":829,"category":711,"image":830,"draft":715},"\"Edge Function returned a non-2xx status code\": справжня помилка","\"Edge Function returned a non-2xx status code\" означає, що ваша функція Supabase відповіла помилкою. Ось де прочитати справжню.","2026-10-27","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fcard-800x500.png",{"path":832,"title":833,"description":834,"published":835,"category":711,"image":836,"draft":715},"\u002Fblog\u002Fcertificate-not-trusted","Сертифікат не довірений: три причини, крім закінчення строку","Сертифікат не довірений, хоча дата в порядку? Він називає іншу адресу, підписав сам себе або в його ланцюжку бракує ланки. Як зрозуміти, що саме.","2026-10-26","\u002Fblog\u002Fcertificate-not-trusted\u002Fcard-800x500.png",{"path":838,"title":839,"description":840,"published":841,"category":711,"image":842,"draft":715},"\u002Fblog\u002Finvalid-input-syntax-for-type-uuid","Supabase \"invalid input syntax for type uuid\": що в лапках","Supabase \"invalid input syntax for type uuid\" означає, що застосунок надіслав щось, що не є id. Прочитайте лапки, притримайте запит, залиште колонку.","2026-10-25","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fcard-800x500.png",{"path":844,"title":845,"description":846,"published":847,"category":711,"image":848,"draft":715},"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","Supabase \"duplicate key value violates unique constraint\": пояснення","Supabase \"duplicate key value violates unique constraint\": значення вже зайняте. Знайдіть стовпець, оберіть виправлення й сховайте значення від очей.","2026-10-24","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fcard-800x500.png",{"path":850,"title":851,"description":852,"published":853,"category":711,"image":854,"draft":715},"\u002Fblog\u002Fsupabase-relation-does-not-exist","Supabase \"relation does not exist\", хоча таблиця на місці","Supabase \"relation does not exist\" або PGRST205: жодна таблиця не має такої назви в цій схемі. Чотири перевірки, і на що зважити, відкриваючи схему.","2026-10-23","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fcard-800x500.png",{"path":856,"title":857,"description":858,"published":859,"category":711,"image":860,"draft":715},"\u002Fblog\u002Fsupabase-function-not-found","Supabase PGRST202: \"Could not find the function in the schema cache\"","Supabase \"Could not find the function in the schema cache\" показує назви аргументів, які надіслав ваш застосунок. Порівняйте їх із функцією.","2026-10-22","\u002Fblog\u002Fsupabase-function-not-found\u002Fcard-800x500.png",{"path":862,"title":863,"description":864,"published":865,"category":711,"image":866,"draft":715},"\u002Fblog\u002Fsupabase-schema-cache-column-error","Supabase PGRST204: \"Could not find the column in the schema cache\"","Supabase \"Could not find the column in the schema cache\" зазвичай означає, що назва не збігається. Чотири перевірки і рядок для перезавантаження.","2026-10-21","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fcard-800x500.png",{"path":868,"title":869,"description":870,"published":871,"category":711,"image":872,"draft":715},"\u002Fblog\u002Fsupabase-email-rate-limit-exceeded","Supabase \"Email Rate Limit Exceeded\": два ліміти, один код помилки","Supabase \"Email Rate Limit Exceeded\" обмежує кожен лист вашого проєкту: 2 на годину з вбудованим відправником. Що враховується і як підняти ліміт.","2026-10-20","\u002Fblog\u002Fsupabase-email-rate-limit-exceeded\u002Fcard-800x500.png",{"path":585,"title":874,"description":875,"published":876,"category":711,"image":877,"draft":715},"Supabase \"Invalid Refresh Token: Refresh Token Not Found\": 3 причини","Supabase \"Invalid Refresh Token: Refresh Token Not Found\" виводить ваших користувачів із системи. Три причини і як лог Auth їх розрізняє.","2026-10-19","\u002Fblog\u002Fsupabase-users-logged-out\u002Fcard-800x500.png",{"path":879,"title":880,"description":881,"published":882,"category":711,"image":883,"draft":715},"\u002Fblog\u002Fpermission-denied-for-schema-public","Supabase \"permission denied for schema public\": три причини","Supabase \"permission denied for schema public\" означає, що бракує GRANT на схему. Що його прибрало і який рядок його повертає.","2026-10-18","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fcard-800x500.png",{"path":885,"title":886,"description":887,"published":888,"category":711,"image":889,"draft":715},"\u002Fblog\u002Fsupabase-bucket-not-found","Supabase \"Bucket not found\": три речі, які це може означати","Supabase \"Bucket not found\" означає одне з трьох: публічне посилання на приватний бакет, неточну назву або не той проєкт.","2026-10-17","\u002Fblog\u002Fsupabase-bucket-not-found\u002Fcard-800x500.png",{"path":580,"title":891,"description":892,"published":893,"category":711,"image":894,"draft":715},"Supabase invalid JWT: який ключ перестав працювати?","Помилка Supabase invalid JWT — це PGRST301: підписаний токен не пройшов перевірку. У новому проєкті йдеться про сесію, а не про ключ API.","2026-10-16","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcard-800x500.png",{"path":896,"title":897,"description":898,"published":899,"category":711,"image":900,"draft":715},"\u002Fblog\u002Fsupabase-single-row-error","Supabase \"multiple (or no) rows returned\" майже завжди означає нуль","Supabase повертає PGRST116, \"JSON object requested, multiple (or no) rows returned\", коли запит нічого не знайшов. Прихований рядок має вигляд відсутнього.","2026-10-15","\u002Fblog\u002Fsupabase-single-row-error\u002Fcard-800x500.png",{"path":902,"title":903,"description":904,"published":905,"category":711,"image":906,"draft":715},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"Database error saving new user\" у Supabase: де шукати","\"Database error saving new user\" означає, що щось причеплене до вашої реєстрації в Supabase впало. Справжня помилка лежить у ваших логах.","2026-10-14","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Fcard-800x500.png",{"path":908,"title":909,"description":910,"published":911,"category":711,"image":912,"draft":715},"\u002Fblog\u002Fwhat-is-supabase","Що таке Supabase? База даних, яку обрав ваш білдер","Що таке Supabase: база даних, вхід і сховище файлів за вашим застосунком. Supabase тримає сервери, а правила про те, хто читає ваші рядки, ваші.","2026-10-13","\u002Fblog\u002Fwhat-is-supabase\u002Fcard-800x500.png",{"path":914,"title":915,"description":916,"published":917,"category":711,"image":918,"draft":715},"\u002Fblog\u002Faws-access-key-exposed-in-frontend","Відкритий у фронтенді ключ AWS це не те саме, що ключ OpenAI","Ключ доступу AWS, відкритий у вашому фронтенді, читає і пише ваше сховище. Рішення зазвичай presigned URL, а не новий ключ.","2026-10-12","\u002Fblog\u002Faws-access-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":920,"title":921,"description":922,"published":923,"category":711,"image":924,"draft":715},"\u002Fblog\u002Fstorage-upload-violates-row-level-security","\"Row-level security policy for table objects\" при вивантаженні","\"New row violates row-level security policy for table objects\" означає, що вашому вивантаженню бракує правила insert. Публічний бакет його не додає.","2026-10-11","\u002Fblog\u002Fstorage-upload-violates-row-level-security\u002Fcard-800x500.png",{"path":926,"title":927,"description":928,"published":929,"category":711,"image":930,"draft":715},"\u002Fblog\u002Fbase44-security-scan","Сканування безпеки Base44: те, що бачить лише воно","Сканування безпеки Base44 перевіряє сім видів проблем зсередини. Ось половина, яку читає тільки воно, і половина, на яку воно ніколи не дивиться.","2026-10-10","\u002Fblog\u002Fbase44-security-scan\u002Fcard-800x500.png",{"path":932,"title":933,"description":934,"published":935,"category":711,"image":936,"draft":715},"\u002Fblog\u002Fapi-key-leaked-what-to-do","Ваш API-ключ витік. Порядок дій","API-ключ витік, і ви не знаєте, що робити першим. Не кожен ключ у вашому фронтенді є витоком, а порядок важить більше за швидкість.","2026-10-09","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcard-800x500.png",{"path":938,"title":939,"description":940,"published":941,"category":711,"image":942,"draft":715},"\u002Fblog\u002Finfinite-recursion-in-policy-for-relation","\"Infinite recursion detected in policy\" без вимкнення RLS","\"Infinite recursion detected in policy for relation\" означає, що ваша policy запитала таблицю, яку вона захищає. Ось як розірвати коло.","2026-10-08","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fcard-800x500.png",{"path":944,"title":945,"description":946,"published":947,"category":711,"image":948,"draft":715},"\u002Fblog\u002Fno-api-key-found-in-request","\"No API key found in request\" у Supabase, і хибне виправлення","\"No API key found in request\" означає, що запит дійшов до Supabase без ключа. Більшість відповідей натомість вказують на правила вашої бази.","2026-10-07","\u002Fblog\u002Fno-api-key-found-in-request\u002Fcard-800x500.png",{"path":950,"title":951,"description":952,"published":953,"category":711,"image":954,"draft":715},"\u002Fblog\u002Fis-base44-safe","Чи безпечний Base44? Дані 5 442 застосунків","Чи безпечний Base44? Ми прогнали дев’ять перевірок на 5 442 робочих застосунках Base44. Майже всі отримали B з причин, яких власник ніколи не обирав.","2026-10-06","\u002Fblog\u002Fis-base44-safe\u002Fcard-800x500.png",{"path":207,"title":956,"description":957,"published":958,"category":711,"image":959,"draft":715},"Сховати ключ API: перенесіть його в Supabase Edge Function","Сховати ключ API означає прибрати його з браузера, а Supabase Edge Function це найменше місце, куди його покласти. Два кроки поруч важать більше.","2026-10-05","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":961,"title":962,"description":963,"published":964,"category":711,"image":965,"draft":715},"\u002Fblog\u002Fenv-file-exposed-on-your-server","Чи відкритий ваш файл .env? Дванадцять шляхів для перевірки","Чи відкритий ваш файл .env на вашому власному вебсервері? Дванадцять адрес скажуть це за хвилину, а влучання означає, що все в ньому вже публічне.","2026-10-04","\u002Fblog\u002Fenv-file-exposed-on-your-server\u002Fcard-800x500.png",{"path":967,"title":968,"description":969,"published":970,"category":711,"image":971,"draft":715},"\u002Fblog\u002Fis-v0-safe","Безпека v0: усі 1 790 просканованих застосунків v0 отримали A","Безпека v0 на 1 790 живих застосунках v0: усі отримали A. Лише 17 згадували базу даних, і саме це здебільшого вимірює оцінка A.","2026-10-03","\u002Fblog\u002Fis-v0-safe\u002Fcard-800x500.png",{"path":973,"title":974,"description":975,"published":976,"category":711,"image":977,"draft":715},"\u002Fblog\u002Fis-bolt-safe","Чи безпечний Bolt? Що показали 1 123 застосунки Bolt","Чи безпечний Bolt? Ми прогнали 1 123 живі застосунки Bolt через дев’ять перевірок. Хостинг був чистим. Знахідки: ключі й відкриті таблиці в самих застосунках.","2026-10-02","\u002Fblog\u002Fis-bolt-safe\u002Fcard-800x500.png",{"path":979,"title":980,"description":981,"published":982,"category":711,"image":983,"draft":715},"\u002Fblog\u002Fis-supabase-down-or-your-app","Supabase не працює чи проблема у вашому застосунку? Як розрізнити","Supabase не працює чи зламався ваш застосунок? Пауза, повна база й ваша власна помилка виглядають однаково. Чотири перевірки їх розрізняють.","2026-10-01","\u002Fblog\u002Fis-supabase-down-or-your-app\u002Fcard-800x500.png",{"path":985,"title":986,"description":987,"published":988,"category":711,"image":989,"draft":715},"\u002Fblog\u002Fdoes-supabase-encrypt-my-data","Чи шифрує Supabase мої дані? Так. Ось від чого це захищає","Чи шифрує Supabase дані? Так: AES-256 у стані спокою, TLS під час передавання, SOC 2 та ISO 27001. Що покриває кожне і який витік не зупиняє жодне.","2026-09-30","\u002Fblog\u002Fdoes-supabase-encrypt-my-data\u002Fcard-800x500.png",{"path":991,"title":992,"description":993,"published":994,"category":711,"image":995,"draft":715},"\u002Fblog\u002Fsupabase-free-plan-limits","Ліміти безкоштовного тарифу Supabase і що стається на кожному","Ліміти безкоштовного тарифу Supabase: 500 МБ бази, 5 ГБ egress і жодних резервних копій. Що стається на кожній межі і що змінює Pro за $25.","2026-09-29","\u002Fblog\u002Fsupabase-free-plan-limits\u002Fcard-800x500.png",{"path":997,"title":998,"description":999,"published":1000,"category":812,"image":1001,"draft":715},"\u002Fblog\u002Ftest-your-supabase-backup","Перевірте резервну копію Supabase до того, як вона знадобиться","Як перевірити резервну копію Supabase: відновити в тестовий проєкт, порахувати рядки, увійти й знайти рядок, якого бракує обрізаному файлу.","2026-09-28","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":1003,"title":1004,"description":1005,"published":1006,"category":812,"image":1007,"draft":715},"\u002Fblog\u002Fsupabase-backup-github-action","Безкоштовна резервна копія Supabase через GitHub Action і підступ","Резервна копія Supabase через GitHub Action безкоштовна й годиться багатьом застосункам. Робочий процес, потрібний рядок підключення та egress.","2026-09-27","\u002Fblog\u002Fsupabase-backup-github-action\u002Fcard-800x500.png",{"path":1009,"title":1010,"description":1011,"published":1012,"category":812,"image":1013,"draft":715},"\u002Fblog\u002Fdownload-your-supabase-backup","Чому ви не можете завантажити резервну копію Supabase","Завантажити резервну копію Supabase на сучасному проєкті не вийде: щоденна копія там фізична. Як це перевірити і як тримати власну копію.","2026-09-26","\u002Fblog\u002Fdownload-your-supabase-backup\u002Fcard-800x500.png",{"path":1015,"title":1016,"description":1017,"published":1012,"category":711,"image":1018,"draft":715},"\u002Fblog\u002Fsupabase-permission-denied-for-table","Supabase \"permission denied for table\": бракує GRANT","З 30 жовтня нова таблиця в Supabase відповідає \"permission denied for table\", доки ви не дасте доступ. Лист показує лише половину виправлення.","\u002Fblog\u002Fsupabase-permission-denied-for-table\u002Fcard-800x500.png",{"path":1020,"title":1021,"description":1022,"published":1023,"category":711,"image":1024,"draft":715},"\u002Fblog\u002Fopen-api-endpoint-exposed","Чи є відкритий API-ендпоінт проблемою безпеки? Читайте JSON","Ваше сканування позначило відкритий API-ендпоінт. Чи це важливо, залежить від того, що повернулося, і більшість знайдених належали платформі.","2026-09-25","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fcard-800x500.png",{"path":1026,"title":1027,"description":1028,"published":1029,"category":711,"image":1030,"draft":715},"\u002Fblog\u002Flovable-security-scan","Сканування безпеки Lovable: чого воно не доведе","Сканування безпеки Lovable: що перевіряють Quick scan і Deep scan, коли запускається кожне, і єдине, чого не доведе жодне сканування зсередини.","2026-09-24","\u002Fblog\u002Flovable-security-scan\u002Fcard-800x500.png",{"path":1032,"title":1033,"description":1034,"published":1035,"category":812,"image":1036,"draft":715},"\u002Fblog\u002Fsupabase-backup-auth-users","Чому у вашому дампі Supabase немає користувачів","Запустіть supabase db dump окремо і отримаєте форму вашої бази та жодного її рядка, без схеми auth, у якій живуть ваші користувачі.","2026-09-23","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":1038,"title":1039,"description":1040,"published":1041,"category":711,"image":1042,"draft":715},"\u002Fblog\u002Fdomain-and-certificate-expiry","Домен закінчився, сайт лежить: що буде далі","Ваш домен закінчився і сайт лежить. Ось який годинник уже пішов, чому прострочений сертифікат це менша з двох бід, і як перевірити обидва.","2026-09-22","\u002Fblog\u002Fdomain-and-certificate-expiry\u002Fcard-800x500.png",{"path":1044,"title":1045,"description":1046,"published":1047,"category":711,"image":1048,"draft":715},"\u002Fblog\u002Fis-lovable-safe","Чи безпечний Lovable? Що показали 18 554 застосунки Lovable","Чи безпечний Lovable? Ми прогнали 18 554 живі застосунки Lovable через дев’ять перевірок. Платформа виявилася найчистішою з п’яти. Знахідки були в застосунках.","2026-09-21","\u002Fblog\u002Fis-lovable-safe\u002Fcard-800x500.png",{"path":1050,"title":1051,"description":1052,"published":1053,"category":711,"image":1054,"draft":715},"\u002Fblog\u002Fvibe-coded-app-security-checklist","Чек-лист безпеки для vibe coding, у девʼяти перевірках","Чек-лист безпеки для vibe coding із девʼяти пунктів, кожен з яких будь-хто може перевірити у вашому живому застосунку ззовні, і кожен має тест на один рядок.","2026-09-20","\u002Fblog\u002Fvibe-coded-app-security-checklist\u002Fcard-800x500.png",{"path":1056,"title":1057,"description":1058,"published":1059,"category":711,"image":1060,"draft":715},"\u002Fblog\u002Fstripe-secret-key-in-frontend","Секретний ключ Stripe у фронтенді може рухати гроші","Секретний ключ Stripe, відкритий у вашому фронтенді, може повертати кошти, списувати й читати кожну картку клієнта. А ключ pk_live_ там і має бути.","2026-09-19","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcard-800x500.png",{"path":1062,"title":1063,"description":1064,"published":1065,"category":711,"image":1066,"draft":715},"\u002Fblog\u002Fvite-and-next-public-env-vars","Змінні оточення Vite відкриті: префікс означає опублікувати це","Змінні оточення Vite відкриті у вашому застосунку, бо так просив префікс. VITE_ і NEXT_PUBLIC_ означають опублікувати це, а ШІ, який їх додав, не знав ціни.","2026-09-18","\u002Fblog\u002Fvite-and-next-public-env-vars\u002Fcard-800x500.png",{"path":1068,"title":1069,"description":1070,"published":1071,"category":711,"image":1072,"draft":715},"\u002Fblog\u002Fbase44-source-maps","Безпека Base44: що позначає скан і що з цього виправляти вам","Безпека Base44 на 5 438 просканованих застосунках: три знахідки майже на кожному належать платформі, source map є бейджем Base44, а ваша частина коротка.","2026-09-17","\u002Fblog\u002Fbase44-source-maps\u002Fcard-800x500.png",{"path":1074,"title":1075,"description":1076,"published":1077,"category":711,"image":1078,"draft":715},"\u002Fblog\u002Fis-cursor-ai-safe","Чи безпечний Cursor AI? Редактор, код і опублікований застосунок","Чи безпечний Cursor AI? Три запитання в одному пошуку: що Cursor зберігає, у чому помиляється його код і чи відкритий застосунок, який ви опублікували.","2026-09-16","\u002Fblog\u002Fis-cursor-ai-safe\u002Fcard-800x500.png",{"path":1080,"title":1081,"description":1082,"published":1083,"category":711,"image":1084,"draft":715},"\u002Fblog\u002Fis-replit-safe","Чи безпечний Replit? Що ми знайшли в 3 042 живих застосунках Replit","Чи безпечний Replit? Ми прогнали 3 042 живі застосунки Replit через дев’ять зовнішніх перевірок. Знахідки були в опублікованому застосунку, а не в хостингу.","2026-09-15","\u002Fblog\u002Fis-replit-safe\u002Fcard-800x500.png",{"path":1086,"title":1087,"description":1088,"published":1089,"category":812,"image":1090,"draft":715},"\u002Fblog\u002Fsupabase-storage-backup","Supabase storage backup: у копії бази даних немає жодного файлу","Supabase storage backup це окрема робота. Копії бази даних зберігають список ваших файлів і жодного з них, тож після відновлення кожне завантаження зламане.","2026-09-14","\u002Fblog\u002Fsupabase-storage-backup\u002Fcard-800x500.png",{"path":1092,"title":1093,"description":1094,"published":1095,"category":812,"image":1096,"draft":715},"\u002Fblog\u002Fsupabase-point-in-time-recovery","Point-in-time recovery у Supabase: ціна та межі","Point-in-time recovery у Supabase відмотує базу на будь-яку секунду за останній тиждень. Коштує $100 на місяць понад Pro і покриває лише базу даних.","2026-09-13","\u002Fblog\u002Fsupabase-point-in-time-recovery\u002Fcard-800x500.png",{"path":1098,"title":1099,"description":1100,"published":1101,"category":812,"image":1102,"draft":715},"\u002Fblog\u002Fsupabase-project-paused-recover","Проєкт Supabase на паузі? Ваші дані нікуди не зникли","Supabase поставив ваш проєкт на паузу після тижня без активності. Нічого не видалено, кнопка Restore стоїть поруч із назвою, і у вас є рік, щоб натиснути.","2026-09-12","\u002Fblog\u002Fsupabase-project-paused-recover\u002Fcard-800x500.png",{"path":1104,"title":1105,"description":1106,"published":1107,"category":711,"image":1108,"draft":715},"\u002Fblog\u002Fsafest-ai-app-builder","Який білдер застосунків з ШІ найбезпечніший? 30 998 тестів","Який білдер застосунків з ШІ найбезпечніший? Ми просканували 30 998 живих застосунків з Lovable, Base44, Replit, v0 і Bolt. Оцінку вирішує не білдер.","2026-09-11","\u002Fblog\u002Fsafest-ai-app-builder\u002Fcard-800x500.png",{"path":1110,"title":1111,"description":1112,"published":1113,"category":711,"image":1114,"draft":715},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","Увімкніть Row Level Security на кожній таблиці Supabase і перевірте","Row Level Security у Supabase без політики закриває таблицю повністю. Політика без увімкненого перемикача не робить нічого. Ось SQL, і ось перевірка.","2026-09-10","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcard-800x500.png",{"path":1116,"title":1117,"description":1118,"published":1119,"category":711,"image":1120,"draft":715},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","Supabase \"RLS disabled in public\": чого не бачить попередження","Supabase позначає \"RLS disabled in public\" як помилку. Про політику читання, яка лишає вашу таблицю так само відкритою, він не каже нічого.","2026-09-09","\u002Fblog\u002Fsupabase-rls-disabled-in-public\u002Fcard-800x500.png",{"path":427,"title":1122,"description":1123,"published":1124,"category":711,"image":1125,"draft":715},"Як замінити злитий ключ service_role у Supabase","Supabase каже спершу закрити витік. Інші посібники кажуть міняти негайно. Що правильно, залежить від того, куди саме потрапив ваш ключ service_role.","2026-09-08","\u002Fblog\u002Frotate-supabase-service-role-key\u002Fcard-800x500.png",{"path":1127,"title":1128,"description":1129,"published":1130,"category":711,"image":1131,"draft":715},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Сканери безпеки для vibe coding у порівнянні, наш теж","Найкращий сканер безпеки для vibe coding залежить від трьох питань, на які не відповідає жоден перелік функцій. Вісім інструментів і ціни.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":1133,"title":1134,"description":1135,"published":1136,"category":711,"image":1137,"draft":715},"\u002Fblog\u002Fsupabase-security-checker","Supabase security checker: п’ять перевірок, які зробите самі","Supabase security checker читає ваш опублікований застосунок, а не налаштування проєкту. Ось п’ять перевірок і як виконати кожну самостійно.","2026-09-06","\u002Fblog\u002Fsupabase-security-checker\u002Fcard-800x500.png",{"path":1139,"title":1140,"description":1141,"published":1142,"category":711,"image":1143,"draft":715},"\u002Fblog\u002Fvibe-coding-security-scanner","Сканер безпеки для vibe coding: чого не бачить скан URL","Сканер безпеки для vibe coding читає ваш робочий застосунок ззовні. Що це охоплює, чотири речі, яких він не бачить, і як читати результат.","2026-09-05","\u002Fblog\u002Fvibe-coding-security-scanner\u002Fcard-800x500.png",{"path":1145,"title":1146,"description":1147,"published":1148,"category":711,"image":1149,"draft":715},"\u002Fblog\u002Fmissing-security-headers","Відсутні заголовки безпеки: коли це справді важливо","Відсутні заголовки безпеки трапляються в наших звітах найчастіше. Від чого вони захищають і коли це найменш терміновий рядок вашого звіту.","2026-09-04","\u002Fblog\u002Fmissing-security-headers\u002Fcard-800x500.png",{"path":1151,"title":1152,"description":1153,"published":1154,"category":711,"image":1155,"draft":715},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","Ваш ключ API OpenAI відкритий у фронтенді. Замініть його.","Ключ API OpenAI, відкритий у фронтенді, не можна прив'язати до домену. Замініть його сьогодні, перенесіть виклик на свій сервер і обмежте витрати.","2026-09-03","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1157,"title":1158,"description":1159,"published":1160,"category":711,"image":1161,"draft":715},"\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","Ключ API у фронтенді: що насправді віддають 30 998 застосунків","Ключ API, відкритий у вашому фронтенді, зазвичай є ключем Google Maps. Ми просканували 30 998 vibe-coded застосунків і порахували, що витікає насправді.","2026-09-02","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fcard-800x500.png",{"path":1163,"title":1164,"description":1165,"published":1166,"category":812,"image":1167,"draft":715},"\u002Fblog\u002Fsupabase-backup-tools-compared","Інструменти резервного копіювання Supabase, і наш серед них","Чотири види інструментів резервного копіювання Supabase, що копіює кожен і коли безкоштовна GitHub Action виграє в будь-якої оплати, нашої теж.","2026-09-01","\u002Fblog\u002Fsupabase-backup-tools-compared\u002Fcard-800x500.png",{"path":1169,"title":1170,"description":1171,"published":1172,"category":711,"image":1173,"draft":715},"\u002Fblog\u002Freplit-secrets-explained","Як користуватися секретами в Replit і що все одно публікується","Як користуватися секретами в Replit: додати, прочитати і усунути дві причини undefined. А ще ключі, які цей інструмент не здатен зберегти в таємниці.","2026-08-31","\u002Fblog\u002Freplit-secrets-explained\u002Fcard-800x500.png",{"path":1175,"title":1176,"description":1177,"published":1178,"category":812,"image":1179,"draft":715},"\u002Fblog\u002Fback-up-supabase-free-tier","Резервні копії на безкоштовному тарифі Supabase без терміналу","На безкоштовному тарифі Supabase резервних копій немає: копію робите ви. Як зробити її з панелі та що втрачає шлях через CSV.","2026-08-30","\u002Fblog\u002Fback-up-supabase-free-tier\u002Fcard-800x500.png",{"path":1181,"title":1182,"description":1183,"published":1184,"category":711,"image":1185,"draft":715},"\u002Fblog\u002Fis-supabase-secure","Чи безпечний Supabase? Так. Ваш проєкт це інше питання","Чи безпечний Supabase? Платформа проходить аудити, шифрує дані й тримає пентести. Їхня документація каже, де це закінчується і починаються ваші налаштування.","2026-08-29","\u002Fblog\u002Fis-supabase-secure\u002Fcard-800x500.png",{"path":1187,"title":1188,"description":1189,"published":1190,"category":711,"image":1191,"draft":715},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Де знайти API-ключі Supabase: anon, service_role і URL","URL проєкту, ключ anon і ключ service_role лежать на одній сторінці панелі. Ось де вона, і яке з чотирьох значень має бути у вашому застосунку.","2026-08-28","\u002Fblog\u002Fwhere-to-find-supabase-api-keys\u002Fcard-800x500.png",{"path":1193,"title":1194,"description":1195,"published":1196,"category":711,"image":1197,"draft":715},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","New row violates row-level security policy в Supabase. Що далі?","\"New row violates row-level security policy\" означає, що Supabase відмовив у записі. Швидке виправлення знову відкриває таблицю для всіх.","2026-08-27","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcard-800x500.png",{"path":1199,"title":1200,"description":1201,"published":1202,"category":711,"image":1203,"draft":715},"\u002Fblog\u002Fcors-wildcard-security-risk","Чи є вайлдкард CORS ризиком для безпеки? Майже ніколи.","Чи є вайлдкард CORS ризиком для безпеки? Зазвичай це типове налаштування вашого білдера, і воно не видає нічого, що ваш сервер і так віддавав кожному.","2026-08-26","\u002Fblog\u002Fcors-wildcard-security-risk\u002Fcard-800x500.png",{"path":1205,"title":1206,"description":1207,"published":1208,"category":812,"image":1209,"draft":715},"\u002Fblog\u002Fsupabase-branching-is-not-a-backup","Branching у Supabase це не бекап. Він іде тільки вперед.","Чому branching у Supabase це не бекап: гілка стартує без ваших даних, а мердж переносить саму лише схему. Для чого він потрібен і що брати замість нього.","2026-08-25","\u002Fblog\u002Fsupabase-branching-is-not-a-backup\u002Fcard-800x500.png",{"path":1211,"title":1212,"description":1213,"published":1214,"category":812,"image":1215,"draft":715},"\u002Fblog\u002Fhow-to-restore-a-supabase-backup","Як відновити резервну копію Supabase, і що ламається потім","Як відновити резервну копію Supabase з панелі або з файлу дампа, що саме замінює відновлення і чому ваш застосунок може лишитися зламаним після нього.","2026-08-24","\u002Fblog\u002Fhow-to-restore-a-supabase-backup\u002Fcard-800x500.png",{"path":1217,"title":1218,"description":1219,"published":1220,"category":711,"image":1221,"draft":715},"\u002Fblog\u002Fsupabase-storage-bucket-public","Ваш бакет Supabase Storage публічний. Чи це проблема?","Публічний бакет Supabase Storage означає, що той, хто має URL файлу, може його відкрити. Він не означає, що хтось може переглянути список вмісту.","2026-08-23","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcard-800x500.png",{"path":1223,"title":1224,"description":1225,"published":1226,"category":711,"image":1227,"draft":715},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","Чи є проблемою ключ API Google у вашому браузері?","Ключ API Google у відкритому коді сторінки наш сканер знаходить найчастіше, і зазвичай усе гаразд. Вирішує це одне безкоштовне налаштування.","2026-08-22","\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1229,"title":1230,"description":1231,"published":1232,"category":812,"image":1233,"draft":715},"\u002Fblog\u002Fai-agent-deleted-my-database","ШІ-агент видалив мої дані в Supabase. Що можна відновити?","ШІ-агент видалив дані з вашої бази. Те, що ви зможете відновити, вирішилося раніше, а наступні хвилини вирішують, скільки з цього вціліє.","2026-08-21","\u002Fblog\u002Fai-agent-deleted-my-database\u002Fcard-800x500.png",{"path":1235,"title":1236,"description":1237,"published":1238,"category":711,"image":1239,"draft":715},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","Чи може будь-хто прочитати вашу базу Supabase? 3 680 застосунків","Чи може будь-хто прочитати вашу базу даних Supabase без входу? Ми просканували 30 998 живих застосунків, зроблених ШІ-білдерами, і виміряли, як часто так.","2026-08-18","\u002Fblog\u002Fcan-anyone-read-your-supabase-database\u002Fcard-800x500.png",{"path":1241,"title":1242,"description":1243,"published":1238,"category":711,"image":1244,"draft":715},"\u002Fblog\u002Fsource-maps-exposed-in-production","Відкриті source maps: ваш застосунок публікує свій вихідний код","Відкрита source map дає будь-кому прочитати оригінальний код вашого застосунку, разом із коментарями. Перевірка за 30 секунд, і що важить насправді.","\u002Fblog\u002Fsource-maps-exposed-in-production\u002Fcard-800x500.png",{"path":1246,"title":1247,"description":1248,"published":1249,"category":711,"image":1250,"draft":715},"\u002Fblog\u002Fsupabase-new-api-keys","Supabase publishable key: це той самий anon key?","Supabase замінив anon і service_role публічними та секретними ключами. Який із них має бути у вашому застосунку, а який не має бути ніколи?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":1252,"title":1253,"description":1254,"published":1255,"category":812,"image":1256,"draft":715},"\u002Fblog\u002Fdoes-supabase-back-up-my-database","Чи робить Supabase резервні копії моєї бази? Залежить від тарифу.","Чи робить Supabase резервні копії вашої бази даних? На платних тарифах щодня, на безкоштовному жодної. Як дізнатися, що у вас насправді є.","2026-08-11","\u002Fblog\u002Fdoes-supabase-back-up-my-database\u002Fcard-800x500.png",{"path":1258,"title":1259,"description":1260,"published":1261,"category":711,"image":1262,"draft":715},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","Supabase Row Level Security увімкнено. Таблиця досі публічна.","Увімкнути Supabase Row Level Security не означає захистити таблицю. Захищають політики, а та, що полагодила застосунок, часто впускає всіх.","2026-08-10","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fcard-800x500.png",{"path":1264,"title":1265,"description":1266,"published":1261,"category":812,"image":1267,"draft":715},"\u002Fblog\u002Fversion-history-is-not-a-backup","Історія версій не є резервною копією. Вона не поверне таблицю.","Lovable і Bolt ведуть історію версій вашого коду. Ваша база даних працює як окремий сервіс, тож відкат назад не поверне ваші дані.","\u002Fblog\u002Fversion-history-is-not-a-backup\u002Fcard-800x500.png",{"path":1269,"title":1270,"description":1271,"published":1272,"category":812,"image":1273,"draft":715},"\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database","Три способи зробити резервну копію бази Supabase, і чого бракує","Панель, pg_dump і керований сервіс. Що насправді зберігає кожен спосіб, що тихо лишає осторонь і який із них переживе втрату акаунта.","2026-08-09","\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database\u002Fcard-800x500.png",{"path":178,"title":1275,"description":1276,"published":1272,"category":711,"image":1277,"draft":715},"Які API-ключі безпечні у браузері, а які ні","Ваш ключ anon у Supabase створений, щоб бути публічним. Ключ service_role для цього не створений, і він ігнорує всі правила, які ви задали. Як їх розрізнити.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1794213654220]