[{"data":1,"prerenderedAt":1128},["ShallowReactive",2],{"blog-uk-supabase-invalid-jwt":3,"blog-index-uk":602},{"id":4,"title":5,"body":6,"category":560,"cover":561,"coverAlt":562,"description":563,"draft":564,"extension":565,"faq":566,"image":582,"keywords":583,"meta":591,"navigation":592,"ogTitle":5,"path":593,"published":594,"seo":595,"stem":596,"tldr":597,"updated":594,"__hash__":601},"blog_uk\u002Fblog\u002Fsupabase-invalid-jwt.md","Supabase invalid JWT: який ключ перестав працювати?",{"type":7,"value":8,"toc":547},"minimark",[9,13,24,27,39,44,47,50,53,56,62,66,73,87,167,172,179,200,207,211,221,234,237,263,268,271,283,294,298,301,313,327,331,334,366,370,373,395,398,405,409,412,428,431,439,442,446,452,461,475,485,502,506,535],[10,11,12],"p",{},"Учора ваш застосунок працював. Сьогодні вранці кожен запит повертається з 401, у\nкоді нічого не змінилося, а у відповіді йдеться про якийсь JWT, якого ви ніколи\nнікуди не вписували.",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","{\n  \"code\": \"PGRST301\",\n  \"message\": \"JWT cryptographic operation failed\"\n}\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26],{},"Помилка Supabase invalid JWT — це та, що з'являється після того, як ви зробили\nвсе правильно. Ви замінили ключ, бо так радив якийсь посібник, або перевели\nпроєкт на новіші ключі, і застосунок замовк.",[10,28,29,30,34,35,38],{},"І ось де посібник за посібником помиляється: ",[31,32,33],"strong",{},"ключ, про який говорить це\nповідомлення, часто не той, який ви щойно змінили."," Пошукайте помилку, і вас\nвідправлять перевіряти ключ ",[21,36,37],{},"anon",". У проєкті, що перейшов на ключі publishable\nі secret, ключ API взагалі не є підписаним токеном, тож перевірку не пройшло щось\nінше у запиті.",[40,41,43],"h2",{"id":42},"що-означає-помилка-supabase-invalid-jwt","Що означає помилка Supabase invalid JWT?",[10,45,46],{},"Щось у вашому запиті було підписаним токеном, і підпис не збігся.",[10,48,49],{},"Пароль — це слово, яке хтось шукає у списку. JWT — ні. Це лист, запечатаний\nсургучем: людина біля дверей не шукає вас ніде, вона тримає штемпель і\nперевіряє, чи печатку на вашому листі зроблено саме ним. Якщо так, ви проходите,\nі ніхто не читає жодного списку.",[10,51,52],{},"Лист із печаткою від іншого штемпеля відхиляють, хоча кожне слово всередині\nправильне. А щойно двері міняють штемпель, жоден лист, що вже в дорозі, більше не\nвідкривається, і при цьому ніхто листів не торкався.",[10,54,55],{},"Ваш запит — це лист. Ваш проєкт Supabase тримає штемпель. 401 тут означає, що ці\nдвоє не зійшлися.",[57,58],"diagram",{"alt":59,"caption":60,"src":61},"Дві смуги, кожна веде запечатаний лист до того самого штемпеля з гравіюванням у вигляді трьох променів. Угорі печатка на листі має те саме гравіювання, лист проходить, таблиця позаду показує три підсвічені рядки, а відповідь — 200. Унизу печатка має крапку в квадраті, бурштинова смуга зупиняє лист, таблиця позаду є порожнім пунктирним контуром, а відповідь — 401.","Ніде нічого не шукають. Печатку на токені порівнюють зі штемпелем, який тримає ваш проєкт, і лист, печатку якого зробило щось інше, відхиляють незалежно від того, що в ньому написано.","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fhow-the-check-works-1600x660.png",[40,63,65],{"id":64},"у-postgrest-13-повідомлення-змінилося-а-код-ні","У PostgREST 13 повідомлення змінилося, а код — ні",[10,67,68,69,72],{},"Перевіряйте ",[21,70,71],{},"PGRST301",". Яка фраза стоятиме поруч, залежить від версії PostgREST,\nна якій працює ваш проєкт, і новіші дають коротий перелік таких фраз.",[10,74,75,76,78,79,82,83,86],{},"Довідник помилок PostgREST описує ",[21,77,71],{}," як отриманий JWT, який не вдалося\nдекодувати або який недійсний, із HTTP 401, і у версії 13, і у версії 14. Ця\nфраза — опис у таблиці документації. Це не те, що потрапляє у ваше поле\n",[21,80,81],{},"message",". За ",[21,84,85],{},"Error.hs"," у гілці main PostgREST, це поле несе одну з таких:",[88,89,90,103],"table",{},[91,92,93],"thead",{},[94,95,96,100],"tr",{},[97,98,99],"th",{},"Що каже повідомлення",[97,101,102],{},"Що це вам говорить",[104,105,106,117,127,137,147,157],"tbody",{},[94,107,108,114],{},[109,110,111],"td",{},[21,112,113],{},"Empty JWT is sent in Authorization header",[109,115,116],{},"Заголовок надіслали, і він був порожній.",[94,118,119,124],{},[109,120,121],{},[21,122,123],{},"Expected 3 parts in JWT; got 1",[109,125,126],{},"Те, що прийшло, не є JWT. JWT складається з трьох частин, розділених крапками.",[94,128,129,134],{},[109,130,131],{},[21,132,133],{},"No suitable key or wrong key type",[109,135,136],{},"У сервера немає ключа, яким можна перевірити токен такого типу.",[94,138,139,144],{},[109,140,141],{},[21,142,143],{},"Wrong or unsupported encoding algorithm",[109,145,146],{},"Токен називає алгоритм підпису, якого сервер не використовує.",[94,148,149,154],{},[109,150,151],{},[21,152,153],{},"JWT cryptographic operation failed",[109,155,156],{},"Токен побудовано правильно, а підпис не збігається.",[94,158,159,164],{},[109,160,161],{},[21,162,163],{},"Unsupported token type",[109,165,166],{},"Токен має форму, яку PostgREST не читає.",[10,168,169,171],{},[21,170,153],{}," — це та, що означає застарілий ключ. Токен\nзібрано правильно й підписано чимось, чого проєкт уже не тримає, і саме це\nлишається після ротації.",[10,173,174,175,178],{},"Старіші проєкти бачать зовсім інше, і звідси походить той один рядок, який\nтрапляється всюди в результатах пошуку. До версії 13 PostgREST друкував те, що\nсказала його JWT-бібліотека, точно так, як вона сказала. Звідти й береться\n",[21,176,177],{},"JWSError JWSInvalidSignature",": це надрукована величина з бібліотеки, а не\nфраза, яку хтось написав для вас. Changelog PostgREST до 13.0.0 описує цю зміну\nяк діагностичні повідомлення про помилки замість відкритих внутрішніх значень.",[180,181,183],"callout",{"type":182},"warn",[10,184,185,188,189,192,193,195,196,199],{},[31,186,187],{},"Якщо ваш власний код порівнює текст повідомлення, порівнюйте код."," Перевірка,\nяка звіряє ",[21,190,191],{},"error.message"," із ",[21,194,177],{},", працює сьогодні й\nперестане працювати того дня, коли ваш проєкт перейде на новіший PostgREST, без\nжодного вашого деплою між цими двома моментами. Supabase каже те саме у власній\nдокументації помилок: використовуйте ",[21,197,198],{},"error.code",", а не порівняння повідомлень\nяк тексту.",[10,201,202,203,206],{},"Поки ви тут, варто знати сусідній код. ",[21,204,205],{},"PGRST303",", також 401, ви отримуєте, коли\nпідпис перевірено, а вміст токена відхилено: протермінований, ще не дійсний або\nз не тією audience. Якщо у вас саме цей код, припиніть дивитися на ключі й\nподивіться на годинник і на сесію.",[40,208,210],{"id":209},"який-це-ключ-ваш-ключ-api-чи-токен-вашого-користувача","Який це ключ: ваш ключ API чи токен вашого користувача?",[10,212,213,214,216,217,220],{},"Це залежить від того, чи ваш проєкт досі користується ",[21,215,37],{}," і ",[21,218,219],{},"service_role",".",[10,222,223,224,226,227,229,230,233],{},"Документація Supabase про ключі API прямо називає різницю. Старі ключі ",[21,225,37],{}," і\n",[21,228,219],{}," — це довгоживучі JWT, тому вони й починаються з ",[21,231,232],{},"eyJ",". Новіші\nключі publishable і secret — це короткі рядки, і вони взагалі не JWT.",[10,235,236],{},"Цей один факт розділяє помилку навпіл:",[238,239,240,251],"ul",{},[241,242,243,250],"li",{},[31,244,245,246,216,248,220],{},"Ваш проєкт досі користується ",[21,247,37],{},[21,249,219],{}," Ключ API у вашому\nзапиті сам є підписаним токеном, тож ключ з іншого проєкту, ключ із часів до\nротації чи обрізана вставка провалюють перевірку підпису так само, як\nпровалила б її зіпсована сесія. Будь-який із двох токенів у запиті може бути\nтим, що не пройшов.",[241,252,253,262],{},[31,254,255,256,216,259,220],{},"Ваш проєкт перейшов на ",[21,257,258],{},"sb_publishable_",[21,260,261],{},"sb_secret_"," Ключ API перестав\nбути токеном, тож на ньому немає підпису, який щось могло б відхилити.\nПеревіряти лишається access token вашого залогіненого користувача, який\nSupabase Auth видає окремо від ключа API.",[57,264],{"alt":265,"caption":266,"src":267},"Два ряди, кожен закінчується штемпелем. У верхньому ряду запечатаний лист із підписом anon і другий запечатаний лист поруч із людиною обидва йдуть до штемпеля. У нижньому ряду проста картка з підписом sb_publishable_ не має ні печатки, ні лінії, і до штемпеля йде лише запечатаний лист поруч із людиною.","У старому проєкті приходять два запечатані листи, і відхиленим може бути будь-який із них. Після переходу на новіші ключі ключ API більше не несе печатки, і сесія вашого залогіненого користувача лишається єдиним, на чому є підпис.","\u002Fblog\u002Fsupabase-invalid-jwt\u002Ftwo-things-called-your-key-1600x820.png",[10,269,270],{},"На практиці перше питання не в тому, який ключ неправильний. Воно в тому, чи був\nхтось залогінений, коли запит провалився. Якщо сторінка без входу працює, а\nсторінка з входом — ні, ви дивитеся на сесії. Якщо падає все, зокрема й\nсторінка, яка не просить нічого особистого, ви дивитеся на ключ API, а ваш\nпроєкт — зі старих.",[10,272,273,274,277,278,220],{},"Якщо йдеться про сесії, а лог Auth ще й показує ",[21,275,276],{},"Invalid Refresh Token",", ваших\nкористувачів викидає тому, що\n",[279,280,282],"a",{"href":281},"\u002Fblog\u002Fsupabase-users-logged-out","сесія за їхнім токеном завершилася",[10,284,285,286,289,290,220],{},"Якщо відповідь прийшла від Supabase Auth і звучить як ",[21,287,288],{},"invalid claim: missing sub claim",", токен пройшов перевірку підпису й нікого не називав. Це ключ,\nнадісланий туди, де має бути токен людини, і\n",[279,291,293],{"href":292},"\u002Fblog\u002Finvalid-claim-missing-sub-claim","це повідомлення має окремий допис",[40,295,297],{"id":296},"invalid-jwt-це-не-no-api-key-found-in-request","Invalid JWT — це не \"No API key found in request\"",[10,299,300],{},"Це два різні повідомлення з двома різними причинами, і виправлення в них не\nмають нічого спільного.",[10,302,303,304,308,309,312],{},"Invalid JWT означає, що токен прийшов і підпис не збігся. ",[279,305,307],{"href":306},"\u002Fblog\u002Fno-api-key-found-in-request","\"No API key found in\nrequest\""," означає, що заголовка ",[21,310,311],{},"apikey"," не\nприйшло взагалі, тож запит відхилили на шлюзі ще до того, як щось запитали у\nвашої бази.",[10,314,315,316,216,318,321,322,324,325,220],{},"Сама відповідь і вирішує, що саме у вас. Відсутній ключ відхиляє шлюз перед\nвашим проєктом, тож тіло відповіді несе ",[21,317,81],{},[21,319,320],{},"hint"," і жодного ",[21,323,21],{},".\nInvalid JWT зайшов достатньо далеко, щоб його прочитав PostgREST, і тому\nповертається з написаним ",[21,326,71],{},[40,328,330],{"id":329},"де-ще-лежить-старий-ключ-по-порядку","Де ще лежить старий ключ, по порядку",[10,332,333],{},"Чотири місця, і перевіряти їх варто саме в цьому порядку, бо він приблизно\nвідповідає тому, як часто кожне з них виявляється відповіддю.",[335,336,337,343,354,360],"ol",{},[241,338,339,342],{},[31,340,341],{},"Налаштування проєкту у вашому конструкторі."," Lovable, Bolt, v0 і Replit\nтримають кожен свій набір змінних середовища, а якщо ви будували з Cursor чи\nClaude Code, той самий набір лежить там, куди ви деплоїте. Це та копія, яку\nоновлює більшість, і єдина, яку оновлює більшість.",[241,344,345,348,349,353],{},[31,346,347],{},"Ключ, вписаний просто в код."," Він їде всередині JavaScript, який\nзавантажують ваші відвідувачі, тож зміна в панелі нічого не змінює, доки файл\nне відредагують і сайт не перезберуть. ",[279,350,352],{"href":351},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Де ця сторінка і що робить кожне\nзначення на ній",", якщо ви її ще не\nвідкривали.",[241,355,356,359],{},[31,357,358],{},"Секрети Edge Function."," Функція тримає власний набір, окремий від набору\nвашого фронтенду, тож вона й далі користується старим ключем, коли все решта\nвже переїхало. Supabase також попереджає, що функція з увімкненим\nналаштуванням Verify JWT може зламатися під час ротації ключа підпису.",[241,361,362,365],{},[31,363,364],{},"Деплой, який ніхто не перезібрав."," Ключ запекли всередину під час\nостаннього збирання, тож робоча копія надсилає його й далі, доки щось не\nзапустить нове збирання. Ваша панель при цьому весь час показує новий ключ, і\nсаме це робить цей випадок важким: місце, куди ви йдете перевіряти, — єдине,\nде вже все правильно.",[40,367,369],{"id":368},"який-ключ-ваш-живий-застосунок-насправді-надсилає","Який ключ ваш живий застосунок насправді надсилає",[10,371,372],{},"Відкрийте опублікований сайт, а не попередній перегляд, і прочитайте, що\nсторінка надсилає.",[10,374,375,376,378,379,381,382,384,385,389,390,394],{},"Старий ключ Supabase — це довгий рядок, що починається з ",[21,377,232],{},". Новіші\nповідомляють про себе першими символами: ",[21,380,258],{}," місце у вашому\nзастосунку, а ",[21,383,261],{}," — ніколи. Якщо знайдете другий на сторінці, яку може\nзавантажити відвідувач, облиште цю статтю і\n",[279,386,388],{"href":387},"\u002Fblog\u002Frotate-supabase-service-role-key","замініть його"," раніше за все інше. Який\nіз двох у вас і\n",[279,391,393],{"href":392},"\u002Fblog\u002Fsupabase-new-api-keys","що змінилося, коли Supabase їх\nперейменував",", варто з'ясувати, перш ніж починати\nпідмінювати значення.",[10,396,397],{},"Щоб побачити, що ваш проєкт видає зараз: усі ключі тепер лежать в одному місці\nпанелі, у Settings, а тоді API Keys, разом зі старими.",[10,399,400,401,220],{},"Якщо вам не хочеться читати мініфікований бандл, наше безкоштовне сканування\nчитає ваш живий застосунок так, як його прочитав би сторонній, і називає ключі,\nвидимі ззовні. Це займає близько 20 секунд і не потребує облікового запису:\n",[279,402,404],{"href":403},"\u002Fsecurity-scanner","просканувати застосунок",[40,406,408],{"id":407},"ротація-ключа-підпису-без-того-щоб-вилогінити-всіх","Ротація ключа підпису без того, щоб вилогінити всіх",[10,410,411],{},"Який із двох шляхів ротації ви обрали, і вирішує, чи помітять це ваші\nкористувачі.",[10,413,414,415,418,419,226,421,423,424,427],{},"Документація Supabase про ключі підпису ставить їх поруч. Ротація ",[31,416,417],{},"старого\nJWT-секрета"," одразу вилогінює всіх активних користувачів, а ",[21,420,37],{},[21,422,219],{}," доводиться ротувати одночасно. Новіші ",[31,425,426],{},"JWT signing keys","\nпрацюють інакше: новий ключ починає як standby, ротація робить його поточним, а\nтой, якого він замінив, стає попереднім. Доки це так, підписи обох ключів і далі\nприймаються, тож листи, запечатані минулого тижня, усе ще відкриваються. Відкликання\nстарого ключа — це окремий крок, який цьому кладе край.",[10,429,430],{},"Два попередження в тій самій документації й ловлять людей:",[238,432,433,436],{},[241,434,435],{},"Компонент, який сам перевіряє токени проти старого JWT-секрета, бібліотекою на\nкшталт jose чи jsonwebtoken, ламається під час ротації. Він тримає старий\nштемпель, і ніхто не сказав йому змінити його.",[241,437,438],{},"Edge Function з увімкненим Verify JWT може зламатися з тієї самої причини.",[10,440,441],{},"Створення ключів publishable і secret теж не відкликає ваших старих ключів. Вони\nпрацюють, доки ви їх не вимкнете, і документація позначає це як окремий крок.\nТож застосунок, що переніс лише половину своїх копій, працює далі тижнями, а\nдень, коли хтось вимикає стару пару, стає днем, коли друга половина починає\nвідповідати 401.",[40,443,445],{"id":444},"як-наглядати-за-застосунком-що-почав-відповідати-401","Як наглядати за застосунком, що почав відповідати 401",[10,447,448,451],{},[31,449,450],{},"Застосунок, який віддає 401 на кожен запит, і далі доступний, і далі віддає\nсторінку, і далі відповідає на будь-яку перевірку працездатності."," У вашій\nпанелі нічого не червоніє. Ви дізнаєтеся про це, коли вам хтось напише.",[10,453,454],{},[31,455,456,460],{},[279,457,459],{"href":458},"\u002Fwebsite-monitoring","Reeve Monitor"," дізнається першим:",[238,462,463,466,469,472],{},[241,464,465],{},"усі дев'ять перевірок щогодини, для трьох застосунків",[241,467,468],{},"лист того дня, коли повторне сканування оцінить застосунок гірше за попереднє",[241,470,471],{},"чи застосунок відповідає, кожні 60 секунд",[241,473,474],{},"місячний звіт про побачене",[10,476,477],{},[31,478,479,480,484],{},"Ключ, який довелося міняти поспіхом, зазвичай був ключем, що лежав не там, де\nмав, і звідси ви не побачите, що ним прочитали чи змінили, поки він був назовні.\n",[279,481,483],{"href":482},"\u002Fsupabase-backups","Reeve Care"," тримає копію вашої бази Supabase, зроблену до\nцього.",[238,486,487,490,493,496,499],{},[241,488,489],{},"зашифрована копія за розкладом, на власному сховищі Reeve у ЄС, поза вашим акаунтом Supabase",[241,491,492],{},"кожна копія перевіряється, перш ніж її зарахувати, підрахунком рядків у кожній таблиці",[241,494,495],{},"відновлення одним кліком, яке зберігає теперішній стан, перш ніж його замінити",[241,497,498],{},"також ваші завантажені файли, щойно ви під'єднаєте доступ до Storage",[241,500,501],{},"усе, що вміє Monitor",[40,503,505],{"id":504},"що-зробити-сьогодні","Що зробити сьогодні",[507,508,509],"key-takeaways",{},[238,510,511,523,526,529,532],{},[241,512,513,514,516,517,519,520,522],{},"Читайте поле ",[21,515,21],{},", а не фразу. ",[21,518,71],{}," — це підпис, який не збігся, ",[21,521,205],{}," — токен, чий вміст відхилили, наприклад протермінований.",[241,524,525],{},"З'ясуйте, який саме токен не пройшов, перш ніж міняти ключ. Якщо сторінки без входу працюють, а сторінки з входом — ні, це сесія, а не ваш ключ API.",[241,527,528],{},"Перевірте всі чотири місця, де ховається старий ключ: налаштування конструктора, сам код, секрети Edge Function і деплой, який ніхто не перезібрав.",[241,530,531],{},"Робіть заміну за один підхід. Кожна залишена копія — це застосунок, що працює наполовину, і живий сайт надсилає старий ключ, доки його не перезберуть.",[241,533,534],{},"На нових ключах підпису саме відкликання припиняє старі підписи. Сама лише ротація лишає їх дійсними.",[10,536,537,538,541,542,546],{},"Почніть із питання, чи сторінка без входу досі завантажується, бо ця одна\nвідповідь вирішує, яка половина статті стосується вас. Якщо виявиться, що\nsecret-ключ лежав відкрито, ",[279,539,540],{"href":387},"його заміна","\nописує порядок дій, а\n",[279,543,545],{"href":544},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","які ключі взагалі було безпечно\nпублікувати"," розповідає, чому\nці двоє стали такі схожі.",{"title":23,"searchDepth":548,"depth":548,"links":549},3,[550,552,553,554,555,556,557,558,559],{"id":42,"depth":551,"text":43},2,{"id":64,"depth":551,"text":65},{"id":209,"depth":551,"text":210},{"id":296,"depth":551,"text":297},{"id":329,"depth":551,"text":330},{"id":368,"depth":551,"text":369},{"id":407,"depth":551,"text":408},{"id":444,"depth":551,"text":445},{"id":504,"depth":551,"text":505},"Основи безпеки","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcover-1200x630.png","Запечатаний лист перед штемпелем, гравіювання якого більше не збігається з печаткою, а позаду ще листи за бурштиновою смугою.","Помилка Supabase invalid JWT — це PGRST301: підписаний токен не пройшов перевірку. У новому проєкті йдеться про сесію, а не про ключ API.",false,"md",[567,570,573,576,579],{"q":568,"a":569},"Що означає PGRST301?","PGRST301 — це відповідь PostgREST із HTTP 401, бо підписаний токен у вашому запиті не вдалося перевірити. PostgREST — це сервер, який перетворює ваші таблиці Supabase на HTTP API, з яким розмовляє ваш застосунок. Його власний довідник помилок описує цей код як отриманий JWT, який не вдалося декодувати або який недійсний, а з версії 13 поле message несе точнішу фразу про те, яка саме частина перевірки не пройшла.",{"q":571,"a":572},"Чому я отримую 401 після заміни ключа Supabase?","Бо десь досі надсилається копія старого ключа. Звичні місця — налаштування проєкту у вашому конструкторі, ключ, вписаний просто в код, секрет Edge Function і деплой, зібраний до заміни й відтоді не перезібраний. Токен, підписаний ключем, який сервер уже не приймає, не проходить перевірку, і до вас це доходить як 401 на кожен запит, а не як повідомлення про ключ.",{"q":574,"a":575},"Чим це відрізняється від No API key found in request?","Це два різні повідомлення з різними причинами. No API key found in request означає, що запит прийшов узагалі без заголовка apikey і його відхилили на шлюзі ще до того, як щось запитали у вашої бази. Invalid JWT означає, що токен був і його підпис не збігся. Перше — це лист без адреси, друге — лист із не тією печаткою.",{"q":577,"a":578},"Чи треба оновити ще й мої Edge Functions?","Так, і саме цю копію забувають найчастіше, бо Edge Function тримає власні секрети, окремо від тих, якими користується ваш фронтенд. Supabase також попереджає, що функція з увімкненим налаштуванням Verify JWT може зламатися під час ротації ключа підпису, і що будь-який компонент, який сам перевіряє токени проти старого JWT-секрета, зламається з тієї самої причини.",{"q":580,"a":581},"Як побачити, який ключ насправді надсилає мій застосунок у продакшені?","Відкрийте опублікований сайт, а не попередній перегляд, і подивіться, що сторінка насправді надсилає. Старий ключ Supabase — це довгий рядок, що починається з eyJ, новіші починаються з sb_publishable_ або sb_secret_. Наше безкоштовне сканування читає живий застосунок так, як його прочитав би сторонній, і називає ключі, видимі ззовні, а отже й показує, чи досі надсилається старий.","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcard-800x500.png",[584,585,586,587,588,589,590],"supabase invalid jwt","pgrst301","bad_jwt supabase","supabase 401 після зміни ключа","jwsinvalidsignature supabase","старий ключ supabase перестав працювати","provided jwt could not be decoded",{},true,"\u002Fblog\u002Fsupabase-invalid-jwt","2026-10-16",{"title":5,"description":563},"blog\u002Fsupabase-invalid-jwt",[598,599,600],"Помилка Supabase invalid JWT приходить як PGRST301 із HTTP 401 і означає, що підпис токена, який пішов із вашим запитом, не вдалося перевірити. Нічого не шукали в списку, щоб потім відмовити.","Ключі publishable і secret — це короткі рядки без підпису, тож у проєкті, який уже перейшов на них, ця помилка стосується вашого залогіненого користувача, а не ключа API.","PostgREST 13 замінив одну розпливчасту фразу коротким переліком конкретних. Читайте те повідомлення, яке ви справді отримали: кожне відкидає іншу причину.","oPt1o7E4p9n5Rftl5UOODNLv1G444N51z3LtdHXaNPA",[603,609,615,621,627,633,638,644,650,656,662,669,675,681,687,693,699,705,711,717,723,729,734,740,746,747,753,759,765,771,777,783,789,795,800,806,812,818,824,830,836,842,848,854,860,866,871,877,883,889,895,901,907,913,919,925,931,937,943,949,955,961,967,973,978,984,990,996,1002,1008,1014,1020,1026,1032,1038,1043,1049,1055,1061,1067,1073,1079,1085,1091,1096,1101,1107,1113,1118,1124],{"path":604,"title":605,"description":606,"published":607,"category":560,"image":608,"draft":564},"\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":610,"title":611,"description":612,"published":613,"category":560,"image":614,"draft":564},"\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":616,"title":617,"description":618,"published":619,"category":560,"image":620,"draft":564},"\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":622,"title":623,"description":624,"published":625,"category":560,"image":626,"draft":564},"\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":628,"title":629,"description":630,"published":631,"category":560,"image":632,"draft":564},"\u002Fblog\u002Fauth-session-missing","Supabase \"Auth session missing!\": куди поділася сесія","\"Auth session missing!\" означає, що supabase-js не знайшла жодної сесії й навіть не спитала Supabase. Куди поділася сесія і як це виправити.","2026-11-05","\u002Fblog\u002Fauth-session-missing\u002Fcard-800x500.png",{"path":292,"title":634,"description":635,"published":636,"category":560,"image":637,"draft":564},"Supabase \"invalid claim: missing sub claim\": ніхто не ввійшов","\"invalid claim: missing sub claim\": Supabase Auth отримав справжній токен без жодного користувача, ключ проєкту замість сесії.","2026-11-04","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fcard-800x500.png",{"path":639,"title":640,"description":641,"published":642,"category":560,"image":643,"draft":564},"\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function","\"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":645,"title":646,"description":647,"published":648,"category":560,"image":649,"draft":564},"\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":651,"title":652,"description":653,"published":654,"category":560,"image":655,"draft":564},"\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":657,"title":658,"description":659,"published":660,"category":560,"image":661,"draft":564},"\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":663,"title":664,"description":665,"published":666,"category":667,"image":668,"draft":564},"\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":670,"title":671,"description":672,"published":673,"category":560,"image":674,"draft":564},"\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":676,"title":677,"description":678,"published":679,"category":560,"image":680,"draft":564},"\u002Fblog\u002Fsupabase-invalid-login","\"Invalid login credentials\" у Supabase: три причини","Supabase відповідає \"Invalid login credentials\" на неправильний пароль, невідому пошту й акаунт Google. Як зрозуміти, що саме.","2026-10-28","\u002Fblog\u002Fsupabase-invalid-login\u002Fcard-800x500.png",{"path":682,"title":683,"description":684,"published":685,"category":560,"image":686,"draft":564},"\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code","\"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":688,"title":689,"description":690,"published":691,"category":560,"image":692,"draft":564},"\u002Fblog\u002Fcertificate-not-trusted","Сертифікат не довірений: три причини, крім закінчення строку","Сертифікат не довірений, хоча дата в порядку? Він називає іншу адресу, підписав сам себе або в його ланцюжку бракує ланки. Як зрозуміти, що саме.","2026-10-26","\u002Fblog\u002Fcertificate-not-trusted\u002Fcard-800x500.png",{"path":694,"title":695,"description":696,"published":697,"category":560,"image":698,"draft":564},"\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":700,"title":701,"description":702,"published":703,"category":560,"image":704,"draft":564},"\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":706,"title":707,"description":708,"published":709,"category":560,"image":710,"draft":564},"\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":712,"title":713,"description":714,"published":715,"category":560,"image":716,"draft":564},"\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":718,"title":719,"description":720,"published":721,"category":560,"image":722,"draft":564},"\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":724,"title":725,"description":726,"published":727,"category":560,"image":728,"draft":564},"\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":281,"title":730,"description":731,"published":732,"category":560,"image":733,"draft":564},"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":735,"title":736,"description":737,"published":738,"category":560,"image":739,"draft":564},"\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":741,"title":742,"description":743,"published":744,"category":560,"image":745,"draft":564},"\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":593,"title":5,"description":563,"published":594,"category":560,"image":582,"draft":564},{"path":748,"title":749,"description":750,"published":751,"category":560,"image":752,"draft":564},"\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":754,"title":755,"description":756,"published":757,"category":560,"image":758,"draft":564},"\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":760,"title":761,"description":762,"published":763,"category":560,"image":764,"draft":564},"\u002Fblog\u002Fwhat-is-supabase","Що таке Supabase? База даних, яку обрав ваш білдер","Що таке Supabase: база даних, вхід і сховище файлів за вашим застосунком. Supabase тримає сервери, а правила про те, хто читає ваші рядки, ваші.","2026-10-13","\u002Fblog\u002Fwhat-is-supabase\u002Fcard-800x500.png",{"path":766,"title":767,"description":768,"published":769,"category":560,"image":770,"draft":564},"\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":772,"title":773,"description":774,"published":775,"category":560,"image":776,"draft":564},"\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":778,"title":779,"description":780,"published":781,"category":560,"image":782,"draft":564},"\u002Fblog\u002Fbase44-security-scan","Сканування безпеки Base44: те, що бачить лише воно","Сканування безпеки Base44 перевіряє сім видів проблем зсередини. Ось половина, яку читає тільки воно, і половина, на яку воно ніколи не дивиться.","2026-10-10","\u002Fblog\u002Fbase44-security-scan\u002Fcard-800x500.png",{"path":784,"title":785,"description":786,"published":787,"category":560,"image":788,"draft":564},"\u002Fblog\u002Fapi-key-leaked-what-to-do","Ваш API-ключ витік. Порядок дій","API-ключ витік, і ви не знаєте, що робити першим. Не кожен ключ у вашому фронтенді є витоком, а порядок важить більше за швидкість.","2026-10-09","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcard-800x500.png",{"path":790,"title":791,"description":792,"published":793,"category":560,"image":794,"draft":564},"\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":306,"title":796,"description":797,"published":798,"category":560,"image":799,"draft":564},"\"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":801,"title":802,"description":803,"published":804,"category":560,"image":805,"draft":564},"\u002Fblog\u002Fis-base44-safe","Чи безпечний Base44? Дані 5 442 застосунків","Чи безпечний Base44? Ми прогнали дев’ять перевірок на 5 442 робочих застосунках Base44. Майже всі отримали B з причин, яких власник ніколи не обирав.","2026-10-06","\u002Fblog\u002Fis-base44-safe\u002Fcard-800x500.png",{"path":807,"title":808,"description":809,"published":810,"category":560,"image":811,"draft":564},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","Сховати ключ API: перенесіть його в Supabase Edge Function","Сховати ключ API означає прибрати його з браузера, а Supabase Edge Function це найменше місце, куди його покласти. Два кроки поруч важать більше.","2026-10-05","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":813,"title":814,"description":815,"published":816,"category":560,"image":817,"draft":564},"\u002Fblog\u002Fenv-file-exposed-on-your-server","Чи відкритий ваш файл .env? Дванадцять шляхів для перевірки","Чи відкритий ваш файл .env на вашому власному вебсервері? Дванадцять адрес скажуть це за хвилину, а влучання означає, що все в ньому вже публічне.","2026-10-04","\u002Fblog\u002Fenv-file-exposed-on-your-server\u002Fcard-800x500.png",{"path":819,"title":820,"description":821,"published":822,"category":560,"image":823,"draft":564},"\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":825,"title":826,"description":827,"published":828,"category":560,"image":829,"draft":564},"\u002Fblog\u002Fis-bolt-safe","Чи безпечний Bolt? Що показали 1 123 застосунки Bolt","Чи безпечний Bolt? Ми прогнали 1 123 живі застосунки Bolt через дев’ять перевірок. Хостинг був чистим. Знахідки: ключі й відкриті таблиці в самих застосунках.","2026-10-02","\u002Fblog\u002Fis-bolt-safe\u002Fcard-800x500.png",{"path":831,"title":832,"description":833,"published":834,"category":560,"image":835,"draft":564},"\u002Fblog\u002Fis-supabase-down-or-your-app","Supabase не працює чи проблема у вашому застосунку? Як розрізнити","Supabase не працює чи зламався ваш застосунок? Пауза, повна база й ваша власна помилка виглядають однаково. Чотири перевірки їх розрізняють.","2026-10-01","\u002Fblog\u002Fis-supabase-down-or-your-app\u002Fcard-800x500.png",{"path":837,"title":838,"description":839,"published":840,"category":560,"image":841,"draft":564},"\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":843,"title":844,"description":845,"published":846,"category":560,"image":847,"draft":564},"\u002Fblog\u002Fsupabase-free-plan-limits","Ліміти безкоштовного тарифу Supabase і що стається на кожному","Ліміти безкоштовного тарифу Supabase: 500 МБ бази, 5 ГБ egress і жодних резервних копій. Що стається на кожній межі і що змінює Pro за $25.","2026-09-29","\u002Fblog\u002Fsupabase-free-plan-limits\u002Fcard-800x500.png",{"path":849,"title":850,"description":851,"published":852,"category":667,"image":853,"draft":564},"\u002Fblog\u002Ftest-your-supabase-backup","Перевірте резервну копію Supabase до того, як вона знадобиться","Як перевірити резервну копію Supabase: відновити в тестовий проєкт, порахувати рядки, увійти й знайти рядок, якого бракує обрізаному файлу.","2026-09-28","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":855,"title":856,"description":857,"published":858,"category":667,"image":859,"draft":564},"\u002Fblog\u002Fsupabase-backup-github-action","Безкоштовна резервна копія Supabase через GitHub Action і підступ","Резервна копія Supabase через GitHub Action безкоштовна й годиться багатьом застосункам. Робочий процес, потрібний рядок підключення та egress.","2026-09-27","\u002Fblog\u002Fsupabase-backup-github-action\u002Fcard-800x500.png",{"path":861,"title":862,"description":863,"published":864,"category":667,"image":865,"draft":564},"\u002Fblog\u002Fdownload-your-supabase-backup","Чому ви не можете завантажити резервну копію Supabase","Завантажити резервну копію Supabase на сучасному проєкті не вийде: щоденна копія там фізична. Як це перевірити і як тримати власну копію.","2026-09-26","\u002Fblog\u002Fdownload-your-supabase-backup\u002Fcard-800x500.png",{"path":867,"title":868,"description":869,"published":864,"category":560,"image":870,"draft":564},"\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":872,"title":873,"description":874,"published":875,"category":560,"image":876,"draft":564},"\u002Fblog\u002Fopen-api-endpoint-exposed","Чи є відкритий API-ендпоінт проблемою безпеки? Читайте JSON","Ваше сканування позначило відкритий API-ендпоінт. Чи це важливо, залежить від того, що повернулося, і більшість знайдених належали платформі.","2026-09-25","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fcard-800x500.png",{"path":878,"title":879,"description":880,"published":881,"category":560,"image":882,"draft":564},"\u002Fblog\u002Flovable-security-scan","Сканування безпеки Lovable: чого воно не доведе","Сканування безпеки Lovable: що перевіряють Quick scan і Deep scan, коли запускається кожне, і єдине, чого не доведе жодне сканування зсередини.","2026-09-24","\u002Fblog\u002Flovable-security-scan\u002Fcard-800x500.png",{"path":884,"title":885,"description":886,"published":887,"category":667,"image":888,"draft":564},"\u002Fblog\u002Fsupabase-backup-auth-users","Чому у вашому дампі Supabase немає користувачів","Запустіть supabase db dump окремо і отримаєте форму вашої бази та жодного її рядка, без схеми auth, у якій живуть ваші користувачі.","2026-09-23","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":890,"title":891,"description":892,"published":893,"category":560,"image":894,"draft":564},"\u002Fblog\u002Fdomain-and-certificate-expiry","Домен закінчився, сайт лежить: що буде далі","Ваш домен закінчився і сайт лежить. Ось який годинник уже пішов, чому прострочений сертифікат це менша з двох бід, і як перевірити обидва.","2026-09-22","\u002Fblog\u002Fdomain-and-certificate-expiry\u002Fcard-800x500.png",{"path":896,"title":897,"description":898,"published":899,"category":560,"image":900,"draft":564},"\u002Fblog\u002Fis-lovable-safe","Чи безпечний Lovable? Що показали 18 554 застосунки Lovable","Чи безпечний Lovable? Ми прогнали 18 554 живі застосунки Lovable через дев’ять перевірок. Платформа виявилася найчистішою з п’яти. Знахідки були в застосунках.","2026-09-21","\u002Fblog\u002Fis-lovable-safe\u002Fcard-800x500.png",{"path":902,"title":903,"description":904,"published":905,"category":560,"image":906,"draft":564},"\u002Fblog\u002Fvibe-coded-app-security-checklist","Чек-лист безпеки для vibe coding, у девʼяти перевірках","Чек-лист безпеки для vibe coding із девʼяти пунктів, кожен з яких будь-хто може перевірити у вашому живому застосунку ззовні, і кожен має тест на один рядок.","2026-09-20","\u002Fblog\u002Fvibe-coded-app-security-checklist\u002Fcard-800x500.png",{"path":908,"title":909,"description":910,"published":911,"category":560,"image":912,"draft":564},"\u002Fblog\u002Fstripe-secret-key-in-frontend","Секретний ключ Stripe у фронтенді може рухати гроші","Секретний ключ Stripe, відкритий у вашому фронтенді, може повертати кошти, списувати й читати кожну картку клієнта. А ключ pk_live_ там і має бути.","2026-09-19","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcard-800x500.png",{"path":914,"title":915,"description":916,"published":917,"category":560,"image":918,"draft":564},"\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":920,"title":921,"description":922,"published":923,"category":560,"image":924,"draft":564},"\u002Fblog\u002Fbase44-source-maps","Безпека Base44: що позначає скан і що з цього виправляти вам","Безпека Base44 на 5 438 просканованих застосунках: три знахідки майже на кожному належать платформі, source map є бейджем Base44, а ваша частина коротка.","2026-09-17","\u002Fblog\u002Fbase44-source-maps\u002Fcard-800x500.png",{"path":926,"title":927,"description":928,"published":929,"category":560,"image":930,"draft":564},"\u002Fblog\u002Fis-cursor-ai-safe","Чи безпечний Cursor AI? Редактор, код і опублікований застосунок","Чи безпечний Cursor AI? Три запитання в одному пошуку: що Cursor зберігає, у чому помиляється його код і чи відкритий застосунок, який ви опублікували.","2026-09-16","\u002Fblog\u002Fis-cursor-ai-safe\u002Fcard-800x500.png",{"path":932,"title":933,"description":934,"published":935,"category":560,"image":936,"draft":564},"\u002Fblog\u002Fis-replit-safe","Чи безпечний Replit? Що ми знайшли в 3 042 живих застосунках Replit","Чи безпечний Replit? Ми прогнали 3 042 живі застосунки Replit через дев’ять зовнішніх перевірок. Знахідки були в опублікованому застосунку, а не в хостингу.","2026-09-15","\u002Fblog\u002Fis-replit-safe\u002Fcard-800x500.png",{"path":938,"title":939,"description":940,"published":941,"category":667,"image":942,"draft":564},"\u002Fblog\u002Fsupabase-storage-backup","Supabase storage backup: у копії бази даних немає жодного файлу","Supabase storage backup це окрема робота. Копії бази даних зберігають список ваших файлів і жодного з них, тож після відновлення кожне завантаження зламане.","2026-09-14","\u002Fblog\u002Fsupabase-storage-backup\u002Fcard-800x500.png",{"path":944,"title":945,"description":946,"published":947,"category":667,"image":948,"draft":564},"\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":950,"title":951,"description":952,"published":953,"category":667,"image":954,"draft":564},"\u002Fblog\u002Fsupabase-project-paused-recover","Проєкт Supabase на паузі? Ваші дані нікуди не зникли","Supabase поставив ваш проєкт на паузу після тижня без активності. Нічого не видалено, кнопка Restore стоїть поруч із назвою, і у вас є рік, щоб натиснути.","2026-09-12","\u002Fblog\u002Fsupabase-project-paused-recover\u002Fcard-800x500.png",{"path":956,"title":957,"description":958,"published":959,"category":560,"image":960,"draft":564},"\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":962,"title":963,"description":964,"published":965,"category":560,"image":966,"draft":564},"\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":968,"title":969,"description":970,"published":971,"category":560,"image":972,"draft":564},"\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":387,"title":974,"description":975,"published":976,"category":560,"image":977,"draft":564},"Як замінити злитий ключ service_role у Supabase","Supabase каже спершу закрити витік. Інші посібники кажуть міняти негайно. Що правильно, залежить від того, куди саме потрапив ваш ключ service_role.","2026-09-08","\u002Fblog\u002Frotate-supabase-service-role-key\u002Fcard-800x500.png",{"path":979,"title":980,"description":981,"published":982,"category":560,"image":983,"draft":564},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Сканери безпеки для vibe coding у порівнянні, наш теж","Найкращий сканер безпеки для vibe coding залежить від трьох питань, на які не відповідає жоден перелік функцій. Вісім інструментів і ціни.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":985,"title":986,"description":987,"published":988,"category":560,"image":989,"draft":564},"\u002Fblog\u002Fsupabase-security-checker","Supabase security checker: п’ять перевірок, які зробите самі","Supabase security checker читає ваш опублікований застосунок, а не налаштування проєкту. Ось п’ять перевірок і як виконати кожну самостійно.","2026-09-06","\u002Fblog\u002Fsupabase-security-checker\u002Fcard-800x500.png",{"path":991,"title":992,"description":993,"published":994,"category":560,"image":995,"draft":564},"\u002Fblog\u002Fvibe-coding-security-scanner","Сканер безпеки для vibe coding: чого не бачить скан URL","Сканер безпеки для vibe coding читає ваш робочий застосунок ззовні. Що це охоплює, чотири речі, яких він не бачить, і як читати результат.","2026-09-05","\u002Fblog\u002Fvibe-coding-security-scanner\u002Fcard-800x500.png",{"path":997,"title":998,"description":999,"published":1000,"category":560,"image":1001,"draft":564},"\u002Fblog\u002Fmissing-security-headers","Відсутні заголовки безпеки: коли це справді важливо","Відсутні заголовки безпеки трапляються в наших звітах найчастіше. Від чого вони захищають і коли це найменш терміновий рядок вашого звіту.","2026-09-04","\u002Fblog\u002Fmissing-security-headers\u002Fcard-800x500.png",{"path":1003,"title":1004,"description":1005,"published":1006,"category":560,"image":1007,"draft":564},"\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":1009,"title":1010,"description":1011,"published":1012,"category":560,"image":1013,"draft":564},"\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":1015,"title":1016,"description":1017,"published":1018,"category":667,"image":1019,"draft":564},"\u002Fblog\u002Fsupabase-backup-tools-compared","Інструменти резервного копіювання Supabase, і наш серед них","Чотири види інструментів резервного копіювання Supabase, що копіює кожен і коли безкоштовна GitHub Action виграє в будь-якої оплати, нашої теж.","2026-09-01","\u002Fblog\u002Fsupabase-backup-tools-compared\u002Fcard-800x500.png",{"path":1021,"title":1022,"description":1023,"published":1024,"category":560,"image":1025,"draft":564},"\u002Fblog\u002Freplit-secrets-explained","Як користуватися секретами в Replit і що все одно публікується","Як користуватися секретами в Replit: додати, прочитати і усунути дві причини undefined. А ще ключі, які цей інструмент не здатен зберегти в таємниці.","2026-08-31","\u002Fblog\u002Freplit-secrets-explained\u002Fcard-800x500.png",{"path":1027,"title":1028,"description":1029,"published":1030,"category":667,"image":1031,"draft":564},"\u002Fblog\u002Fback-up-supabase-free-tier","Резервні копії на безкоштовному тарифі Supabase без терміналу","На безкоштовному тарифі Supabase резервних копій немає: копію робите ви. Як зробити її з панелі та що втрачає шлях через CSV.","2026-08-30","\u002Fblog\u002Fback-up-supabase-free-tier\u002Fcard-800x500.png",{"path":1033,"title":1034,"description":1035,"published":1036,"category":560,"image":1037,"draft":564},"\u002Fblog\u002Fis-supabase-secure","Чи безпечний Supabase? Так. Ваш проєкт це інше питання","Чи безпечний Supabase? Платформа проходить аудити, шифрує дані й тримає пентести. Їхня документація каже, де це закінчується і починаються ваші налаштування.","2026-08-29","\u002Fblog\u002Fis-supabase-secure\u002Fcard-800x500.png",{"path":351,"title":1039,"description":1040,"published":1041,"category":560,"image":1042,"draft":564},"Де знайти API-ключі Supabase: anon, service_role і URL","URL проєкту, ключ anon і ключ service_role лежать на одній сторінці панелі. Ось де вона, і яке з чотирьох значень має бути у вашому застосунку.","2026-08-28","\u002Fblog\u002Fwhere-to-find-supabase-api-keys\u002Fcard-800x500.png",{"path":1044,"title":1045,"description":1046,"published":1047,"category":560,"image":1048,"draft":564},"\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":1050,"title":1051,"description":1052,"published":1053,"category":560,"image":1054,"draft":564},"\u002Fblog\u002Fcors-wildcard-security-risk","Чи є вайлдкард CORS ризиком для безпеки? Майже ніколи.","Чи є вайлдкард CORS ризиком для безпеки? Зазвичай це типове налаштування вашого білдера, і воно не видає нічого, що ваш сервер і так віддавав кожному.","2026-08-26","\u002Fblog\u002Fcors-wildcard-security-risk\u002Fcard-800x500.png",{"path":1056,"title":1057,"description":1058,"published":1059,"category":667,"image":1060,"draft":564},"\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":1062,"title":1063,"description":1064,"published":1065,"category":667,"image":1066,"draft":564},"\u002Fblog\u002Fhow-to-restore-a-supabase-backup","Як відновити резервну копію Supabase, і що ламається потім","Як відновити резервну копію Supabase з панелі або з файлу дампа, що саме замінює відновлення і чому ваш застосунок може лишитися зламаним після нього.","2026-08-24","\u002Fblog\u002Fhow-to-restore-a-supabase-backup\u002Fcard-800x500.png",{"path":1068,"title":1069,"description":1070,"published":1071,"category":560,"image":1072,"draft":564},"\u002Fblog\u002Fsupabase-storage-bucket-public","Ваш бакет Supabase Storage публічний. Чи це проблема?","Публічний бакет Supabase Storage означає, що той, хто має URL файлу, може його відкрити. Він не означає, що хтось може переглянути список вмісту.","2026-08-23","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcard-800x500.png",{"path":1074,"title":1075,"description":1076,"published":1077,"category":560,"image":1078,"draft":564},"\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":1080,"title":1081,"description":1082,"published":1083,"category":667,"image":1084,"draft":564},"\u002Fblog\u002Fai-agent-deleted-my-database","ШІ-агент видалив мої дані в Supabase. Що можна відновити?","ШІ-агент видалив дані з вашої бази. Те, що ви зможете відновити, вирішилося раніше, а наступні хвилини вирішують, скільки з цього вціліє.","2026-08-21","\u002Fblog\u002Fai-agent-deleted-my-database\u002Fcard-800x500.png",{"path":1086,"title":1087,"description":1088,"published":1089,"category":560,"image":1090,"draft":564},"\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":1092,"title":1093,"description":1094,"published":1089,"category":560,"image":1095,"draft":564},"\u002Fblog\u002Fsource-maps-exposed-in-production","Відкриті source maps: ваш застосунок публікує свій вихідний код","Відкрита source map дає будь-кому прочитати оригінальний код вашого застосунку, разом із коментарями. Перевірка за 30 секунд, і що важить насправді.","\u002Fblog\u002Fsource-maps-exposed-in-production\u002Fcard-800x500.png",{"path":392,"title":1097,"description":1098,"published":1099,"category":560,"image":1100,"draft":564},"Supabase publishable key: це той самий anon key?","Supabase замінив anon і service_role публічними та секретними ключами. Який із них має бути у вашому застосунку, а який не має бути ніколи?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":1102,"title":1103,"description":1104,"published":1105,"category":667,"image":1106,"draft":564},"\u002Fblog\u002Fdoes-supabase-back-up-my-database","Чи робить Supabase резервні копії моєї бази? Залежить від тарифу.","Чи робить Supabase резервні копії вашої бази даних? На платних тарифах щодня, на безкоштовному жодної. Як дізнатися, що у вас насправді є.","2026-08-11","\u002Fblog\u002Fdoes-supabase-back-up-my-database\u002Fcard-800x500.png",{"path":1108,"title":1109,"description":1110,"published":1111,"category":560,"image":1112,"draft":564},"\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":1114,"title":1115,"description":1116,"published":1111,"category":667,"image":1117,"draft":564},"\u002Fblog\u002Fversion-history-is-not-a-backup","Історія версій не є резервною копією. Вона не поверне таблицю.","Lovable і Bolt ведуть історію версій вашого коду. Ваша база даних працює як окремий сервіс, тож відкат назад не поверне ваші дані.","\u002Fblog\u002Fversion-history-is-not-a-backup\u002Fcard-800x500.png",{"path":1119,"title":1120,"description":1121,"published":1122,"category":667,"image":1123,"draft":564},"\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":544,"title":1125,"description":1126,"published":1122,"category":560,"image":1127,"draft":564},"Які API-ключі безпечні у браузері, а які ні","Ваш ключ anon у Supabase створений, щоб бути публічним. Ключ service_role для цього не створений, і він ігнорує всі правила, які ви задали. Як їх розрізнити.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1794213654449]