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