<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tushar Bhendarkar</title>
    <description>The latest articles on DEV Community by Tushar Bhendarkar (@tusharbhendarkar).</description>
    <link>https://dev.to/tusharbhendarkar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4172570%2F1e64be41-4537-434a-bf74-ea3331a318fd.jpeg</url>
      <title>DEV Community: Tushar Bhendarkar</title>
      <link>https://dev.to/tusharbhendarkar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tusharbhendarkar"/>
    <language>en</language>
    <item>
      <title>Website Contact Form Not Sending Email: What Broke in 150 Real Cases</title>
      <dc:creator>Tushar Bhendarkar</dc:creator>
      <pubDate>Fri, 30 Oct 2026 03:30:02 +0000</pubDate>
      <link>https://dev.to/tusharbhendarkar/website-contact-form-not-sending-email-what-broke-in-150-real-cases-305</link>
      <guid>https://dev.to/tusharbhendarkar/website-contact-form-not-sending-email-what-broke-in-150-real-cases-305</guid>
      <description>&lt;p&gt;When a contact form stops emailing, the first advice is always to check the spam folder. I went through 150 public threads where a site owner's form stopped sending and the cause was confirmed, and the email was sitting in spam in 8 of them. In 63, more than 4 in 10, the website was sending the email in a way inbox providers don't trust, so it never arrived anywhere.&lt;/p&gt;

&lt;p&gt;The threads come from WordPress.org support for form and email plugins, Stack Exchange, and the forums of Netlify, Shopify, Square Online, Wix, Webflow and several hosts, from 2008 to 2026. I only counted a thread when the person confirmed the fix worked, an accepted answer named the cause, or support staff confirmed it.&lt;/p&gt;

&lt;p&gt;Below is where form emails get lost, how to find which step yours stops at, and the fix for each cause, with notes for WordPress, Wix, Shopify, Netlify and Webflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why contact form emails don't arrive: 150 confirmed cases
&lt;/h2&gt;

&lt;p&gt;A contact form email passes through three steps: the website has to send it, the sending server has to hand it to your email provider in a way it trusts, and your provider has to deliver it to your inbox. Each cause below breaks one of those steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmvpw5gwm69szhe5b4zr1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmvpw5gwm69szhe5b4zr1.png" alt="Bar chart of why 150 website contact forms stopped emailing their owners, grouped by step. Sending, 63: sent by the web server with no email login 23, From address was the visitor's email 14, host blocked or limited outgoing mail 13, email login broken or expired 8, domain missing SPF, DKIM or MX records 5. On the website, 38: the form never sent it 24, wrong recipient or form settings 14. Delivery, 38: the web server kept the email for itself 17, filtered as spam 8, platform stopped sending after a bounce 7, receiving server rejected it 6. Other, 11." width="800" height="440"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Sending is the weak step: 63 of 150. The spam folder, the usual first suggestion, held the email in 8.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;WordPress.org is 64 of the 150 threads, so WordPress causes weigh heavily. But the same causes came up on custom PHP sites, Joomla, Drupal, Magento and ASP.NET, because they all start from the same default: let the web server handle the email itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, find where your form email stops
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check the submission was saved.&lt;/strong&gt; WPForms, Forminator, Fluent Forms and Netlify Forms keep entries; for Contact Form 7, the free Flamingo plugin does. No entry means the form never sent anything, and the problem is on the website.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send the form's email to a test address.&lt;/strong&gt; Open the &lt;a href="https://tusharbhendarkar.com/tools/email-spam-tester" rel="noopener noreferrer"&gt;email spam tester&lt;/a&gt;, then set the form's notification address to the test address it gives you, or enter it as the visitor's email if your form sends an auto-reply. Submit once. If nothing arrives, the sending step is broken; if it arrives, the report shows whether it passed SPF, DKIM and DMARC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If the test arrives but your inbox never does&lt;/strong&gt;, the problem is in delivery: your web server keeping the email, a filter, or a blocked address.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Fix the sending: a real email login, from your own domain
&lt;/h2&gt;

&lt;p&gt;Three of the top causes have the same fix. In 23 cases the site sent through the web server's built-in mail, usually PHP mail(), with no login to a real email account, so Gmail, Outlook and company mail servers had no reason to trust it. In 14, the form put the visitor's address in the From field, so the email claimed to come from Yahoo or Gmail while being sent by your web host. In 13 more, the host blocked or limited outgoing mail, for reasons from spam and malware to plan limits and hosts with no mail service at all.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Send through a real account or sending service.&lt;/strong&gt; In WordPress, an SMTP plugin such as WP Mail SMTP, FluentSMTP or Post SMTP connects the site to Google Workspace, Microsoft 365, Zoho or a service like Brevo, Mailgun or Amazon SES. Custom sites do the same with a mail library and SMTP login, or the service's API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send From your own domain, Reply-To the visitor.&lt;/strong&gt; Use an address such as &lt;a href="mailto:website@yourdomain.com"&gt;website@yourdomain.com&lt;/a&gt; as the sender, and put the visitor's address in Reply-To, so clicking Reply still answers them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Publish SPF, DKIM and DMARC for the service you send through.&lt;/strong&gt; Five cases were missing records outright; the &lt;a href="https://tusharbhendarkar.com/blog/why-are-my-emails-going-to-spam" rel="noopener noreferrer"&gt;guide to why emails go to spam&lt;/a&gt; explains each one and what I found across 1,545 small business domains.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The From field matters more than it looks, because many visitors' email providers tell the world to refuse mail that pretends to be from them. Their published DMARC policies, as of October 2026:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Visitor's email&lt;/th&gt;
&lt;th&gt;DMARC policy&lt;/th&gt;
&lt;th&gt;A form email "From" them&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;yahoo.com, aol.com, comcast.net, verizon.net&lt;/td&gt;
&lt;td&gt;reject&lt;/td&gt;
&lt;td&gt;Refused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;btinternet.com, sky.com&lt;/td&gt;
&lt;td&gt;reject&lt;/td&gt;
&lt;td&gt;Refused&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;icloud.com, me.com, att.net, proton.me&lt;/td&gt;
&lt;td&gt;quarantine&lt;/td&gt;
&lt;td&gt;Sent to spam&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gmail.com, outlook.com, hotmail.com&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Often delivered, but not trusted&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's why a form can work when you test it with your Gmail address and fail for a customer on Yahoo. And if the email login itself breaks, which happened in 8 cases, half of them were Gmail connections: a normal password Google blocked, or an OAuth connection that expired or was left in testing mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the web server keeps your email (17 cases)
&lt;/h2&gt;

&lt;p&gt;This one surprises people, because the form works and the email is sent, just to the wrong place. When your website and your email are with different providers, for example the site on Bluehost or GoDaddy cPanel hosting and the email on Google Workspace or Microsoft 365, the web server may still think it handles email for your domain. Mail to &lt;a href="mailto:info@yourdomain.com"&gt;info@yourdomain.com&lt;/a&gt; then goes to a mailbox on the web server that nobody reads, and never reaches your real inbox, while mail to a Gmail address works fine.&lt;/p&gt;

&lt;p&gt;In cPanel, open Email Routing for the domain and switch it from Local Mail Exchanger to Remote Mail Exchanger. On Plesk, Virtualmin and similar panels, turn off the mail service for that domain. The giveaway is a form that delivers to an outside address but not to your own domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the form never sends (24 cases)
&lt;/h2&gt;

&lt;p&gt;In 10 of these, the form's own spam protection was rejecting real enquiries: expired or mismatched reCAPTCHA keys, Fluent Forms' token check, cookie consent plugins blocking the spam check, and twice WordPress's Disallowed Comment Keys list, which in one site contained ".com", so every message with an email address was treated as spam. After changing any anti-spam setting, submit the form yourself from a phone and a computer.&lt;/p&gt;

&lt;p&gt;The rest were code and setup: JavaScript errors from another plugin, fields without name attributes, a form whose PHP script was hosted where PHP can't run, and four Netlify Forms sites where the submission never reached Netlify's form handler, for example a React form with no plain HTML copy for Netlify to detect at build time. Check the browser console when you submit; an error there means the email was never attempted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Platform notes: Wix, Shopify, Square Online, Netlify, Webflow
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Wix:&lt;/strong&gt; notifications come from an automation; one had no recipient set, and a form built from loose input fields instead of a Wix Forms widget never triggered it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shopify:&lt;/strong&gt; contact form messages go to the store's Sender email in the notification settings, so check it is an inbox you read. Twice Shopify had stopped sending to an address after earlier bounces, which only support could clear.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Square Online:&lt;/strong&gt; one form had no recipient set on its Submit button; another worked but went to spam.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Netlify Forms:&lt;/strong&gt; five notification addresses were on Netlify's bounce suppression list after failed deliveries, one caused by a brief Gmail outage; Netlify support has to remove them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webflow:&lt;/strong&gt; one free-plan site hit the cap on form notification emails, so submissions were saved but no more emails went out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contact Form 7:&lt;/strong&gt; one site had &lt;code&gt;skip_mail: on&lt;/code&gt; in Additional Settings, which tells it not to send email at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to stop losing enquiries
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Keep a copy of every submission&lt;/strong&gt; in the form plugin or platform, so a mail problem never loses the lead itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Send through a real email account on your own domain&lt;/strong&gt;, with SPF, DKIM and DMARC set up for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test after every change&lt;/strong&gt; to hosting, DNS, email provider or plugins: one submission to the &lt;a href="https://tusharbhendarkar.com/tools/email-spam-tester" rel="noopener noreferrer"&gt;email spam tester&lt;/a&gt; shows whether it still arrives and passes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;More on email, domains and hosting is in &lt;a href="https://tusharbhendarkar.com/blog/topics/launch-hosting" rel="noopener noreferrer"&gt;Launch and hosting&lt;/a&gt;. And if you'd like it handled, I set up form email that reliably arrives as a client project. Email me the website address and what the form is built with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why is my website contact form not sending emails?&lt;/strong&gt;&lt;br&gt;
Most often the website sends the email through the web server's built-in mail, with no login to a real email account, so Gmail, Outlook and company mail servers drop it. The next most common causes are the form's own spam protection blocking real messages, and a web server that delivers mail for your domain to itself instead of to your real inbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why am I not receiving emails from my website contact form?&lt;/strong&gt;&lt;br&gt;
If the form says the message was sent, check whether the submission was saved, then where the email went. In 150 confirmed cases only 8 were in the spam folder; 17 were kept on the web server because it thought it handled the domain's email, and 7 were never sent because the platform had blocked the address after an earlier bounce.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I fix a WordPress contact form not sending email?&lt;/strong&gt;&lt;br&gt;
Install an SMTP plugin such as WP Mail SMTP, FluentSMTP or Post SMTP and connect it to a real email account or sending service on your own domain. Set the From address to that account and put the visitor's address in Reply-To. That was the fix in 32 of the 86 WordPress threads I found, more than any other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is Contact Form 7 not sending email?&lt;/strong&gt;&lt;br&gt;
Contact Form 7 uses WordPress's default mailer, which on many hosts sends unauthenticated mail that never arrives. Also check that the Mail tab's From field uses your own domain, that Additional Settings does not contain skip_mail: on, and that reCAPTCHA or a cookie plugin is not blocking submissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should the From address on a contact form be the visitor's email?&lt;/strong&gt;&lt;br&gt;
No. Send From an address on your own domain and put the visitor's address in Reply-To, so you can still reply with one click. Yahoo, AOL, Comcast, Verizon, BT and Sky publish a reject policy for their addresses, so a form email pretending to come from them gets refused.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I test my website contact form?&lt;/strong&gt;&lt;br&gt;
Submit the form once and check that the plugin or platform saved the entry. Then point the form's notification at the address from an email spam tester, or use it as the visitor's email if the form sends an auto-reply, and see whether the email arrives and passes SPF, DKIM and DMARC.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post first appeared on &lt;a href="https://tusharbhendarkar.com/blog/website-contact-form-not-sending-email" rel="noopener noreferrer"&gt;tusharbhendarkar.com&lt;/a&gt;. I write about the engineering behind what I ship. If something here is broken for you, email me at &lt;a href="mailto:tusharbhendarkar44@gmail.com"&gt;tusharbhendarkar44@gmail.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>email</category>
      <category>webdev</category>
      <category>php</category>
    </item>
    <item>
      <title>Too Many Redirects Error: What Caused 165 Real Cases, and the Fix for Each</title>
      <dc:creator>Tushar Bhendarkar</dc:creator>
      <pubDate>Tue, 27 Oct 2026 03:44:29 +0000</pubDate>
      <link>https://dev.to/tusharbhendarkar/too-many-redirects-error-what-caused-165-real-cases-and-the-fix-for-each-4ja7</link>
      <guid>https://dev.to/tusharbhendarkar/too-many-redirects-error-what-caused-165-real-cases-and-the-fix-for-each-4ja7</guid>
      <description>&lt;p&gt;When a site shows "too many redirects", the usual advice is to clear your cookies. I went through 165 public threads where someone hit this error and the cause was confirmed, and clearing cookies fixed just 4 of them. In 65, about 4 in 10, the cause was the same mistake: HTTPS handled in two places that can't see each other, so each one keeps sending the browser to the other.&lt;/p&gt;

&lt;p&gt;The threads come from Cloudflare Community, WordPress.org, Stack Overflow and the forums of hosts and site builders such as Netlify, Webflow and Ghost, from 2013 to 2026. I only counted a thread when the person confirmed the fix worked or an accepted answer named the cause.&lt;/p&gt;

&lt;p&gt;Below is what the error means, what caused those 165 loops, how to tell which layer your loop is in from the addresses it bounces between, and the fix for each.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the too many redirects error means
&lt;/h2&gt;

&lt;p&gt;A redirect is a site telling the browser "this page lives at another address, go there". A loop happens when two rules undo each other: one sends the browser from A to B, the other from B back to A. The browser follows along until it hits its limit. Chrome and Firefox give up after 20 redirects, the limit set by the web's &lt;a href="https://fetch.spec.whatwg.org/#http-redirect-fetch" rel="noopener noreferrer"&gt;Fetch standard&lt;/a&gt;, and show one of these messages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chrome and Edge:&lt;/strong&gt; "This page isn't working. yoursite.com redirected you too many times." with ERR_TOO_MANY_REDIRECTS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safari:&lt;/strong&gt; "Safari can't open the page. Too many redirects occurred trying to open..."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firefox:&lt;/strong&gt; "The page isn't redirecting properly"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;If you're only visiting the site:&lt;/strong&gt; reload once, then delete the cookies and site data for just that site and try again, or open it in a private window. In Chrome, click the icon to the left of the address and open the site's cookies and site data. In Safari on iPhone, go to Settings, Safari (under Apps on newer iOS), Advanced, Website Data, and remove the site. If it still fails, the problem is on the site's side, and the rest of this post is for its owner.&lt;/p&gt;

&lt;h2&gt;
  
  
  What causes too many redirects: 165 confirmed cases
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr5527rx4v0yeyqgcj8hb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr5527rx4v0yeyqgcj8hb.png" alt="Bar chart of the causes of 165 confirmed too many redirects errors: Cloudflare SSL mode Flexible 40, HTTPS hidden by a proxy or load balancer 25, a WordPress plugin 24, host or site builder domain settings 15, trailing slash or language redirects 12, login or middleware loops 11, server rules 11, other app bugs 9, WordPress site address 5, conflicting www and https rules 5, a Cloudflare redirect rule 4, browser cookies or cache 4." width="800" height="440"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The two red bars are the same mistake in different places: HTTPS handled twice. Browser cookies, the usual first suggestion, fixed 4.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cloudflare's own forum is a quarter of the sample, which inflates the Cloudflare causes. But outside that forum the same mistake still leads: Flexible SSL in 14 threads, and HTTPS hidden by a proxy or load balancer in 22 more.&lt;/p&gt;

&lt;p&gt;The pattern behind the top two is worth understanding, because it explains most loops. Your visitor's connection is secure up to some middle layer, such as Cloudflare, a load balancer or a reverse proxy, and that layer talks to your server over plain http. Your server sees an http request, redirects it to https, and the middle layer passes that redirect back to the browser, which asks again over https. Round and round, 20 times.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find the loop from the addresses it bounces between
&lt;/h2&gt;

&lt;p&gt;Before changing settings, look at the actual hops. Paste the address into the &lt;a href="https://tusharbhendarkar.com/tools/redirect-checker" rel="noopener noreferrer"&gt;redirect checker&lt;/a&gt;, which follows each redirect and stops at the loop, or run &lt;code&gt;curl -IL https://yoursite.com&lt;/code&gt; in a terminal. Where the addresses bounce tells you which layer is to blame:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The hops look like&lt;/th&gt;
&lt;th&gt;Usual cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The same https address, over and over&lt;/td&gt;
&lt;td&gt;HTTPS handled twice: Cloudflare Flexible, a proxy or load balancer, or a tunnel connecting over http&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yoursite.com to &lt;a href="http://www.yoursite.com" rel="noopener noreferrer"&gt;www.yoursite.com&lt;/a&gt; and back&lt;/td&gt;
&lt;td&gt;Two layers disagree on www: host or site builder settings against Cloudflare, the server or the CMS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/page to /page/ and back&lt;/td&gt;
&lt;td&gt;Trailing slash rules: the framework adds the slash and the host or proxy strips it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/en to / and back, or between language subdomains&lt;/td&gt;
&lt;td&gt;A language redirect that also runs on the address it redirects to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/dashboard to /login and back&lt;/td&gt;
&lt;td&gt;Login or middleware: the session is not seen, or the login page runs the same check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One address redirecting to itself&lt;/td&gt;
&lt;td&gt;A rule matching its own target, such as a too-broad redirect plugin rule&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If the loop only happens for you and not in a private window or on your phone, it is the rare cookie case. Otherwise it is in the site's settings, and the fixes below follow the order of how often each cause came up.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to fix too many redirects, cause by cause
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cloudflare SSL mode set to Flexible (40 cases)
&lt;/h3&gt;

&lt;p&gt;In Cloudflare, open SSL/TLS and change the encryption mode from Flexible to Full (strict), or Full if your server only has a self-signed certificate. That makes Cloudflare connect to your server over https, so the server stops redirecting. Cloudflare's &lt;a href="https://developers.cloudflare.com/ssl/troubleshooting/too-many-redirects/" rel="noopener noreferrer"&gt;troubleshooting guide&lt;/a&gt; covers the same fix. Check it even if you never picked Flexible: in three of the threads, Cloudflare's Automatic setting had chosen it.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTPS hidden by a proxy or load balancer (25 cases)
&lt;/h3&gt;

&lt;p&gt;This is the same loop without Cloudflare: an AWS load balancer, Heroku's router, nginx in front of Apache, a Docker proxy or a Kubernetes ingress handles https and passes plain http to your app. The fix is to let the app trust the X-Forwarded-Proto header that the proxy sets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WordPress:&lt;/strong&gt; add the snippet from WordPress's &lt;a href="https://developer.wordpress.org/advanced-administration/security/https/" rel="noopener noreferrer"&gt;HTTPS guide&lt;/a&gt; to wp-config.php, which sets &lt;code&gt;$_SERVER['HTTPS'] = 'on'&lt;/code&gt; when the header says https.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Django:&lt;/strong&gt; set &lt;code&gt;SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")&lt;/code&gt;, with the HTTP_ prefix, which one thread missed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Express:&lt;/strong&gt; call &lt;code&gt;app.set('trust proxy', 1)&lt;/code&gt; and check &lt;code&gt;req.secure&lt;/code&gt;, not the raw protocol, which is always http behind Heroku.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;nginx in front of another app:&lt;/strong&gt; pass &lt;code&gt;proxy_set_header X-Forwarded-Proto https&lt;/code&gt; when an outer proxy already handled https, because &lt;code&gt;$scheme&lt;/code&gt; will be http there.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three of these cases were a Cloudflare Tunnel pointed at &lt;code&gt;http://localhost&lt;/code&gt; while the server forced https. Point the tunnel at the https port instead, or stop redirecting on the server; my guide to &lt;a href="https://tusharbhendarkar.com/blog/cloudflare-tunnel-local-machine-public-api" rel="noopener noreferrer"&gt;exposing a local API with Cloudflare Tunnel&lt;/a&gt; shows the setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  A WordPress plugin (24 cases)
&lt;/h3&gt;

&lt;p&gt;SEO, redirect, security, membership and caching plugins all add redirects. Yoast SEO came up in 6 threads, half of them its option to remove the category base from URLs; the Redirection plugin looped when a rule matched its own new address. If you can't reach the dashboard, rename the plugin's folder in wp-content/plugins to turn it off, then turn plugins back on one at a time. Security plugins can leave rules in .htaccess that keep looping after they are removed, so check that file too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Domain settings on the host or site builder (15 cases)
&lt;/h3&gt;

&lt;p&gt;Site builders and hosts pick one address as the main one and redirect the rest to it. In 5 of the Webflow threads, Webflow's default domain was the bare domain while the DNS was set up for www, and making the www address the default fixed it. Netlify and Google Sites looped when their DNS records were switched to Proxied in Cloudflare; setting them to DNS only fixed it. Most of the rest were DNS records pointing somewhere other than the host, such as a Shopify store whose A record did not point to Shopify, or registrar forwarding left on for a Bubble app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trailing slash, language and login loops (23 cases)
&lt;/h3&gt;

&lt;p&gt;These are rules in the app itself. A language redirect has to skip addresses that already have the language; a trailing slash rule has to agree with what the host does to the URL; and login middleware must not run on the login page, static files or the page it sends users to. In Next.js, use the middleware matcher to exclude those paths. When a login loop only happens in production, check that the session cookie and the app's base URL use the same host, including www.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server rules, site address and www conflicts (25 cases)
&lt;/h3&gt;

&lt;p&gt;In WordPress, the WordPress Address and Site Address under Settings, General must match the real address exactly; two threads were fixed by removing capital letters from them. Then make sure only one layer decides between www and the bare domain and between http and https: the host, a Cloudflare redirect rule, .htaccess or nginx, not two of them. Four of the loops came from Cloudflare redirect rules, so check Rules in the Cloudflare dashboard too.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to stop redirect loops from coming back
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Decide one final address&lt;/strong&gt;, such as &lt;a href="https://www.yoursite.com" rel="noopener noreferrer"&gt;https://www.yoursite.com&lt;/a&gt;, and write it down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let one layer do each redirect.&lt;/strong&gt; If Cloudflare forces https, the server doesn't need to; if the host picks www, nothing else should.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test after every change&lt;/strong&gt; to DNS, SSL, plugins or hosting: the &lt;a href="https://tusharbhendarkar.com/tools/redirect-checker" rel="noopener noreferrer"&gt;redirect checker&lt;/a&gt; tests http, https, www and the bare domain at once, and all four should reach the final address in one hop.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;More on domains, SSL and hosting is in &lt;a href="https://tusharbhendarkar.com/blog/topics/launch-hosting" rel="noopener noreferrer"&gt;Launch and hosting&lt;/a&gt;. And if you'd rather hand it over, I fix redirect loops and run domain and hosting moves as client projects. Email me the site address and where it is hosted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What does "too many redirects" mean?&lt;/strong&gt;&lt;br&gt;
The site kept telling the browser to go to another address, which sent it back again, until the browser gave up. Chrome and Firefox stop after 20 redirects, the limit set by the web's Fetch standard. It is almost always a problem with the site, not your device.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I fix too many redirects in Safari or Chrome?&lt;/strong&gt;&lt;br&gt;
Reload once, then delete the cookies and site data for just that site and try again, or open it in a private window. In Safari on iPhone that is Settings, Safari (under Apps on newer iOS), Advanced, Website Data. If it still fails, the site itself is broken and only its owner can fix it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does clearing cookies fix too many redirects?&lt;/strong&gt;&lt;br&gt;
Rarely. In 165 cases where the cause was confirmed, clearing cookies or cache was the fix in only 4, all visitors to sites like Amazon, Gmail and Yahoo. In the other 161 the loop was in the site's own settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does Cloudflare cause too many redirects?&lt;/strong&gt;&lt;br&gt;
With the SSL/TLS mode set to Flexible, Cloudflare connects to your server over plain http. If the server redirects http to https, Cloudflare passes that back to the browser, which asks again over https, and the loop never ends. Switch the mode to Full (strict).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I fix too many redirects in WordPress?&lt;/strong&gt;&lt;br&gt;
Check Cloudflare's SSL mode first, then make the WordPress Address and Site Address match your real address exactly, including https and &lt;a href="http://www" rel="noopener noreferrer"&gt;www&lt;/a&gt;. If the loop started after installing or changing a plugin, rename its folder in wp-content/plugins to turn it off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How many redirects is too many?&lt;/strong&gt;&lt;br&gt;
Browsers give up at 20, but a healthy address needs at most one: from http or www straight to the final https address. Google's crawlers follow up to 10 hops, and every extra hop slows the page down.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post first appeared on &lt;a href="https://tusharbhendarkar.com/blog/too-many-redirects-error" rel="noopener noreferrer"&gt;tusharbhendarkar.com&lt;/a&gt;. I write about the engineering behind what I ship. If something here is broken for you, email me at &lt;a href="mailto:tusharbhendarkar44@gmail.com"&gt;tusharbhendarkar44@gmail.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cloudflare</category>
      <category>webdev</category>
      <category>wordpress</category>
      <category>nginx</category>
    </item>
    <item>
      <title>How Much Does It Cost to Build an App for My Business? Real 2026 Numbers</title>
      <dc:creator>Tushar Bhendarkar</dc:creator>
      <pubDate>Mon, 26 Oct 2026 03:30:05 +0000</pubDate>
      <link>https://dev.to/tusharbhendarkar/how-much-does-it-cost-to-build-an-app-for-my-business-real-2026-numbers-448c</link>
      <guid>https://dev.to/tusharbhendarkar/how-much-does-it-cost-to-build-an-app-for-my-business-real-2026-numbers-448c</guid>
      <description>&lt;p&gt;Half of the small businesses posting a new app build on Upwork in September budgeted $900 or less. The simplest real app, a web tool with sign-up and one main screen, is about 124 hours of work, which comes to $4,600 to $9,200 at going rates. That gap is why so many app projects stall, get abandoned halfway, or launch and then break.&lt;/p&gt;

&lt;p&gt;I see the budgets because I track freelance job posts, and I see the cost side because I build these apps. So I priced five apps small businesses ask for most, hour by hour, using a published estimating model, and put them next to what owners actually budget.&lt;/p&gt;

&lt;p&gt;Below is what each one costs, where the hours go, why some quotes come in so much lower, and how to spend less without paying twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What small business apps cost, against what owners budget
&lt;/h2&gt;

&lt;p&gt;The budgets come from 34 fixed-price job posts for a new app or SaaS build in my September 2026 sample of 501 Upwork posts: the middle one was $900, and the middle half ran from $100 to $3,500. The costs use the hour estimates published by &lt;a href="https://appdevelopmentcostcalculator.com/methodology/" rel="noopener noreferrer"&gt;appdevelopmentcostcalculator.com&lt;/a&gt; in October 2026, priced at the middle of Clutch's hourly bands: $37 for an offshore app company and $74.50 for a US software agency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F97pnt59zisiph9d0jqgz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F97pnt59zisiph9d0jqgz.png" alt="Range chart of estimated build costs for five small-business apps against a $900 median Upwork budget: simple web tool $4.6k to $9.2k, AI document assistant $8.8k to $18k, customer portal $15k to $31k, booking web app $17k to $34k, booking app on iPhone, Android and web $32k to $64k." width="800" height="440"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Even the simplest app costs about five times the typical budget. Budgets: 34 fixed-price new-app posts on Upwork, September 2026.&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;App&lt;/th&gt;
&lt;th&gt;Hours&lt;/th&gt;
&lt;th&gt;Weeks at 30 h/week&lt;/th&gt;
&lt;th&gt;Cost range&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Simple web tool: sign-up and one main screen&lt;/td&gt;
&lt;td&gt;124&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;$4,600 to $9,200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI assistant that answers from your documents&lt;/td&gt;
&lt;td&gt;239&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;$8,800 to $18,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Customer portal: logins, files, payments, admin&lt;/td&gt;
&lt;td&gt;412&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;$15,000 to $31,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Booking web app with payments and reminders&lt;/td&gt;
&lt;td&gt;451&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;$17,000 to $34,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Booking app on iPhone, Android and web&lt;/td&gt;
&lt;td&gt;862&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;$32,000 to $64,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;None of these include hosting, app store fees or the monthly bill for an AI model, which depend on how many people use the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the hours go
&lt;/h2&gt;

&lt;p&gt;Take the booking web app: sign-up, a booking calendar, card payments, reminders, an admin dashboard and one connection to another tool, such as your accounting software. Of its 451 hours, building the features themselves is only 176, or 39%. The rest is what makes the app safe to put in front of customers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Testing, 77 hours (17%).&lt;/strong&gt; Every payment path, every booking clash, every phone size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Foundation and backend setup, 66 hours (15%).&lt;/strong&gt; The database, sign-in plumbing and hosting everything else sits on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code review and hardening, 61 hours (13%).&lt;/strong&gt; Security checks and fixing what testing finds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Planning and calls, 55 hours (12%).&lt;/strong&gt; Agreeing what gets built, and changes along the way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design, 16 hours (4%)&lt;/strong&gt; with a ready-made UI kit; a custom design adds much more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Features differ a lot, too. In the same model, sign-up and login are about 37 hours, card payments about 62, and chat between users about 108, because messaging needs real-time updates, storage and moderation. You can try your own mix in the &lt;a href="https://tusharbhendarkar.com/tools/app-development-cost-calculator" rel="noopener noreferrer"&gt;app development cost calculator&lt;/a&gt;, which shows the same hours at offshore, freelance and US agency rates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why some quotes come in at $500
&lt;/h2&gt;

&lt;p&gt;When the budget is $900, someone will still take the job. What you get for it is usually one of these: a template with your logo on it, a no-code setup you'll outgrow, the screens without a working backend, or a first draft that skips the 42% of hours that go into testing, review and planning. None of those are wrong if you know that's what you're buying. The trouble is when it's sold as the finished app.&lt;/p&gt;

&lt;p&gt;AI app builders have changed the low end. Lovable, Bolt and Replit can produce a working first version in days for the price of a subscription, and for testing an idea that's often the right call. But the same gap shows up later: the &lt;a href="https://tusharbhendarkar.com/blog/lovable-app-not-working" rel="noopener noreferrer"&gt;Lovable apps&lt;/a&gt; and &lt;a href="https://tusharbhendarkar.com/blog/replit-app-not-working" rel="noopener noreferrer"&gt;Replit apps&lt;/a&gt; I've looked at mostly break on the same production work, such as login, data security, payments and deployment, that a cheap quote leaves out.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes the price most
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mobile apps.&lt;/strong&gt; Building for iPhone, Android and the web takes about 1.85 times the build hours of a web app alone, and testing doubles. That's most of the jump from $17,000 to $32,000 in the table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments and subscriptions.&lt;/strong&gt; Card payments and subscriptions are among the heaviest everyday features, and the ones you least want to get wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connections to your other tools.&lt;/strong&gt; Each one, such as QuickBooks, HubSpot or Shopify, adds about 37 hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI features.&lt;/strong&gt; An AI assistant adds about 72 hours on its own, mostly server work, plus the AI provider's monthly bill.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to spend less without paying twice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build one job first.&lt;/strong&gt; Pick the single thing a customer must be able to do, and cut the rest. An admin dashboard can start as a spreadsheet; subscriptions can start as one-off payments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start on the web.&lt;/strong&gt; A web app that works on phones costs about half as much as native apps, and can often be installed like an app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use a managed backend&lt;/strong&gt; such as Supabase or Firebase, which cuts server work by about 30% compared with a custom server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prototype with an AI builder, then pay for the production work&lt;/strong&gt;: security, payments, testing and deployment, rather than rebuilding from scratch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agree the scope per milestone&lt;/strong&gt;, so you see a working piece before paying for the next.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're still deciding, the &lt;a href="https://tusharbhendarkar.com/tools/app-development-cost-calculator" rel="noopener noreferrer"&gt;cost calculator&lt;/a&gt; lets you untick features and see what each one adds, and there are more guides on getting a first version built in &lt;a href="https://tusharbhendarkar.com/blog/topics/build-mvp" rel="noopener noreferrer"&gt;Build your MVP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And if you'd like it built, I take on app builds as client projects, from a first version to finishing one an AI builder started. Email me what the app needs to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How much does it cost to build an app for a small business?&lt;/strong&gt;&lt;br&gt;
At 2026 market rates, a simple web app with sign-up and one main screen is about $4,600 to $9,200, a customer portal or booking web app $15,000 to $34,000, and a booking app on iPhone, Android and the web $32,000 to $64,000. The range depends mostly on which features you need and whether it has to be a native mobile app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much does AI app development cost?&lt;/strong&gt;&lt;br&gt;
An assistant that answers questions from your own documents, with sign-up and file uploads, is about 239 hours of work, roughly $8,800 to $18,000 at offshore to US agency rates. On top of that you pay the AI provider every month, and that bill grows with how much people use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why do some developers quote $500 for an app?&lt;/strong&gt;&lt;br&gt;
A quote that low usually covers a template, a no-code setup, or only the screens, without the server, testing and launch work that make up more than half of a real build. Compare quotes by what is included and how many hours they assume, not just the total.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How long does it take to build an app?&lt;/strong&gt;&lt;br&gt;
A simple web app is about 4 weeks for one developer working 30 hours a week, a booking or customer portal web app about 14 to 15 weeks, and a booking app on iPhone, Android and the web about 29 weeks. A smaller first version can launch much sooner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I build a web app or a mobile app first?&lt;/strong&gt;&lt;br&gt;
For most small businesses, a web app that works well on phones first. Building for iPhone and Android as well takes nearly twice the build hours, and you can add the stores later once people use it; an installable web app covers much of the gap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it cheaper to build my app with Lovable, Bolt or Replit?&lt;/strong&gt;&lt;br&gt;
Usually, for a first version. These tools can get a working prototype in days. The cost comes later, when it needs to be secure, reliable and connected to payments, which is where most of the problems I see with AI-built apps start.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post first appeared on &lt;a href="https://tusharbhendarkar.com/blog/how-much-does-it-cost-to-build-an-app" rel="noopener noreferrer"&gt;tusharbhendarkar.com&lt;/a&gt;. I write about the engineering behind what I ship. If something here is broken for you, email me at &lt;a href="mailto:tusharbhendarkar44@gmail.com"&gt;tusharbhendarkar44@gmail.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mvp</category>
      <category>startup</category>
      <category>webdev</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Turn Your Own Machine Into a Public API Server (For Free) Using Cloudflare Tunnel</title>
      <dc:creator>Tushar Bhendarkar</dc:creator>
      <pubDate>Sun, 25 Oct 2026 03:30:03 +0000</pubDate>
      <link>https://dev.to/tusharbhendarkar/turn-your-own-machine-into-a-public-api-server-for-free-using-cloudflare-tunnel-e3m</link>
      <guid>https://dev.to/tusharbhendarkar/turn-your-own-machine-into-a-public-api-server-for-free-using-cloudflare-tunnel-e3m</guid>
      <description>&lt;p&gt;Recently, I wanted to use my own machine as a server. Not for production, just experimenting. Testing an API. Saving cloud costs. Trying out a few AI endpoints.&lt;/p&gt;

&lt;p&gt;I expected this to be complicated: VPNs, port forwarding, firewalls, all the usual pain. So I researched a bunch of solutions. And surprisingly, the cleanest, fastest, most secure option was 👉 &lt;strong&gt;Cloudflare Tunnel&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's free. It's easy. And it works beautifully. Sharing the exact steps I used in case someone else needs this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F63uniopnphevefuc5l46.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F63uniopnphevefuc5l46.png" alt="Diagram of Cloudflare Tunnel: a visitor calls a hostname on Cloudflare's edge, cloudflared on your machine keeps an outbound tunnel open, and traffic reaches localhost:8000 without opening inbound ports."&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;cloudflared connects out to Cloudflare, so your router and firewall stay closed.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: Install cloudflared
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;cloudflared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Log in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cloudflared tunnel login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This connects your Cloudflare account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create a Tunnel
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cloudflared tunnel create my-tunnel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cloudflare will generate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a tunnel ID&lt;/li&gt;
&lt;li&gt;a credentials JSON file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This authenticates your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: (Optional) Map a Domain / Subdomain
&lt;/h2&gt;

&lt;p&gt;If you want a custom URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cloudflared tunnel route dns my-tunnel api.yourwebsite.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No domain? For quick tests, &lt;code&gt;cloudflared tunnel --url http://localhost:8000&lt;/code&gt; gives you a temporary trycloudflare.com address instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Configure the Tunnel
&lt;/h2&gt;

&lt;p&gt;Create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;~/.cloudflared/config.yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;tunnel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;your-tunnel-id&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;credentials-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;path-to-credentials.json&amp;gt;&lt;/span&gt;

&lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api.example.com&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:8000&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http_status:404&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means: public URL → tunnel → your local FastAPI server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Run the Tunnel
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cloudflared tunnel run my-tunnel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! Your local API is now securely exposed to the internet. No port forwarding. No firewall changes. No server deployment.&lt;/p&gt;

&lt;p&gt;Congrats! Your machine is now acting as a secure public API server, without any deployment or port forwarding. Feels like magic the first time you see it work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional: Keep It Running Automatically
&lt;/h2&gt;

&lt;p&gt;On macOS you can use launchctl to keep the tunnel alive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;launchctl load ~/Library/LaunchAgents/com.tunnel.plist
launchctl start com.tunnel
launchctl stop com.tunnel
launchctl unload ~/Library/LaunchAgents/com.tunnel.plist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🎯 Why This Is Useful
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Test APIs without deploying&lt;/li&gt;
&lt;li&gt;Run local AI inference endpoints&lt;/li&gt;
&lt;li&gt;Prototype backend changes instantly&lt;/li&gt;
&lt;li&gt;Webhook testing&lt;/li&gt;
&lt;li&gt;Connect a frontend to a local backend&lt;/li&gt;
&lt;li&gt;Experiment without cloud costs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you try this, you realize how powerful "local → public" can be.&lt;/p&gt;

&lt;p&gt;If the API behind your tunnel is a chatbot that answers from documents, here is &lt;a href="https://tusharbhendarkar.com/blog/ai-chatbot-giving-wrong-answers" rel="noopener noreferrer"&gt;why it may give wrong answers and how to fix it&lt;/a&gt;. More notes like this one are in &lt;a href="https://tusharbhendarkar.com/blog/topics/engineering" rel="noopener noreferrer"&gt;engineering notes&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is Cloudflare Tunnel free?&lt;/strong&gt;&lt;br&gt;
Yes. Cloudflare Tunnel is free to use with a Cloudflare account, which is what makes it a good fit for testing APIs and running small projects from your own machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need to open ports or change my firewall?&lt;/strong&gt;&lt;br&gt;
No. cloudflared makes an outbound connection from your machine to Cloudflare, so nothing has to be forwarded on your router and no inbound firewall rule is needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need my own domain to use Cloudflare Tunnel?&lt;/strong&gt;&lt;br&gt;
Not for testing. Running cloudflared tunnel --url &lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt; gives you a temporary trycloudflare.com address. For a stable address, route the tunnel to a hostname on a domain in your Cloudflare account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is it safe to expose my local machine this way?&lt;/strong&gt;&lt;br&gt;
Only the services listed in the ingress rules are reachable, and visitors connect to Cloudflare rather than to your IP address. If the API should not be public, put Cloudflare Access in front of the hostname so only approved users get through.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post first appeared on &lt;a href="https://tusharbhendarkar.com/blog/cloudflare-tunnel-local-machine-public-api" rel="noopener noreferrer"&gt;tusharbhendarkar.com&lt;/a&gt;. I write about the engineering behind what I ship. If something here is broken for you, email me at &lt;a href="mailto:tusharbhendarkar44@gmail.com"&gt;tusharbhendarkar44@gmail.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cloudflare</category>
      <category>selfhosted</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How I Debugged Expensify Bugs &amp; Got Paid</title>
      <dc:creator>Tushar Bhendarkar</dc:creator>
      <pubDate>Sat, 24 Oct 2026 03:30:02 +0000</pubDate>
      <link>https://dev.to/tusharbhendarkar/how-i-debugged-expensify-bugs-got-paid-1o7e</link>
      <guid>https://dev.to/tusharbhendarkar/how-i-debugged-expensify-bugs-got-paid-1o7e</guid>
      <description>&lt;p&gt;Expensify paid me $500 for fixing two bugs in their app. I don't work there. Nobody assigned me the work. I found the bugs on GitHub, wrote up the root causes, and my fixes were merged into a React Native app used by more than 10 million people.&lt;/p&gt;

&lt;p&gt;The money is the hook, but it's not the story. The story is what it takes to get a fix merged into a codebase that size when nobody knows who you are. Here is how both bugs went down, plus two investigations that never became PRs but taught me just as much. (The contracts, payments and review are in the &lt;a href="https://tusharbhendarkar.com/work/expensify" rel="noopener noreferrer"&gt;Expensify case study&lt;/a&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  How Expensify's bounty system works
&lt;/h2&gt;

&lt;p&gt;Expensify runs one of the most open contributor programs of any production app. Bugs are filed as public GitHub issues, most carry a bounty (typically $250), and anyone can submit a proposal: a written explanation of the root cause and the fix you intend to make. If your proposal is approved, you implement it, go through review like any employee would, and get paid when it ships.&lt;/p&gt;

&lt;p&gt;The catch: proposals compete. The way you win is not by being first, it's by being the person who actually understood the bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug 1: The onboarding modal that wouldn't go away
&lt;/h2&gt;

&lt;p&gt;The symptom: a user connects Xero (accounting software) to their Expensify account, has two-factor authentication disabled, and refreshes the page. The onboarding modal pops up behind the 2FA setup prompt. This user finished onboarding long ago. The modal had no business being there (&lt;a href="https://github.com/Expensify/App/issues/46026" rel="noopener noreferrer"&gt;issue #46026&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The obvious place to look was the modal component. That was the wrong place.&lt;/p&gt;

&lt;p&gt;The network tab told the real story. On app load, the client calls an &lt;code&gt;OpenApp&lt;/code&gt; API to fetch initial user data into Onyx, Expensify's client-side state store. When Xero is connected and 2FA is disabled, the server refuses with error code 666 ("Your organization requires Two Factor Authentication") and returns &lt;strong&gt;empty&lt;/strong&gt; data. That means the &lt;code&gt;nvp_onboarding&lt;/code&gt; key, the flag saying "this user finished onboarding", never arrives on the client.&lt;/p&gt;

&lt;p&gt;From there the dominoes fall in order: the &lt;code&gt;hasCompletedGuidedSetupFlow&lt;/code&gt; selector reads the missing key and returns false, the navigation root sees an "un-onboarded" user, and it calculates the onboarding route as the app's initial state. The UI was doing exactly what it was told. The data was the problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpoi7bzsc20xrgmh6lu7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpoi7bzsc20xrgmh6lu7j.png" alt="Data flow of Expensify issue 46026: OpenApp returns error 666 with empty data, the onboarding flag never arrives, and navigation starts at onboarding. The fix makes the initial state aware of the pending 2FA requirement." width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Every step did what it was told. The missing onboarding flag, not the modal, was the bug.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My fix (&lt;a href="https://github.com/Expensify/App/pull/46750" rel="noopener noreferrer"&gt;PR #46750&lt;/a&gt;, three files) made the initial-state calculation aware of the pending 2FA gate: if the 2FA requirement modal is about to show, don't treat missing onboarding data as "user needs onboarding". Merged, $250.&lt;/p&gt;

&lt;p&gt;The lesson: when a screen does something impossible, ask what data it was missing, not which component misbehaved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug 2: The chat that vanished when you pressed the down arrow
&lt;/h2&gt;

&lt;p&gt;The symptom: in Search, navigating chats with the keyboard scrolled the selected chat out of view whenever the chats were large (&lt;a href="https://github.com/Expensify/App/issues/48902" rel="noopener noreferrer"&gt;issue #48902&lt;/a&gt;). An accessibility bug: if you can't see what's focused, keyboard navigation is useless.&lt;/p&gt;

&lt;p&gt;The cause lived in React Native's &lt;code&gt;SectionList&lt;/code&gt; virtualization. The shared list component computes &lt;code&gt;getItemLayout&lt;/code&gt; offsets by hand, and the list header and footer were accounted for differently than &lt;code&gt;SectionList&lt;/code&gt; expects. Combine that with SectionList's default behavior of keeping exactly one item above the focused one, and items that can grow taller than the screen, and the focused chat ends up off-screen.&lt;/p&gt;

&lt;p&gt;Here is the part that made this bug hard: the component, &lt;code&gt;BaseSelectionList&lt;/code&gt;, is shared by a huge surface of the app. The reviewer suggested only pushing section header layouts conditionally. I tested that branch across my accounts and found it broke the virtualized list entirely on one of them, and I posted a video proving it (&lt;a href="https://github.com/Expensify/App/issues/48902#issuecomment-2431756897" rel="noopener noreferrer"&gt;the discussion&lt;/a&gt;). We converged on &lt;code&gt;viewOffsetToKeepFocusedItemInView&lt;/code&gt;: admittedly a workaround, and I said so in the thread, but it was the change whose failure modes we could actually enumerate.&lt;/p&gt;

&lt;p&gt;My fix (&lt;a href="https://github.com/Expensify/App/pull/51754" rel="noopener noreferrer"&gt;PR #51754&lt;/a&gt;) shipped. Merged, $250.&lt;/p&gt;

&lt;p&gt;The lesson: in a shared component, the best fix is not the most elegant one. It's the one whose blast radius you can defend in review.&lt;/p&gt;

&lt;h2&gt;
  
  
  The investigations that never became PRs
&lt;/h2&gt;

&lt;p&gt;Two more bugs ate my evenings without paying out, and they were still worth it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The back button that went home&lt;/strong&gt;(&lt;a href="https://github.com/Expensify/App/issues/47562#issuecomment-2313075324" rel="noopener noreferrer"&gt;issue #47562&lt;/a&gt;): creating a workspace from the search page, then pressing back, dumped you at the inbox instead of where you came from. The existing code "fixed" this with &lt;code&gt;Navigation.resetToHome()&lt;/code&gt;, which is a mask, not a fix. I traced the real cause into the core routing logic: an extra route was being pushed into the root navigation state because a flag (&lt;code&gt;isCentralPaneAndBottomTabMandatory&lt;/code&gt;) stayed true during full-screen navigator actions. I proposed both the targeted fix and the deeper flag change, with file-and-line links for every claim.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The mis-styled system message&lt;/strong&gt;(&lt;a href="https://github.com/Expensify/App/issues/47487#issuecomment-2301724004" rel="noopener noreferrer"&gt;issue #47487&lt;/a&gt;): a system message ("exported this report to CSV") rendered faded with an error indicator. Everyone's instinct said frontend styling bug. I clicked "Reply in thread" and watched the backend return an error for exactly this message type and no other. The styling was the app correctly rendering a server-side data problem. That analysis redirected the fix to the PHP backend and saved someone a week of patching the wrong layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What transfers to any large codebase
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reproduce before you read code.&lt;/strong&gt; Both merged fixes started with a reliable reproduction, not a theory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write the root cause down with evidence.&lt;/strong&gt; Every proposal linked the exact lines involved. Maintainers can verify a claim in minutes instead of re-deriving it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trace the data, not the symptom.&lt;/strong&gt; One bug looked like a modal problem and was a missing API payload. Another looked like CSS and was a backend error. The same rule explains &lt;a href="https://tusharbhendarkar.com/blog/ai-chatbot-giving-wrong-answers" rel="noopener noreferrer"&gt;why AI chatbots give wrong answers&lt;/a&gt;: the model is rarely the problem, the data it receives is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Respect the blast radius.&lt;/strong&gt; When a component is shared everywhere, choose the fix you can test the edges of, and show your testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to break into a large open source codebase, find a project that pays for fixed bugs and treat the proposal like the product. The bounty is $250. The proof that you can walk into 10 million users' worth of unfamiliar code and leave it better is worth a lot more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How does the Expensify bug bounty program work?&lt;/strong&gt;&lt;br&gt;
Expensify files bugs as public GitHub issues, and most carry a bounty, typically $250. Anyone can submit a proposal explaining the root cause and the intended fix. If the proposal is approved, you implement it, go through code review like an employee would, and get paid when the fix ships.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much does Expensify pay for a bug fix?&lt;/strong&gt;&lt;br&gt;
Most issues carry a bounty of about $250. The two fixes in this post paid $250 each, $500 in total.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do you need to work at Expensify to fix their bugs?&lt;/strong&gt;&lt;br&gt;
No. Proposals are open to anyone. Both fixes here came from an outside contributor, working as a contractor through Upwork.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you get a proposal accepted on a large open source project?&lt;/strong&gt;&lt;br&gt;
Reproduce the bug reliably first, write the root cause down with links to the exact lines involved, trace the data rather than the screen where the symptom appears, and choose a fix whose side effects you can test and explain.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post first appeared on &lt;a href="https://tusharbhendarkar.com/blog/how-i-debugged-expensify-bugs-and-got-paid" rel="noopener noreferrer"&gt;tusharbhendarkar.com&lt;/a&gt;. I write about the engineering behind what I ship. If something here is broken for you, email me at &lt;a href="mailto:tusharbhendarkar44@gmail.com"&gt;tusharbhendarkar44@gmail.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>reactnative</category>
      <category>debugging</category>
      <category>career</category>
    </item>
    <item>
      <title>AI Chatbot Giving Wrong Answers? Why It Can't Find What's in Your Documents</title>
      <dc:creator>Tushar Bhendarkar</dc:creator>
      <pubDate>Fri, 23 Oct 2026 03:30:03 +0000</pubDate>
      <link>https://dev.to/tusharbhendarkar/ai-chatbot-giving-wrong-answers-why-it-cant-find-whats-in-your-documents-372b</link>
      <guid>https://dev.to/tusharbhendarkar/ai-chatbot-giving-wrong-answers-why-it-cant-find-whats-in-your-documents-372b</guid>
      <description>&lt;p&gt;When an AI chatbot gives wrong answers about your own documents, the AI model is usually not the problem. The model only reads the few passages a search step hands it, and when that search hands it the wrong ones, a perfectly good model answers badly.&lt;/p&gt;

&lt;p&gt;You have probably seen both versions of this. The bot says "I couldn't find that information" when the answer is sitting on page 4 of your PDF. Or it answers confidently with something your documents never said. Same root cause, different symptom.&lt;/p&gt;

&lt;p&gt;I build these pipelines in my day job: pulling text out of documents, splitting it, indexing it, and running sets of test questions that catch wrong answers before users do. When an answer comes out wrong, the cause is usually in one of those earlier steps, not in the model.&lt;/p&gt;

&lt;p&gt;In this post I'll explain why it happens in plain English, the five places it usually breaks, how to check each one, and how to prove a fix worked. It applies to chatbots built on your own documents (often called RAG, short for retrieval-augmented generation) and to ChatGPT custom GPTs with uploaded files.&lt;/p&gt;

&lt;h2&gt;
  
  
  How a document chatbot answers a question
&lt;/h2&gt;

&lt;p&gt;A chatbot that answers from your documents does not read all of them every time someone asks something. That would be slow and expensive. Instead, each document is prepared once, and every question goes through the same short pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Extraction.&lt;/strong&gt; The text is pulled out of the PDF, page by page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chunking.&lt;/strong&gt; The text is cut into short passages of a few paragraphs each.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embeddings.&lt;/strong&gt; Each passage is turned into a list of numbers that captures its meaning, so passages can be compared with questions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search.&lt;/strong&gt; The question is converted the same way, and the closest passages are found.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context.&lt;/strong&gt; The best few passages are pasted into the prompt next to the question.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Answer.&lt;/strong&gt; The model writes a reply from what it was given, ideally citing the document and page.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fphcvxzctcpz7vfw8sqgi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fphcvxzctcpz7vfw8sqgi.png" alt="Diagram of a document chatbot pipeline: extraction, chunking, embeddings, search, context and answer. Stages 1 to 5 decide what the model sees." width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Stages 1 to 5 decide what the model sees. The answer can only use what reaches it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The model at the end can only be as good as the passages it receives. If the right passage never reaches it, it either says it found nothing, which is honest, or fills the gap with something that sounds right, which is worse.&lt;/p&gt;

&lt;h2&gt;
  
  
  A worked example: the refund question
&lt;/h2&gt;

&lt;p&gt;Here is the failure in miniature, using a sample refund policy. The document, scores and results are illustrative, but the pattern is a very common one.&lt;/p&gt;

&lt;p&gt;A customer asks: "When can I request a refund?" The rules are on page 4. The chatbot replies: "I couldn't find that information in your documents."&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 1: the chunk with the answer never says "refund"
&lt;/h3&gt;

&lt;p&gt;The heading "5. Refunds" sits at the very bottom of page 3, and the rules start at the top of page 4. The document was split page by page, so the heading landed in one chunk and the rules in another:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8i5tlo684e057jl9932z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8i5tlo684e057jl9932z.png" alt="Two pages of a sample refund policy. The heading 5. Refunds is the last line of page 3 in chunk A; the refund rules start page 4 in chunk B, which never uses the word refund." width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Chunk A has the word the customer typed but no answer. Chunk B has the answer but never says refund.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Chunk B holds the answer, but it talks about "money back" and a "return window". The word the customer typed lives in a different chunk.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 2: search finds the right chunk, then throws it away
&lt;/h3&gt;

&lt;p&gt;Search does find chunk B, but it scores 0.76 for similarity, just under a cut-off of 0.80 that was set to filter out weak matches. Only three results are kept anyway:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1ymnohluewxsx8hfr30k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1ymnohluewxsx8hfr30k.png" alt="Search scores for the question When can I request a refund: shipping 0.83 and cancellations 0.81 pass a 0.80 cut-off, while the real answer on page 4 scores 0.76 and is dropped." width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The real answer scores 0.76 and falls just under the 0.80 cut-off, so the model never sees it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So the model receives a passage about shipping and one about cancellations, neither of which mentions refunds, and correctly says it found nothing. Every part of the system did what it was told. Two settings, chosen once and never tested, did the damage.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five places it breaks, in the order to check them
&lt;/h2&gt;

&lt;p&gt;Do not guess which stage is broken, and do not start by swapping the AI model. For each question that fails, look at what every stage produced. The root cause becomes something you can see, not something you argue about.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Extraction: is the text actually there?
&lt;/h3&gt;

&lt;p&gt;Open the extracted text for the page that holds the answer. Scanned PDFs are pictures of pages, with no text at all unless they go through OCR (text recognition) first. Tables often come out as a jumble of numbers, and two-column layouts can be read straight across both columns, mixing sentences. If the text is wrong here, nothing later can fix it.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Chunking: does each heading stay with its text?
&lt;/h3&gt;

&lt;p&gt;Check the chunk that should contain the answer. Splitting by page or by a fixed number of characters cuts sections in half, exactly as in the refund example. Split along headings instead, keep each heading attached to the text under it, let neighbouring chunks overlap a little, and store the document name and page number with every chunk so answers can cite them.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Embeddings: are documents and questions measured the same way?
&lt;/h3&gt;

&lt;p&gt;Documents and questions must be converted with the same embedding model. If the model was changed, or a PDF was replaced, every chunk has to be converted again. A half-updated index produces search results that look random.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Search settings: is a filter hiding good matches?
&lt;/h3&gt;

&lt;p&gt;Look at how many passages are kept and whether there is a minimum score. Both are usually guessed when a chatbot is built; set them from test questions instead. When customers use different words from your documents ("money back" versus "refund"), meaning-based search alone can miss. Combining it with plain keyword search, known as hybrid search, catches those cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. What reaches the model: is the found text sent, and is "not covered" allowed?
&lt;/h3&gt;

&lt;p&gt;Check the final prompt. Sometimes the right passages are found and then cut to fit a length limit. And if the instructions never say "answer only from these passages, and say so when they do not cover the question", the model fills gaps with confident guesses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it makes up answers instead of saying it doesn't know
&lt;/h2&gt;

&lt;p&gt;A made-up answer is the same failure as "I couldn't find that", with a different ending. When search returns passages that are near the topic but do not contain the answer, the model tries to be helpful and stitches together something plausible.&lt;/p&gt;

&lt;p&gt;Two changes stop most of it: an instruction to answer only from the passages provided, and a citation (document and page) on every answer. A citation makes a wrong answer easy to spot, for you and for your customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prove the fix with the same questions, before and after
&lt;/h2&gt;

&lt;p&gt;Before changing anything, agree on a set of test questions and the right answer for each. Include three kinds: direct questions, reworded ones (how customers actually talk), and questions your documents cannot answer, where the right reply is "that isn't covered". Run them before the fix and after. The fix only counts if this table improves:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdaksxkzxb6fidw6m0x9w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdaksxkzxb6fidw6m0x9w.png" alt="The same questions before and after the fix. Before: the bot cannot find the refund rules and invents a Canada shipping answer. After: it answers with a page citation and says Canada is not covered." width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before: not found, and a made-up answer. After: the right answer with its page, and an honest 'not covered'.&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;When can I request a refund?&lt;/td&gt;
&lt;td&gt;Direct&lt;/td&gt;
&lt;td&gt;Not found&lt;/td&gt;
&lt;td&gt;Correct, p. 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can I get my money back?&lt;/td&gt;
&lt;td&gt;Reworded&lt;/td&gt;
&lt;td&gt;Generic answer&lt;/td&gt;
&lt;td&gt;Correct, p. 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How long do I have to return something?&lt;/td&gt;
&lt;td&gt;Reworded&lt;/td&gt;
&lt;td&gt;Not found&lt;/td&gt;
&lt;td&gt;Correct, p. 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can I cancel before it ships?&lt;/td&gt;
&lt;td&gt;Direct&lt;/td&gt;
&lt;td&gt;Correct&lt;/td&gt;
&lt;td&gt;Correct, p. 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;When will my order arrive?&lt;/td&gt;
&lt;td&gt;Direct&lt;/td&gt;
&lt;td&gt;Correct&lt;/td&gt;
&lt;td&gt;Correct, p. 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What if my item arrives damaged?&lt;/td&gt;
&lt;td&gt;Reworded&lt;/td&gt;
&lt;td&gt;Generic answer&lt;/td&gt;
&lt;td&gt;Correct, p. 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Do you ship to Canada?&lt;/td&gt;
&lt;td&gt;Not in the documents&lt;/td&gt;
&lt;td&gt;Made-up answer&lt;/td&gt;
&lt;td&gt;Says it's not covered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who is your CEO?&lt;/td&gt;
&lt;td&gt;Not in the documents&lt;/td&gt;
&lt;td&gt;Says it's not covered&lt;/td&gt;
&lt;td&gt;Says it's not covered&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In this sample, 3 of 8 answers were right before the fix and 8 of 8 after, each with the page cited. Look at the Canada question: before the fix the bot invented an answer, and after it says the documents do not cover it. For a business, that is the most important row in the table.&lt;/p&gt;

&lt;p&gt;Keep the questions. Every time you add documents or change a setting, run them again.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you use ChatGPT custom GPTs or file uploads
&lt;/h2&gt;

&lt;p&gt;The same mechanics apply. A custom GPT searches its knowledge files for relevant passages instead of reading them end to end, so the same things go wrong:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scanned PDFs may have no readable text. Run OCR first, or upload a text version.&lt;/li&gt;
&lt;li&gt;Long files with vague headings are hard to search. Split them into smaller files with clear, descriptive headings.&lt;/li&gt;
&lt;li&gt;In the instructions, tell it to search the knowledge files before answering, to name the section it used, and to say when the files do not cover a question.&lt;/li&gt;
&lt;li&gt;Test it with the same before-and-after questions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a custom GPT still misses answers after that, it has reached the limits of a tool you cannot tune. That is usually the point where a small chatbot of your own, where you control the chunking, the search settings and the prompt, starts paying for itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The principle: find the stage that breaks, fix only that
&lt;/h2&gt;

&lt;p&gt;Wrong answers from a document chatbot feel like an AI problem, so the instinct is to try a bigger model. Usually the fix is earlier and cheaper: text that was never extracted, a heading cut off from its section, or a search filter set by guesswork. Trace one failing question through every stage, fix the stage that breaks, and prove it with the same questions before and after.&lt;/p&gt;

&lt;p&gt;It is the same rule that got two of my fixes merged into a 10 million user app: &lt;a href="https://tusharbhendarkar.com/blog/how-i-debugged-expensify-bugs-and-got-paid" rel="noopener noreferrer"&gt;trace the data, not the symptom&lt;/a&gt;. More guides like this one are in &lt;a href="https://tusharbhendarkar.com/blog/topics/ai-chatbots" rel="noopener noreferrer"&gt;AI chatbots and AI features&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you would rather hand it over, I trace chatbots exactly this way. Send me the PDFs that cause trouble and a few questions that fail today, and you get the same before-and-after table for your own documents, with a short note on the root cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why does my chatbot say it can't find information that is in my PDF?&lt;/strong&gt;&lt;br&gt;
Because the passage with the answer never reached the AI model. Usually the document was split so the answer sits in a chunk without the words people search for, or a search setting (a minimum score, or too few results kept) dropped it. Check the extracted text and the search results for that question before changing anything else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does my AI chatbot make up answers?&lt;/strong&gt;&lt;br&gt;
When search returns passages that are close to the topic but do not contain the answer, the model fills the gap with something plausible. Tell it to answer only from the passages it is given and to say when they do not cover the question, and require a document and page citation on every answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is my custom GPT not using its knowledge files?&lt;/strong&gt;&lt;br&gt;
Custom GPTs search their knowledge files instead of reading them end to end, so scanned PDFs, very long files and vague headings all make the right passage hard to find. Use text-based PDFs, split long files into smaller ones with clear headings, and tell the GPT in its instructions to search the knowledge files before answering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will a better AI model fix wrong answers?&lt;/strong&gt;&lt;br&gt;
Rarely. A stronger model still only sees the passages it is given. If the problem is extraction, chunking or search settings, a new model gives the same wrong answers in nicer words.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can this be fixed without rebuilding the chatbot?&lt;/strong&gt;&lt;br&gt;
Usually, yes. Most fixes are a change to how documents are split, a re-index, adjusted search settings and a clearer prompt. A fixed set of test questions tells you when it is done.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post first appeared on &lt;a href="https://tusharbhendarkar.com/blog/ai-chatbot-giving-wrong-answers" rel="noopener noreferrer"&gt;tusharbhendarkar.com&lt;/a&gt;. I build and fix AI chatbots and RAG systems for small businesses. If something here is broken for you, email me at &lt;a href="mailto:tusharbhendarkar44@gmail.com"&gt;tusharbhendarkar44@gmail.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>llm</category>
      <category>chatbot</category>
    </item>
    <item>
      <title>How to Transfer GitHub Repo Ownership Without Breaking Your Site</title>
      <dc:creator>Tushar Bhendarkar</dc:creator>
      <pubDate>Thu, 22 Oct 2026 03:30:03 +0000</pubDate>
      <link>https://dev.to/tusharbhendarkar/how-to-transfer-github-repo-ownership-without-breaking-your-site-583f</link>
      <guid>https://dev.to/tusharbhendarkar/how-to-transfer-github-repo-ownership-without-breaking-your-site-583f</guid>
      <description>&lt;p&gt;Transferring a GitHub repository takes about two clicks, and those clicks are rarely the problem. I went through 80 public reports of repository transfers going wrong, from Vercel's and Netlify's forums, GitHub, Railway, Reddit and Stack Overflow. The transfer itself failed in only 12 of them. The rest broke afterwards: hosting that quietly stopped deploying (22), a GitHub Pages site or custom domain that went down (15), and people locked out of accounts the old developer still controlled (12).&lt;/p&gt;

&lt;p&gt;Every guide ranking for this search shows you GitHub's Transfer button and stops there. This one covers the button, and then everything connected to the repository that the button doesn't move.&lt;/p&gt;

&lt;p&gt;It's also how I think about code in general. At The AI Lab, the code I work on, including the Trust Identity Protocol, lives in a GitHub organization, not in anyone's personal account. People join and leave; the code stays with the business. If your app's code sits in a developer's or agency's account, this is how to bring it home.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually breaks when you transfer a repository
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0fv1mj6svy690pyt64is.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0fv1mj6svy690pyt64is.png" alt="Bar chart of 80 GitHub repository transfers that went wrong: hosting stopped deploying 22, GitHub Pages or custom domain broke 15, lost access or wrong account 12, transfer blocked or failed 10, CI or secrets broke 9, developer would not transfer 3, name already taken 2, other 7." width="800" height="368"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The transfer itself failed in only 12 of 80 reports. Most broke afterwards, in what was connected to the repository.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Two things stood out. Hosting providers sometimes keep showing "Connected" while pointing at a repository that no longer exists at that address, so nothing looks wrong until you push a change and the live site doesn't update. And for small businesses, the code was rarely the hostage. Eleven reports were custom domains still sitting in an old Vercel or Netlify account, ten of them a former developer's or agency's. Only three were a developer refusing to hand the code over.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to transfer GitHub repo ownership, step by step
&lt;/h2&gt;

&lt;p&gt;Before you start, check three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The person transferring has admin access&lt;/strong&gt; to the repository. Collaborators with write access can't do it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The new owner has no repository with the same name&lt;/strong&gt;, or a fork in the same network. If it does, rename one first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For an organization, you're allowed to create repositories there&lt;/strong&gt;, and the source organization allows transfers out. Owners can restrict both.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Open the repository on GitHub and click Settings.&lt;/li&gt;
&lt;li&gt;Scroll to the Danger Zone and click Transfer.&lt;/li&gt;
&lt;li&gt;Choose an organization, or type the username of the new owner.&lt;/li&gt;
&lt;li&gt;Type the repository name to confirm, then click "I understand, transfer this repository".&lt;/li&gt;
&lt;li&gt;If the new owner is a personal account, they get an email and must accept it within one day, or the invitation expires.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;GitHub's &lt;a href="https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository" rel="noopener noreferrer"&gt;transfer docs&lt;/a&gt; say old web links and git addresses redirect to the new location, so nothing breaks immediately. The redirect disappears if anyone later creates a repository with the old name, so update your local copy anyway with git remote set-url origin and the new address. The original owner is added as a collaborator afterwards; remove them when you're ready.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Moves with the repository&lt;/th&gt;
&lt;th&gt;Stays behind or needs redoing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Commit history, issues, pull requests, wiki, stars&lt;/td&gt;
&lt;td&gt;Hosting links (Vercel, Netlify, Cloudflare and others)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repository secrets, webhooks and deploy keys&lt;/td&gt;
&lt;td&gt;Organization-level secrets, variables and runners&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large files stored with Git LFS&lt;/td&gt;
&lt;td&gt;Container images and project boards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redirects from the old address&lt;/td&gt;
&lt;td&gt;GitHub Pages address (not redirected)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One more trap: a private repository moved to a GitHub Free account loses its protected branches, wiki and GitHub Pages, because those are paid features for private repositories. If the site is published with GitHub Pages, it goes offline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Relink your hosting, or your site stops updating
&lt;/h2&gt;

&lt;p&gt;This was the biggest group in the reports. Vercel, Netlify, Cloudflare, Render, Railway and AWS Amplify all connect to GitHub through an app installed on one GitHub account. After a transfer, that app has to be installed on the new owner, and the hosting project pointed at the repository's new address:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Host&lt;/th&gt;
&lt;th&gt;What to do after the transfer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Vercel&lt;/td&gt;
&lt;td&gt;Settings, Git: disconnect, then connect the repository at its new address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Netlify&lt;/td&gt;
&lt;td&gt;Continuous deployment, Repository, Manage repository: link the repository again&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare Pages&lt;/td&gt;
&lt;td&gt;Cannot relink: transfer it back, or create a new Pages project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare Workers&lt;/td&gt;
&lt;td&gt;Disconnect builds, then connect the repository again&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Render, Railway, Amplify&lt;/td&gt;
&lt;td&gt;Install their GitHub app on the new owner, then reconnect&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Vercel has two traps worth knowing. On the free Hobby plan, private repositories owned by a GitHub organization can't deploy at all, so moving a client's private repository into their new organization stops their deploys until they upgrade or keep it in a personal account. And Hobby only deploys commits authored by the owner of the Vercel account, according to &lt;a href="https://vercel.com/docs/git" rel="noopener noreferrer"&gt;Vercel's Git docs&lt;/a&gt;. Netlify has its own: the repository link belongs to the person who created it, so when the departing developer loses access, builds can stop, and the fix is to relink the same repository from your side.&lt;/p&gt;

&lt;p&gt;Moving the hosting project itself is a separate step from moving the code. On Vercel, a project transfer carries its deployments, domains and environment variables, but not integrations such as a database, and it needs a team that both people belong to. On Netlify, if you and the developer don't share a team, the site transfer goes through support.&lt;/p&gt;

&lt;p&gt;However it's hosted, I'd do it in this order: transfer the repository, reconnect the hosting, deploy once from the new repository, and compare the live site page by page with what visitors saw before. Only then remove the old access.&lt;/p&gt;

&lt;h2&gt;
  
  
  The domain is the part that gets stuck
&lt;/h2&gt;

&lt;p&gt;In the reports, the domain was the most common thing still sitting in a former developer's account. Code can be transferred in two clicks; a domain registered or connected in someone else's account needs their cooperation, so sort it out while you're still on good terms.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Registrar:&lt;/strong&gt; the domain should be registered in an account in your name, with your email on it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vercel:&lt;/strong&gt; if your project only uses a subdomain, the root domain stays with the original Vercel team, which keeps paying for it if it was bought there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Pages:&lt;/strong&gt; if the developer verified your domain on their account, only their repositories can publish to it. Verify it on your own account, which also releases it if GitHub says the domain is already in use. A domain left pointing at GitHub Pages after the site is removed can be taken over by someone else, which is exactly the state a transfer to a free account can leave you in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same goes for every account around the code: hosting, email sending, payments, analytics and the database. Twelve reports were people locked out of one of these after the developer left. Make a list, and move each one into your name before you say goodbye.&lt;/p&gt;

&lt;h2&gt;
  
  
  For developers: what else changes after a transfer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Secrets you can't read back.&lt;/strong&gt; Organization-level Actions secrets and variables stay with the old organization, and GitHub never shows secret values again, so collect the real values from wherever the developer keeps them before the move.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud deploys through OIDC.&lt;/strong&gt; Workflows that log in to AWS, Azure or Google Cloud are trusted by a subject that names the owner and repository. Since July 15, 2026, a repository transferred or renamed switches to a new subject format that includes IDs, so update the trust policies on the cloud side, per GitHub's &lt;a href="https://docs.github.com/en/actions/reference/security/oidc" rel="noopener noreferrer"&gt;OIDC reference&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A fresh organization is locked down.&lt;/strong&gt; New organizations block OAuth-based tools until an owner approves them, and don't allow new deploy keys by default. The receiving organization's Actions policy applies immediately too, and it can block common actions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Things that point at the old owner.&lt;/strong&gt; Reusable workflows or actions in the agency's private repositories, CODEOWNERS teams, container images on ghcr.io, and submodule URLs. Package publishing breaks too: npm and PyPI trusted publishing check the owner and repository name.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When you cannot get a transfer
&lt;/h2&gt;

&lt;p&gt;If the developer won't transfer the repository but will give you read access, you can still get a full copy with its history. GitHub documents it as mirroring: create an empty repository in your account, clone the old one with git clone --bare, then push it with git push --mirror to yours. The copy has every commit and branch; it just isn't linked to the original.&lt;/p&gt;

&lt;p&gt;If they won't give you any access, who owns the code comes down to your contract, and that's a question for a lawyer, not for me. The cheaper fix is prevention: create the GitHub organization yourself at the start, give it at least two owners as GitHub recommends, and invite developers to it. The same goes for apps built with AI tools; I covered that version in &lt;a href="https://tusharbhendarkar.com/blog/export-lovable-project-to-github" rel="noopener noreferrer"&gt;how to export a Lovable project to GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The order that keeps your site running
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create your GitHub organization or account, and accounts for hosting and the domain.&lt;/li&gt;
&lt;li&gt;Collect secret values and a list of every connected service while the developer is still around.&lt;/li&gt;
&lt;li&gt;Transfer the repository, and accept it within one day.&lt;/li&gt;
&lt;li&gt;Install the hosting app on the new owner and reconnect the project.&lt;/li&gt;
&lt;li&gt;Move the domain and the other accounts into your name.&lt;/li&gt;
&lt;li&gt;Deploy once from the new repository and check the live site page by page.&lt;/li&gt;
&lt;li&gt;Only then remove the old developer's access.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;More guides on taking control of an app are in &lt;a href="https://tusharbhendarkar.com/blog/topics/fix-ai-built-app" rel="noopener noreferrer"&gt;Fix an AI-built app&lt;/a&gt;. And if you'd rather hand it over, I take these on as client projects: send me where things live today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How do I transfer ownership of a GitHub repository?&lt;/strong&gt;&lt;br&gt;
You need admin access to the repository. Open its Settings, go to Danger Zone, click Transfer, choose an organization or type a username, then type the repository name to confirm. A transfer to a personal account sends the new owner an email that expires if it is not accepted within one day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do commit history and issues move when I transfer a repo?&lt;/strong&gt;&lt;br&gt;
Yes. Commit history, issues, pull requests, the wiki, stars and watchers move with the repository, and so do its webhooks, repository secrets and deploy keys. Organization-level secrets, runners and project boards stay with the old owner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do old GitHub links still work after a transfer?&lt;/strong&gt;&lt;br&gt;
Yes. Web links and git clone, fetch and push to the old address redirect to the new one, though GitHub still recommends updating your remote with git remote set-url. The redirect stops working if someone later creates a repository with the old name, and GitHub Pages addresses are not redirected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will my Vercel or Netlify site keep deploying after I transfer the repo?&lt;/strong&gt;&lt;br&gt;
Not reliably. Both deploy through a GitHub app installed on one account, so install it on the new owner and reconnect the project to the repository at its new address. On Vercel's free Hobby plan, private repositories owned by an organization cannot deploy at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I transfer a GitHub repo to an organization?&lt;/strong&gt;&lt;br&gt;
Yes, if you are allowed to create repositories in that organization. Transfers out of an organization also need permission there, and organization owners can restrict transfers to owners only. Once it arrives, the organization's own rules, Actions policy and default permissions apply.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if my developer will not transfer the repository?&lt;/strong&gt;&lt;br&gt;
If they will give you read access, you can make a full copy with its history in your own account by cloning it with --bare and pushing it with --mirror. If they will not give any access, it comes down to your contract, which is a question for a lawyer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do GitHub Actions secrets transfer with the repository?&lt;/strong&gt;&lt;br&gt;
Repository-level secrets do. Organization-level secrets and variables belong to the old organization and stay there, and secret values can never be read back from GitHub, so get the real values from your developer before the move if you need to recreate them.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post first appeared on &lt;a href="https://tusharbhendarkar.com/blog/transfer-github-repo-ownership" rel="noopener noreferrer"&gt;tusharbhendarkar.com&lt;/a&gt;. I fix apps built with Lovable, Replit, Bolt and Base44, and I build &lt;a href="https://tusharbhendarkar.com/tools" rel="noopener noreferrer"&gt;free checkers&lt;/a&gt; for live apps. If something here is broken for you, email me at &lt;a href="mailto:tusharbhendarkar44@gmail.com"&gt;tusharbhendarkar44@gmail.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>git</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Export Lovable Project to GitHub and Really Own the Code</title>
      <dc:creator>Tushar Bhendarkar</dc:creator>
      <pubDate>Wed, 21 Oct 2026 03:36:23 +0000</pubDate>
      <link>https://dev.to/tusharbhendarkar/how-to-export-lovable-project-to-github-and-really-own-the-code-52ji</link>
      <guid>https://dev.to/tusharbhendarkar/how-to-export-lovable-project-to-github-and-really-own-the-code-52ji</guid>
      <description>&lt;p&gt;Several of the top guides on exporting a Lovable project to GitHub are out of date. They point to a GitHub button Lovable removed in May 2026, say you need a paid plan (sync works on every plan), and warn you never to rename the repository, a rule Lovable dropped this year. Follow them and you lose an afternoon looking for a button that isn't there.&lt;/p&gt;

&lt;p&gt;The bigger problem is the one none of them cover. Exporting is easy. Owning the code is not the same thing, and the hard part comes when an app was built by a freelancer or an agency and the repository sits in their account. I sorted 136 public reports of Lovable and GitHub going wrong, and nine were exactly that: a handover stuck because the developer held the repository or the project.&lt;/p&gt;

&lt;p&gt;This is the setup I'd use: how to export, what you actually get, the rules that keep the sync working, and the order for moving an app out of a developer's accounts and into yours without breaking anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What goes wrong between Lovable and GitHub
&lt;/h2&gt;

&lt;p&gt;The reports come from Reddit, YouTube, Hacker News, Trustpilot and GitHub, from 2025 to October 2026. I put each one in a group by the cause the thread confirmed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6x8omom9spa6vavbyq7i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6x8omom9spa6vavbyq7i.png" alt="Bar chart of 136 reports of Lovable and GitHub problems by cause: sync stopped or out of step 20, cannot connect an existing repo 13, branch rules 13, repo renamed moved or deleted 12, incidents 11, project moved to another account 10, build fails outside Lovable 10, wrong account 9, developer or agency owns the repo 9." width="800" height="496"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Most problems are about the sync and the account the repository lives in, not the code itself.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One caution on the chart: the reports about renaming or moving the repository all date from before Lovable changed its rules in 2026. Today renaming is safe, and moving is safe if you do it in the right order, which is most of what this post is about.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to export a Lovable project to GitHub, step by step
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Open Project settings, Git, GitHub.&lt;/strong&gt; The GitHub button in the editor's top bar moved there on May 18, 2026. You can also reach it from the + button next to the chat, then Project, GitHub.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add a connection.&lt;/strong&gt; A GitHub popup asks you to pick your personal account or an organization, then All repositories or Only select repositories, then Install &amp;amp; Authorize. If nothing happens, your browser blocked the popup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connect the project.&lt;/strong&gt; Lovable creates a brand-new private repository under the account you picked and starts two-way sync straight away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check it.&lt;/strong&gt; The project settings should show Connected, a sync status card, a branch picker and links to clone or view the repository on GitHub.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Two permission traps come up often. Only a Lovable workspace owner or admin can add the GitHub connection. And in a GitHub organization, only an organization owner can install Lovable's app, because it asks for administration rights to create repositories; anyone else can only send a request. Lovable's &lt;a href="https://docs.lovable.dev/integrations/github" rel="noopener noreferrer"&gt;GitHub docs&lt;/a&gt; cover both.&lt;/p&gt;

&lt;p&gt;Sync works on every plan, with GitHub, GitLab or Bitbucket. Downloading the whole codebase as a zip is for paid plans, and Lovable recommends sync over zip snapshots anyway. One thing you can't do is connect a repository you already have: Lovable always creates a new one, so if you have existing code, let it create the repository and copy your code in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is in the repository, and what is not
&lt;/h2&gt;

&lt;p&gt;This is where "I exported it, so I own it" usually goes wrong. According to Lovable's &lt;a href="https://docs.lovable.dev/integrations/git-sync-overview" rel="noopener noreferrer"&gt;sync overview&lt;/a&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;In the repository&lt;/th&gt;
&lt;th&gt;Not in the repository&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Your app code&lt;/td&gt;
&lt;td&gt;Your database records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edge functions and database migration files&lt;/td&gt;
&lt;td&gt;Uploaded files, and since May 2026 large images, which live on Lovable's CDN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A .env file pointing at the live backend&lt;/td&gt;
&lt;td&gt;Secrets, and the credentials behind connectors and Lovable AI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two of those surprise people. The .env file means a copy of the code on your laptop reads and writes the same live database as your published app; a Git branch doesn't give you a separate database. And because large images now sit on Lovable's CDN, a downloaded copy has small pointer files where the images were, so they fail to load outside Lovable. If you plan to host it elsewhere, I covered what breaks in &lt;a href="https://tusharbhendarkar.com/blog/lovable-app-not-working" rel="noopener noreferrer"&gt;Lovable app not working&lt;/a&gt;, and how the backend works in &lt;a href="https://tusharbhendarkar.com/blog/lovable-supabase-integration" rel="noopener noreferrer"&gt;Lovable Supabase integration&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rules that keep the two-way sync working
&lt;/h2&gt;

&lt;p&gt;Every edit in Lovable is committed and pushed to the repository, and commits pushed to the synced branch show up in Lovable. Pushing never changes your live site, though; only Publish in Lovable does. The rest is a handful of rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't rewrite history.&lt;/strong&gt; No force-pushing, rebasing or squashing commits that are already on the synced branch; undo with a revert commit. Since October 5, 2026, if something does replace Lovable's commits, Lovable keeps a backup that only the project owner can restore.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protected branches reject Lovable.&lt;/strong&gt; If GitHub refuses a push, Lovable pushes to a branch called lovable-sync and asks you to merge it. If your team requires pull requests on main, point Lovable at its own branch instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code you push isn't deployed.&lt;/strong&gt; Lovable doesn't deploy a changed edge function or run a new migration that arrives through Git; ask Lovable in chat to do it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Big files fail.&lt;/strong&gt; GitHub rejects files over 100 MB, and Lovable itself can't save files over 10 MB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Renaming is fine now.&lt;/strong&gt; Renaming the repository, your username or the organization keeps the sync running. Older guides say otherwise because that was the rule until mid-2026.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If commits you pushed don't appear in Lovable, GitHub probably never told Lovable about the push. Lovable's official fix is to push any new commit, even an empty one. What I wouldn't do is disconnect and reconnect: reconnecting always creates a new repository, and in the reports it often made things worse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is the code really yours? Check whose account it is in
&lt;/h2&gt;

&lt;p&gt;Lovable's FAQ says you own the projects and code you create. Its terms put that as between Lovable and the account holder, which is the account that built the project. If a freelancer or an agency built it in their account, who owns it between the two of you comes down to your contract. That's a question for a lawyer, not for me. What I can tell you is how to see where things stand.&lt;/p&gt;

&lt;p&gt;The first part of a GitHub address is the owner: in github.com/someone/your-app, someone owns it. In the repository's Settings, Collaborators and teams lists everyone with access. Two details from GitHub's docs matter here. A personal account's repository has exactly one owner, and every collaborator gets write access, never admin. And if the repository sits in an organization, every owner of that organization can always see it. So if it lives in your agency's organization, being given access isn't the same as owning it, however friendly everyone is.&lt;/p&gt;

&lt;p&gt;The apps I build live in GitHub repositories, and on any codebase the first thing I check is whose account it's in. If you're setting up your own, use a GitHub organization rather than a personal account: it can have several owners, and GitHub recommends at least two.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to move it from your developer's account into yours
&lt;/h2&gt;

&lt;p&gt;The order matters, because two of Lovable's rules interact. Transferring a repository keeps the sync only if the Lovable workspace already has a GitHub connection for the new owner. And moving a Lovable project to another workspace breaks its GitHub link, so reconnecting there creates a new repository with fresh history. This is the order I'd use:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpdwz6yxlrnd0qsshye5k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpdwz6yxlrnd0qsshye5k.png" alt="Six steps to move a Lovable app into your own accounts: create your own GitHub and Lovable accounts, add your GitHub connection in the workspace that holds the project, transfer the repository, take over the Lovable project, move the domain, Supabase and Stripe accounts, then check the live site and remove the developer's access." width="800" height="560"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Set up your side first, transfer the repository second, and close old access only after the live site checks out.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Transferring the repository
&lt;/h3&gt;

&lt;p&gt;On GitHub the developer opens the repository's Settings, Danger Zone, Transfer, and types your username or organization. A transfer to a personal account sends you an email that expires if you don't accept within one day. Issues, pull requests, history, webhooks, secrets and deploy keys move with it, and old links and git addresses redirect, according to GitHub's &lt;a href="https://docs.github.com/en/repositories/creating-and-managing-repositories/transferring-a-repository" rel="noopener noreferrer"&gt;transfer docs&lt;/a&gt;. Afterwards the original owner is added as a collaborator, so remove them when you're ready. If a transfer isn't possible, the developer can give you read access and you can mirror the repository into your account with its full history.&lt;/p&gt;

&lt;h3&gt;
  
  
  Taking over the Lovable project
&lt;/h3&gt;

&lt;p&gt;If the developer built your app in a workspace of its own, this is the easy case: since September 7, 2026, the workspace's main owner role, which covers billing and domains, can be handed to any Lovable account by email, on every plan. Nothing moves, so nothing breaks. If your project sits in the developer's shared workspace, move it to yours with Move workspace. Its GitHub link breaks, so reconnect it in your workspace and keep the repository you were transferred as the history. A Cloud backend travels with the project, and your workspace pays for it from then on.&lt;/p&gt;

&lt;p&gt;Avoid remixing as a handover. A remix is a separate copy that leaves behind the GitHub sync, secrets, database data, custom domain and collaborators.&lt;/p&gt;

&lt;h3&gt;
  
  
  Everything that isn't code
&lt;/h3&gt;

&lt;p&gt;The domain, your own Supabase project if you use one, Stripe, and the email service each need to be in your name too. Then publish, check the live site page by page, and only after that remove the developer from the repository and the workspace. Exporting the code doesn't cancel anything either: Lovable says the subscription and any Cloud usage carry on until you change them.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the sync breaks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Install the GitHub App for  to enable gitsync".&lt;/strong&gt; Usually after a workspace transfer or a removed connection. Reconnect and install the app on the owner shown.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every project on a connection disconnected at once.&lt;/strong&gt; Someone uninstalled the Lovable GitHub app. Reinstalling works, but each project reconnects to a new repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lovable lost access to one repository.&lt;/strong&gt; Someone switched the app to Only select repositories and left yours out. Reconnect and add it back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The repository was deleted.&lt;/strong&gt; Restore it on GitHub, which is possible for 90 days, and the sync resumes. Otherwise reconnect to get a new repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need a different GitHub account on the project.&lt;/strong&gt; You can't switch it in place; disconnect and reconnect to the other account, which creates a new repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Own the accounts, not just a copy
&lt;/h2&gt;

&lt;p&gt;A zip file or a repository in someone else's account is a copy of your app. Ownership is the repository, the Lovable project, the backend and the domain all sitting in accounts you control, with at least two people able to get in. Export early, check whose name is on each one, and move them in order. More guides like this are in &lt;a href="https://tusharbhendarkar.com/blog/topics/fix-ai-built-app" rel="noopener noreferrer"&gt;Fix an AI-built app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And if you'd rather hand it over, I take these on as client projects: send me what you have and where each piece lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How do I export my Lovable project to GitHub?&lt;/strong&gt;&lt;br&gt;
Open Project settings, Git, GitHub, then Add connection. Pick your personal account or organization in the GitHub popup and click Install &amp;amp; Authorize. Lovable creates a new private repository there and starts two-way sync. The old GitHub button in the editor top bar was removed in May 2026.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is Lovable's GitHub sync free?&lt;/strong&gt;&lt;br&gt;
Yes. Two-way sync with GitHub, GitLab and Bitbucket works on every Lovable plan, including Free. Downloading the whole codebase as a zip file needs a paid plan, and the Free plan code editor is read-only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I connect an existing GitHub repository to Lovable?&lt;/strong&gt;&lt;br&gt;
No. Connecting always makes Lovable create a brand-new repository, and reconnecting later creates another one. The workaround is to let Lovable create its repository, then copy your existing code into it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I rename or move the repository after connecting?&lt;/strong&gt;&lt;br&gt;
Renaming the repository, your GitHub username or the organization now keeps the sync running. Transferring it to another owner keeps the sync only if your Lovable workspace also has a GitHub connection for that new owner; otherwise Lovable disconnects the project. Many older guides still say never rename it, which was the rule until mid-2026.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does the GitHub export include my database?&lt;/strong&gt;&lt;br&gt;
No. The repository holds your code, edge functions and database migration files, plus a .env file pointing at the live backend. It does not hold your data, uploaded files, secrets or the credentials behind Lovable AI, and since May 2026 large images live on Lovable's CDN instead of in the repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I get my Lovable project from my developer?&lt;/strong&gt;&lt;br&gt;
Get your own GitHub account and Lovable access first, add a GitHub connection for your account in the workspace that holds the project, then have the developer transfer the repository to you and hand over the project. Move the domain, backend and payment accounts too, and remove the developer's access last.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why did my Lovable GitHub sync stop?&lt;/strong&gt;&lt;br&gt;
Usually GitHub never told Lovable about a push; the official fix is to push any new commit, even an empty one. Other causes are a protected branch, a removed or suspended GitHub app, or a repository moved to an owner the workspace is not connected to. Avoid disconnecting and reconnecting, which creates a new repository.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post first appeared on &lt;a href="https://tusharbhendarkar.com/blog/export-lovable-project-to-github" rel="noopener noreferrer"&gt;tusharbhendarkar.com&lt;/a&gt;. I fix apps built with Lovable, Replit, Bolt and Base44, and I build &lt;a href="https://tusharbhendarkar.com/tools" rel="noopener noreferrer"&gt;free checkers&lt;/a&gt; for live apps. If something here is broken for you, email me at &lt;a href="mailto:tusharbhendarkar44@gmail.com"&gt;tusharbhendarkar44@gmail.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>lovable</category>
      <category>github</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
    <item>
      <title>Lovable Stripe Integration: Built-in Payments or Your Own Stripe, and Why Payments Break</title>
      <dc:creator>Tushar Bhendarkar</dc:creator>
      <pubDate>Tue, 20 Oct 2026 03:30:06 +0000</pubDate>
      <link>https://dev.to/tusharbhendarkar/lovable-stripe-integration-built-in-payments-or-your-own-stripe-and-why-payments-break-3hhb</link>
      <guid>https://dev.to/tusharbhendarkar/lovable-stripe-integration-built-in-payments-or-your-own-stripe-and-why-payments-break-3hhb</guid>
      <description>&lt;p&gt;The worst Lovable payment bug is the one where the payment works. The customer is charged, Stripe shows the money, and the app never unlocks what they paid for. I went through 52 public reports of payments going wrong in Lovable apps, all from Reddit, and 14 of them were exactly that: a subscription status that never updated, a webhook that was never set up, or a signature check that rejected Stripe's message.&lt;/p&gt;

&lt;p&gt;Getting a checkout button on the page is the easy part. Both apps I built this year, Launch Check and &lt;a href="https://tusharbhendarkar.com/tools/client-approvals" rel="noopener noreferrer"&gt;Client Approvals&lt;/a&gt;, take subscriptions through Stripe, and the work is in everything after the button: test and live mode, webhooks, and the moment the database is told someone has paid.&lt;/p&gt;

&lt;p&gt;This guide covers both ways Lovable connects to Stripe, how to take each one live, and the places a successful payment gets lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  What goes wrong with payments in Lovable apps
&lt;/h2&gt;

&lt;p&gt;I sorted the 52 reports by the cause each thread confirmed. Fourteen were payments that succeeded without the app finding out. Four were test and live mode mixed up, four were checkout redirects and success pages, and three were people burning credits asking the AI to fix payments. The largest group, 25, was a mix, but nine of those were founders who couldn't use Stripe at all because of their country or type of business. I'll come back to that one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two ways to add Stripe to a Lovable app
&lt;/h2&gt;

&lt;p&gt;Lovable's &lt;a href="https://docs.lovable.dev/features/payments" rel="noopener noreferrer"&gt;payments docs&lt;/a&gt; describe two separate paths. Built-in payments, launched in April 2026, have Lovable create and manage the payment account for you, on Stripe or Paddle. The older path connects your own Stripe account with a key.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Built-in payments&lt;/th&gt;
&lt;th&gt;Your own Stripe&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lovable plan&lt;/td&gt;
&lt;td&gt;Pro or higher&lt;/td&gt;
&lt;td&gt;Any plan, including Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who sets up the account&lt;/td&gt;
&lt;td&gt;Lovable, on Stripe or Paddle&lt;/td&gt;
&lt;td&gt;You, in your Stripe account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fees&lt;/td&gt;
&lt;td&gt;Stripe rates, or Paddle 5% + 50 cents; nothing added by Lovable&lt;/td&gt;
&lt;td&gt;Stripe rates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Preview&lt;/td&gt;
&lt;td&gt;Test mode with test cards&lt;/td&gt;
&lt;td&gt;Whatever key you paste; a live key means real payments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webhooks&lt;/td&gt;
&lt;td&gt;Set up by Lovable&lt;/td&gt;
&lt;td&gt;None by default; you add them yourself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tax and verification&lt;/td&gt;
&lt;td&gt;Paddle handles sales tax as merchant of record&lt;/td&gt;
&lt;td&gt;You, in Stripe&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;My take: if this is your first paid product, use built-in payments, because the webhook work is the part people get wrong and Lovable does it for you. If you already run a business on Stripe, or you're on the Free plan, connect your own account and treat the webhooks as your job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built-in payments: setting up and going live
&lt;/h2&gt;

&lt;p&gt;Only project or workspace admins and owners can turn built-in payments on, and Lovable recommends adding login first so purchases link to users. In the preview everything runs in test mode with test cards; the published app runs live, and live checkout doesn't work until the go-live steps are done. Products and prices copy from test to live when you publish, but discount codes don't, so create those in live separately. Don't edit products directly in the Stripe or Paddle dashboard either, which can leave test and live IDs out of step.&lt;/p&gt;

&lt;p&gt;Going live on Stripe means claiming the Stripe account Lovable created for you, finishing Stripe's onboarding, then copying your products and the app to live. On Paddle it's a readiness check, identity and business verification, and a review of your domain, which usually takes about a day. The readiness check most often fails on missing privacy, terms or refund pages, or a site that isn't published. Lovable also suggests connecting a custom domain first, because the provider reviews the live address.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your own Stripe account: setting up and going live
&lt;/h2&gt;

&lt;p&gt;Paste a restricted key (rk_) or secret key (sk_) into the Connect Stripe form, never into the chat. Publishable keys (pk_) don't work. Lovable stores it as a backend secret called STRIPE_SECRET_KEY, and the payment code runs in edge functions. Stripe's &lt;a href="https://docs.stripe.com/keys" rel="noopener noreferrer"&gt;key docs&lt;/a&gt; recommend restricted keys, which carry only the permissions you grant.&lt;/p&gt;

&lt;p&gt;The trap is that this path has no separate test environment: the key you paste decides the mode. If you paste a live key, payments you make while testing in the preview are real. Use a test key until you're ready. To go live, Lovable's &lt;a href="https://docs.lovable.dev/integrations/stripe" rel="noopener noreferrer"&gt;Stripe docs&lt;/a&gt; list three steps: submit a live key, switch the app to live price IDs (they're different from test ones), and make one real purchase on the published app.&lt;/p&gt;

&lt;p&gt;Two more things to know. By default this path uses no webhooks: the app asks Stripe directly whether a signed-in user has an active subscription, and matches subscriptions to users by email address. And the customer portal, where people manage their subscription, has to be switched on once in the Stripe dashboard before it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the payment works but the app does not know
&lt;/h2&gt;

&lt;p&gt;Here is the path a payment takes after the customer clicks pay, and where it breaks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa8qqw9wau541fojeaiqn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa8qqw9wau541fojeaiqn.png" alt="Five steps of a Stripe payment in a Lovable app: checkout, paid, webhook, verify and unlock, with the failure points under each: test price or key in live mode, webhook endpoint deleted or duplicated or rejected for not being signed in, wrong signing secret or changed body, and upgrade or renewal events not handled." width="800" height="470"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Anything that breaks after step 2 means the customer paid and got nothing.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The endpoint is gone or doubled.&lt;/strong&gt; Built-in payments register two webhook endpoints per environment. Lovable warns that deleting the app's endpoint silently breaks unlocking, and that a duplicate one you added by hand delivers events the app can't verify.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The wrong signing secret.&lt;/strong&gt; Every webhook endpoint has its own secret, and test and live secrets differ even for the same address, according to Stripe's &lt;a href="https://docs.stripe.com/webhooks" rel="noopener noreferrer"&gt;webhook docs&lt;/a&gt;. Stripe says the wrong secret is the most common reason signature checks fail; the one from the Stripe command line tool differs from the dashboard's too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The body was changed.&lt;/strong&gt; The signature is checked against the raw request. Anything that parses or re-formats the body first breaks the check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The function expects a signed-in user.&lt;/strong&gt; Stripe can't sign in to your app, so a webhook function that requires a logged-in user rejects it. In some threads that was the whole bug. The function has to be public and rely on the signature instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upgrades and renewals aren't handled.&lt;/strong&gt; customer.subscription.updated fires on renewals and plan changes, so handling only the first purchase misses them. Stripe also doesn't promise events arrive in order, and can send the same one twice, so record the event IDs you've already processed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My rule is simple: the success page says thank you, and only a verified webhook unlocks anything. Stripe's own &lt;a href="https://docs.stripe.com/checkout/fulfillment" rel="noopener noreferrer"&gt;fulfilment guide&lt;/a&gt; says not to rely on the success page alone, and requires webhooks for subscriptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test mode and live mode are two separate worlds
&lt;/h2&gt;

&lt;p&gt;In Stripe, test and live objects are completely separate: a product or price created in test mode can't be used for a live payment, and test cards like 4242 4242 4242 4242 only work in test mode. Copying a test product to live creates a new product with new IDs, which is why an app still holding test price IDs fails the moment it goes live. The customer portal has separate settings for each mode too, so switching it on in test doesn't switch it on in live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you take your first real payment
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Publish the app, and connect a custom domain if you can.&lt;/li&gt;
&lt;li&gt;Add privacy, terms and refund pages.&lt;/li&gt;
&lt;li&gt;Check you're on live keys and live price IDs, with the live webhook secret if you use one.&lt;/li&gt;
&lt;li&gt;Switch on the customer portal in live mode.&lt;/li&gt;
&lt;li&gt;Buy your own product with a real card on the live site, and check it unlocks.&lt;/li&gt;
&lt;li&gt;Upgrade, cancel and refund that test purchase, and check the app follows each change.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  When Stripe is not an option
&lt;/h2&gt;

&lt;p&gt;Nine of the reports were founders who couldn't use Stripe at all: their country isn't supported, Stripe wanted a registered company, or their kind of business wasn't accepted. Built-in payments can run on Paddle instead, which acts as the merchant of record: it sells to your customer on your behalf and handles VAT and sales tax, for 5% plus 50 cents per transaction (a flat 10% under $10). Stripe has its own merchant of record option, Managed Payments, in fewer countries. PayPal, Razorpay and other providers aren't built in and need custom code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disconnecting does not stop billing
&lt;/h2&gt;

&lt;p&gt;One last warning from Lovable's docs. Disconnecting built-in payments is permanent and doesn't cancel subscriptions or refund anyone, so your customers keep being billed. If your app uses its own Supabase project, disconnecting your own Stripe leaves the secret key there, so the payment functions can keep charging. Switching providers doesn't migrate products, prices or subscriptions either. Cancel and clean up in Stripe first, then disconnect.&lt;/p&gt;

&lt;p&gt;If your app is breaking in other places too, I wrote up the most common causes in &lt;a href="https://tusharbhendarkar.com/blog/lovable-app-not-working" rel="noopener noreferrer"&gt;Lovable app not working&lt;/a&gt;, and how the database side works in &lt;a href="https://tusharbhendarkar.com/blog/lovable-supabase-integration" rel="noopener noreferrer"&gt;Lovable Supabase integration&lt;/a&gt;. And if you'd rather hand it over, I take these on as client projects: send me the app and what's going wrong with payments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How do I add Stripe to a Lovable app?&lt;/strong&gt;&lt;br&gt;
Either turn on built-in payments, where Lovable creates and manages a Stripe or Paddle account for you (Pro plan and up), or connect your own Stripe account by pasting a restricted or secret key into the Connect Stripe form, which works on any plan. Never paste a key into the chat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I use built-in payments or my own Stripe account?&lt;/strong&gt;&lt;br&gt;
Lovable recommends built-in payments for most new projects: it handles the webhooks and keeps test and live separate. Connect your own Stripe if you already run a business on Stripe, are on the Free plan, or want to manage keys and webhooks yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why did the customer pay but my Lovable app did not unlock anything?&lt;/strong&gt;&lt;br&gt;
The app never got, or never accepted, Stripe's message that the payment succeeded. Common causes are a deleted or duplicated webhook endpoint, the wrong signing secret, a function that rejects Stripe because it is not signed in, and upgrade or renewal events that are not handled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Stripe work in the Lovable preview?&lt;/strong&gt;&lt;br&gt;
With built-in payments the preview runs in test mode with test cards, and the published app runs live. With your own Stripe there is no separate test environment: the key you paste sets the mode, so with a live key, payments made in the preview are real.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I take my Lovable app's payments live?&lt;/strong&gt;&lt;br&gt;
With your own Stripe: switch to a live key, switch the app to live price IDs (they differ from test ones) and make one real purchase on the published app. With built-in payments: complete the provider's verification and readiness check, which needs privacy, terms and refund pages and a published site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if Stripe is not available in my country?&lt;/strong&gt;&lt;br&gt;
Lovable's built-in payments can run on Paddle instead, which acts as the merchant of record and handles sales tax and VAT, for 5% plus 50 cents per transaction. Other providers such as PayPal or Razorpay are not built in and need custom code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does disconnecting Stripe in Lovable cancel subscriptions?&lt;/strong&gt;&lt;br&gt;
No. Disconnecting built-in payments is permanent and does not cancel subscriptions or refund anyone, so customers keep being billed. With your own Supabase project, disconnecting your own Stripe leaves the secret key in Supabase, so payment functions can keep charging.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post first appeared on &lt;a href="https://tusharbhendarkar.com/blog/lovable-stripe-integration" rel="noopener noreferrer"&gt;tusharbhendarkar.com&lt;/a&gt;. I fix apps built with Lovable, Replit, Bolt and Base44, and I build &lt;a href="https://tusharbhendarkar.com/tools" rel="noopener noreferrer"&gt;free checkers&lt;/a&gt; for live apps. If something here is broken for you, email me at &lt;a href="mailto:tusharbhendarkar44@gmail.com"&gt;tusharbhendarkar44@gmail.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>lovable</category>
      <category>stripe</category>
      <category>supabase</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Lovable Supabase Integration: Cloud or Your Own Project, and How to Set It Up Safely</title>
      <dc:creator>Tushar Bhendarkar</dc:creator>
      <pubDate>Sun, 18 Oct 2026 03:38:19 +0000</pubDate>
      <link>https://dev.to/tusharbhendarkar/lovable-supabase-integration-cloud-or-your-own-project-and-how-to-set-it-up-safely-53p3</link>
      <guid>https://dev.to/tusharbhendarkar/lovable-supabase-integration-cloud-or-your-own-project-and-how-to-set-it-up-safely-53p3</guid>
      <description>&lt;p&gt;If you connect your own Supabase project to Lovable and launch as is, your sign-up emails won't reach your customers. Supabase's built-in email only sends to members of your own Supabase team, and only two an hour. It's one of a handful of settings the Lovable Supabase integration leaves to you, and an easy one to discover only when your first real customer tries to sign up.&lt;/p&gt;

&lt;p&gt;For my last post I sorted 264 public reports of broken Lovable apps. Sixty-eight of them were about Supabase: logins, connections and who can see what. The biggest single cause was the database access rules, switched off or open to everyone (12 reports), followed by confusion between Lovable Cloud and your own Supabase project (6) and security warnings that were never fixed, or turned out to be noise (6).&lt;/p&gt;

&lt;p&gt;I build on Supabase myself. Launch Check and Client Approvals both run on it, so this is the setup I use, written for a Lovable app: which backend to pick, how to connect it, the settings Lovable doesn't do for you, and how I test that one user can never see another user's data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lovable Cloud or your own Supabase project?
&lt;/h2&gt;

&lt;p&gt;Lovable Cloud is Supabase, run for you. It's built on Supabase's open-source foundation, each project gets its own Supabase project behind it, and it's switched on by default, so most Lovable apps never need a separate Supabase account. Lovable's &lt;a href="https://docs.lovable.dev/integrations/supabase" rel="noopener noreferrer"&gt;Supabase docs&lt;/a&gt; give three reasons to connect your own project instead: owning the account and billing, full Supabase dashboard access, or reusing a project you already have.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Lovable Cloud&lt;/th&gt;
&lt;th&gt;Your own Supabase project&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Who runs it&lt;/td&gt;
&lt;td&gt;Lovable&lt;/td&gt;
&lt;td&gt;You, in your Supabase account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How you pay&lt;/td&gt;
&lt;td&gt;Lovable credits&lt;/td&gt;
&lt;td&gt;Your Supabase plan (free tier to start)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where you manage it&lt;/td&gt;
&lt;td&gt;Inside Lovable (Database, Users, Storage, Logs)&lt;/td&gt;
&lt;td&gt;The Supabase dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Login settings and Google sign-in&lt;/td&gt;
&lt;td&gt;From the Lovable chat&lt;/td&gt;
&lt;td&gt;In Supabase and Google Cloud, by you&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Branded emails&lt;/td&gt;
&lt;td&gt;Paid Lovable plans&lt;/td&gt;
&lt;td&gt;Your own email server (custom SMTP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backups and pausing&lt;/td&gt;
&lt;td&gt;Managed in Lovable&lt;/td&gt;
&lt;td&gt;Managed in Supabase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Switching later&lt;/td&gt;
&lt;td&gt;No automatic move either way&lt;/td&gt;
&lt;td&gt;Moving onto Cloud is not supported&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;My rule: Cloud for a prototype or an internal tool, my own Supabase project for anything I'd sell or hand over to a client. Then the database, its bill and its backups are in my name, and I can open it without going through a builder.&lt;/p&gt;

&lt;p&gt;Whichever you pick, decide early. There is no automatic move between the two in either direction. Since July 3, 2026 you can export a Cloud database and remove Cloud from a project, but removing it permanently deletes the Cloud data, storage, logins and functions. One more trap: Lovable turns Cloud on automatically when a feature needs a backend, because the permission defaults to Always allow. If you want your own Supabase, change it to Ask each time under Connectors, Cloud, Manage my agent's permissions, before you build anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to connect your own Supabase project
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a Supabase project.&lt;/strong&gt; The free tier is enough to start, and the integration works on every Lovable plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link your Supabase organization once.&lt;/strong&gt; A Lovable workspace owner or admin does this; other members can then connect projects from it without their own Supabase account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connect the project.&lt;/strong&gt; For a new app, click + in the prompt box, open Databases and pick your Supabase project. For an existing app with no backend yet, open More, Cloud and choose "Already have a Supabase project? Connect it here". That link disappears once Cloud is on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the Cloud view.&lt;/strong&gt; It should show your Supabase project's name and status, with links to its dashboard.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From then on Lovable writes database changes as migrations, shows them for your approval, runs them and saves them in supabase/migrations. It deploys edge functions, stores keys for services like Stripe as Supabase secrets, and creates storage buckets when a feature needs uploads. One Lovable project connects to one Supabase project. Several Lovable projects can share one Supabase project, but they overwrite each other's secrets, which Lovable now warns about.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcl4lvrclckewnv4cbnjt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcl4lvrclckewnv4cbnjt.png" alt="Diagram of a Lovable app on Supabase: the visitor's browser holds a public key and talks directly to Supabase Auth, Database, Storage and Edge functions; row level security rules guard the database and storage, and edge functions hold the secret service role key."&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Your app's browser code talks to the database directly. The access rules on each table are what stop one visitor reading another's data.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The settings Lovable leaves to you
&lt;/h2&gt;

&lt;p&gt;On your own Supabase project, Lovable's docs are clear that login settings, providers and email live in the Supabase dashboard, not in Lovable. These are the three I set before anyone else signs up.&lt;/p&gt;
&lt;h3&gt;
  
  
  Emails: add your own email server
&lt;/h3&gt;

&lt;p&gt;Supabase's &lt;a href="https://supabase.com/docs/guides/auth/auth-smtp" rel="noopener noreferrer"&gt;email docs&lt;/a&gt; say that without custom SMTP, auth emails only go to members of your Supabase organization, and real users get "Email address not authorized". The built-in service also sends at most two emails an hour and isn't meant for production. Add any SMTP provider (Supabase lists Resend, Postmark, AWS SES and others) under Authentication, SMTP Settings. The limit then starts at 30 emails an hour, and you can raise it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Login addresses: Site URL and redirect URLs
&lt;/h3&gt;

&lt;p&gt;The Site URL is where users land after confirming their email, resetting a password or signing in with Google. It starts as &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;, and it must be your live address. Every other address your app sends people back to has to be on the Redirect URLs list under Authentication, URL Configuration. If it isn't, Supabase quietly sends the user to the Site URL instead, which is the classic "sign-in sends me to localhost" bug. Supabase's &lt;a href="https://supabase.com/docs/guides/auth/redirect-urls" rel="noopener noreferrer"&gt;redirect docs&lt;/a&gt; suggest wildcards for preview addresses and exact paths for production. For Google sign-in, add Supabase's callback address as an authorized redirect in Google Cloud. Without your own branding or a custom domain, Google's consent screen shows your-project-id.supabase.co instead of your app's name.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pausing on the free plan
&lt;/h3&gt;

&lt;p&gt;Supabase pauses free projects after about a week with too little database activity, after emailing you a warning. Restore it from the dashboard with Resume project; paid plans are never paused for inactivity. One catch from Supabase's &lt;a href="https://supabase.com/docs/guides/api/api-keys" rel="noopener noreferrer"&gt;API key docs&lt;/a&gt;: the old anon and service_role keys are being retired by the end of 2026, and since November 2025 a project restored from a pause comes back without them. If your app still uses a long key starting with eyJ, plan the move to the new publishable key before a pause forces it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Row level security, in plain English
&lt;/h2&gt;

&lt;p&gt;Your app's code runs in the visitor's browser and talks to the database directly, using a publishable key that is public on purpose. Supabase's own analogy: the publishable key is taped to the front door and only opens the lobby, and the secret key is the master key that stays in your pocket. What decides which rooms the lobby key opens is row level security (RLS): rules on each table that act like a WHERE clause added to every query.&lt;/p&gt;

&lt;p&gt;Lovable writes these rules for you and checks them after schema changes, but it still tells you to verify every table yourself before going live. One reason: tables created with SQL, which is how migrations create them, need RLS switched on explicitly. This is the standard pattern for data that belongs to one user, from Supabase's &lt;a href="https://supabase.com/docs/guides/database/postgres/row-level-security" rel="noopener noreferrer"&gt;RLS guide&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;alter&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="k"&gt;security&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"Owners read their notes"&lt;/span&gt;
&lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt;
&lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"Owners add their own notes"&lt;/span&gt;
&lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt;
&lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;check&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"Owners edit their notes"&lt;/span&gt;
&lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt;
&lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;check&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"Owners delete their notes"&lt;/span&gt;
&lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt;
&lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;index&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One policy per action makes it obvious what each rule covers. WITH CHECK stops someone creating a row that belongs to another user, or moving a row to someone else when they edit it. Wrapping auth.uid() in a select and indexing user_id are performance fixes: in Supabase's own benchmark on 100,000 rows, wrapping auth.uid() took a query from 179 ms to 9 ms, and indexing user_id took another from 171 ms to under 0.1 ms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rules that look safe but are not
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A rule that lets everyone through.&lt;/strong&gt; using (true) means RLS shows as enabled, and everyone gets in anyway. A read rule "to anon using (true)" gives every signed-out visitor every row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Several rules on one table.&lt;/strong&gt; They combine with OR, so the most generous one wins.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roles kept in user_metadata.&lt;/strong&gt; Users can edit their own metadata, so never base access on it. Keep roles in app_metadata or a table.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Things that skip RLS completely
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The secret key&lt;/strong&gt; (the old service_role key) ignores every rule. It belongs in edge functions only, and Lovable warns that server code using it must check permissions itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SECURITY DEFINER functions&lt;/strong&gt; run as their owner, usually postgres, and anyone can call them by default, including signed-out visitors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Views&lt;/strong&gt; run as their creator too. On Postgres 15 and up, create them with security_invoker = true.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage&lt;/strong&gt; has its own rules on storage.objects. Tight table rules don't protect an uploaded file, and a broad read rule on storage lets anyone list every file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Supabase's Security Advisor flags most of these, and its production checklist starts with it. One more date for your calendar: from October 30, 2026, Supabase stops exposing new tables to the API automatically in existing projects, according to its &lt;a href="https://github.com/orgs/supabase/discussions/45329" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;. A new table without an explicit grant then returns "permission denied for table" (error 42501), even if its RLS rules are right. Lovable's docs don't mention this yet, so if a new table suddenly returns 42501 after that date, a missing grant is the first thing I'd check.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I test that one user can never see another's data
&lt;/h2&gt;

&lt;p&gt;When I built &lt;a href="https://tusharbhendarkar.com/tools/client-approvals" rel="noopener noreferrer"&gt;Client Approvals&lt;/a&gt;, I wrote a database test that signs in as different people and tries to break the rules. A team member should see only their own workspace. They try to comment on another workspace's request, attach a comment to the wrong item, post while pretending to be the client, and upload a file into another workspace's folder. Another user tries to read those comments and list those files. A signed-out client with a share link should see exactly one request and nothing else. Every one of those attempts has to fail, and the test rolls everything back when it finishes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyqoowv9uau74igl2pqde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyqoowv9uau74igl2pqde.png" alt="Access test grid: the owner of the data can read, change and share it; another signed-in user and a signed-out visitor are blocked from reading, changing, adding records to other accounts, uploading into other folders and listing other files, and a shared link shows only that one item."&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Sign in as each kind of person and try every action. Anything not marked allowed must fail, for files as well as tables.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The trap is testing in the wrong place. Supabase's SQL editor runs as the table owner, which ignores RLS, so a query that returns rows there proves nothing about what your users see. Use Supabase's user impersonation (in public beta), or run the check as a real role. This is the shape of it, inside a transaction so nothing is kept:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- act as user B, signed in&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'request.jwt.claims'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;json_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sub'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;user B id&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'role'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'authenticated'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;local&lt;/span&gt; &lt;span class="k"&gt;role&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;user A id&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- must be 0&lt;/span&gt;

&lt;span class="c1"&gt;-- act as a signed-out visitor&lt;/span&gt;
&lt;span class="k"&gt;reset&lt;/span&gt; &lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;set_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'request.jwt.claims'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'role'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'anon'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;local&lt;/span&gt; &lt;span class="k"&gt;role&lt;/span&gt; &lt;span class="n"&gt;anon&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- must be 0&lt;/span&gt;

&lt;span class="k"&gt;rollback&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't write SQL, do the manual version: create two normal accounts, add something private in each, then try to open one account's records and files while signed in as the other, and again while signed out. Both attempts must fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the integration stops working
&lt;/h2&gt;

&lt;p&gt;These are the problems that come up most, from the reports and from Supabase's own troubleshooting docs, with the fix for each:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Supabase connection issue" in Lovable.&lt;/strong&gt; Lovable can't find the project: it was deleted, or the workspace lost access to the organization. Disconnect, or use Manage organizations and pick the project again. Disconnecting deletes nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing loads right after turning on RLS.&lt;/strong&gt; RLS with no policies blocks everything through the API. Add the policies the app needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data won't load on Cloud.&lt;/strong&gt; Lovable's list: the instance is too small, a query error, RLS blocking the query, or a paused backend that needs Wake up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Email address not authorized".&lt;/strong&gt; No custom SMTP yet. See the email section above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Users land on localhost after signing in.&lt;/strong&gt; The Site URL or Redirect URLs list is missing your live address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Too Many Requests" at sign-up.&lt;/strong&gt; Supabase allows 30 sign-up and sign-in requests per 5 minutes per IP address by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Infinite recursion" (error 42P17).&lt;/strong&gt; Two tables' policies read each other, which is common with sharing features. Move the check into a helper function in a private schema.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A scan warning you can't clear.&lt;/strong&gt; Leaked password protection needs Supabase's Pro plan, so it can't be switched on for a free project.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Moving from Lovable Cloud to your own Supabase
&lt;/h2&gt;

&lt;p&gt;It's possible, by hand. Lovable's &lt;a href="https://docs.lovable.dev/tips-tricks/external-deployment-hosting" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt; says to keep the app running on Cloud while you build the new backend, and to switch only when it's complete. The schema, RLS policies, functions, triggers and storage bucket policies move with the migration files. Auth providers, secrets, table data, storage files, users and edge functions you move yourself.&lt;/p&gt;

&lt;p&gt;The Cloud export is a PostgreSQL backup file that you restore with pg_restore, with limits of 15 GB of storage and one export a day, and it leaves out storage files, edge function code and secrets. Cloud projects changed since October 7, 2026 record their changes as Drizzle migrations, which supabase db push won't run, so apply them in the order the guide lists. The final switch is two files in the Lovable project, .env and supabase/config.toml. Only remove Cloud after the new backend works, because removal deletes the Cloud data for good.&lt;/p&gt;

&lt;h2&gt;
  
  
  The integration is the easy part
&lt;/h2&gt;

&lt;p&gt;Connecting Lovable to Supabase takes a few clicks. What makes it production-ready is everything around the connection: emails that reach real people, login addresses that point at your live site, and access rules that you've actually tried to break. If your app is already misbehaving, I wrote up what breaks most often in &lt;a href="https://tusharbhendarkar.com/blog/lovable-app-not-working" rel="noopener noreferrer"&gt;Lovable app not working&lt;/a&gt;, and there are more guides in &lt;a href="https://tusharbhendarkar.com/blog/topics/fix-ai-built-app" rel="noopener noreferrer"&gt;Fix an AI-built app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And if you'd rather hand it over, I take these on as client projects: send me the app link and what you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is Lovable Cloud the same as Supabase?&lt;/strong&gt;&lt;br&gt;
Lovable Cloud is built on Supabase, and each Lovable project gets its own separate Supabase project behind it. The difference is who runs it: with Cloud, Lovable manages it, you see it inside Lovable and pay in Lovable credits. With your own Supabase project, the account, dashboard and bill are yours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I use Lovable Cloud or my own Supabase project?&lt;/strong&gt;&lt;br&gt;
Cloud is fine for prototypes and internal tools, and it is the default. Use your own Supabase project when you want to own the account and billing, use the full Supabase dashboard, or reuse an existing project. Decide early: there is no automatic switch between the two in either direction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I connect Lovable to my own Supabase project?&lt;/strong&gt;&lt;br&gt;
A workspace owner or admin links your Supabase organization once. Then, for a new project, click + in the prompt box, open Databases and pick the Supabase project. For an existing project that has no backend yet, open More, Cloud and choose "Already have a Supabase project? Connect it here". That link is missing once Cloud is on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why are my Supabase emails not reaching users?&lt;/strong&gt;&lt;br&gt;
Without your own email server (custom SMTP), Supabase only sends auth emails to members of your Supabase organization, and at most 2 an hour. Real users get "Email address not authorized". Add an SMTP provider such as Resend or Postmark under Authentication, SMTP Settings before you launch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does Supabase say permission denied for my table?&lt;/strong&gt;&lt;br&gt;
Error 42501 means the role has no grant on the table, which fails before any access rule runs. From October 30, 2026, new tables in existing Supabase projects are no longer exposed to the API automatically, so each new table needs an explicit grant. If nothing loads right after you turned row level security on, the table has no policies yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I check my Lovable app's row level security?&lt;/strong&gt;&lt;br&gt;
Run Supabase's Security Advisor, then test as real users. Do not trust the SQL editor: it runs as the table owner and ignores the rules. Use Supabase's user impersonation, a test that sets a user's role, or sign in as two normal accounts and try to open each other's records. Both attempts must fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I move a Lovable app from Lovable Cloud to my own Supabase?&lt;/strong&gt;&lt;br&gt;
Yes, by hand. Export the Cloud database, rebuild the schema in a new Supabase project, then move auth providers, secrets, files, users and edge functions yourself, and switch the app's settings last. Keep Cloud running until the new backend is complete, because removing Cloud permanently deletes its data.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post first appeared on &lt;a href="https://tusharbhendarkar.com/blog/lovable-supabase-integration" rel="noopener noreferrer"&gt;tusharbhendarkar.com&lt;/a&gt;. I fix apps built with Lovable, Replit, Bolt and Base44, and I build &lt;a href="https://tusharbhendarkar.com/tools" rel="noopener noreferrer"&gt;free checkers&lt;/a&gt; for live apps. If something here is broken for you, email me at &lt;a href="mailto:tusharbhendarkar44@gmail.com"&gt;tusharbhendarkar44@gmail.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>lovable</category>
      <category>supabase</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why Is My Base44 App Not Working? What 192 Real Reports Say</title>
      <dc:creator>Tushar Bhendarkar</dc:creator>
      <pubDate>Sat, 17 Oct 2026 03:42:49 +0000</pubDate>
      <link>https://dev.to/tusharbhendarkar/why-is-my-base44-app-not-working-what-192-real-reports-say-3cfb</link>
      <guid>https://dev.to/tusharbhendarkar/why-is-my-base44-app-not-working-what-192-real-reports-say-3cfb</guid>
      <description>&lt;p&gt;More than one in five people who said their Base44 app had stopped working had done nothing wrong. I went through 192 public reports of broken Base44 apps, from Reddit, Base44's own feedback board, Trustpilot and YouTube, and sorted each one by what actually caused it. The biggest group, 42 reports, was Base44 itself having an outage.&lt;/p&gt;

&lt;p&gt;I did this because Base44 keeps coming up in the freelance work I look at: founders who built something real with it, then got stuck. Before telling anyone how to fix their app, I wanted to know what actually breaks.&lt;/p&gt;

&lt;p&gt;The outage number matters because the usual reaction is to ask Base44's AI to fix the app. During an outage that can't work. It costs credits, and it can break code that was fine. Base44's own docs say credits aren't refunded when the AI gets something wrong.&lt;/p&gt;

&lt;p&gt;So below are the causes in the order I'd check them, with the fix for each from Base44's documentation. If your app works but isn't showing up in Google, skip to the last section: I ran 45 live Base44 apps through my free checkers, and 8 of them were telling Google not to index them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What 192 broken Base44 apps had in common
&lt;/h2&gt;

&lt;p&gt;The reports run from August 2025 to October 2026: 88 Reddit threads, 73 posts on Base44's public feedback board, 16 Trustpilot reviews, and a handful from X, YouTube, GitHub, Hacker News and elsewhere. I put each one in a group based on the cause the thread confirmed, or the fix that worked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwsrzwe6cgcbzdukic0d1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwsrzwe6cgcbzdukic0d1.png" alt="Bar chart of 192 reports of broken Base44 apps by cause: Base44 outage 42, login and sign-up 27, preview or cache only 16, AI edit broke working code 15, backend functions 11, integrations 10, custom domain 10, and smaller groups." width="800" height="520"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The single biggest cause was Base44 itself being down. Login problems and preview-only issues come next.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What surprised me is how little of this is code. Three groups make up almost half of the list: Base44 outages, login and sign-up problems, and issues that only exist in the editor preview or your browser's cache. You don't fix any of those by rewriting your app. The rest are real problems in an app's settings or code, and many of them come down to a setting you can check in a few minutes. In 22 reports the cause was never found, or didn't fit any group.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: make sure it is not Base44, or your network
&lt;/h2&gt;

&lt;p&gt;This is the step people skip. Before you touch the app, open &lt;a href="https://status.base44.com" rel="noopener noreferrer"&gt;status.base44.com&lt;/a&gt; and the r/Base44 subreddit. Base44's status feed lists 26 incidents between December 2025 and September 2026, five of them affecting previews or published apps. In the reports, people sometimes posted about an outage on Reddit before the status page showed anything. If your app and other people's apps broke at the same time, wait before you change anything.&lt;/p&gt;

&lt;p&gt;When an outage ends, your browser can keep showing the old loading screen. After its September 9, 2026 incident, Base44 told users: "If you still see Loading your app, refresh the page or clear your browser cache." A hard refresh (Ctrl+Shift+R, or Cmd+Shift+R on a Mac) or a private window is the quickest test.&lt;/p&gt;

&lt;p&gt;Then I'd open the app on a phone using mobile data, not Wi-Fi. Four of the reports turned out to be the visitor's internet provider blocking base44.app addresses: Comcast Xfinity's Advanced Security feature in the US, and an internet provider in the Philippines. The app was fine. Those visitors just couldn't reach it until they switched network or used a VPN.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: work out which kind of "not working" you have
&lt;/h2&gt;

&lt;p&gt;"Not working" covers very different problems, and the fix depends on which one you have. Find the line that matches what you see, then jump to its section below.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you see&lt;/th&gt;
&lt;th&gt;Most likely causes&lt;/th&gt;
&lt;th&gt;Check first&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nothing loads, for anyone, you included&lt;/td&gt;
&lt;td&gt;Base44 outage, or a stale cache after one&lt;/td&gt;
&lt;td&gt;Status page, hard refresh, another network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;It loads for you but not for others&lt;/td&gt;
&lt;td&gt;Not published, set to Private, a custom domain, or the visitor's network&lt;/td&gt;
&lt;td&gt;App Visibility and the Publish button&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;It works in the preview, not in the live app&lt;/td&gt;
&lt;td&gt;Not republished, test data, a backend function not redeployed&lt;/td&gt;
&lt;td&gt;Publish panel, test data switch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Login, sign-up or Google sign-in fails&lt;/td&gt;
&lt;td&gt;Login settings, emails in spam, Google OAuth setup&lt;/td&gt;
&lt;td&gt;Login settings, spam folder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data is missing or will not save&lt;/td&gt;
&lt;td&gt;Data permissions, or a field that does not match its table&lt;/td&gt;
&lt;td&gt;"Act as a user", App Logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;It worked yesterday, broke after an AI change&lt;/td&gt;
&lt;td&gt;The AI edit itself&lt;/td&gt;
&lt;td&gt;The Revert button, Version History&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Emails, AI features or payments stopped&lt;/td&gt;
&lt;td&gt;Credits ran out, plan limits, API keys&lt;/td&gt;
&lt;td&gt;Credits page, plan, Secrets&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Base44 also has a built-in check: type "Something is wrong" in the AI chat and it runs a diagnosis. Read what it says, but don't let it make several rounds of changes before you know the cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  It works in the preview but not when published
&lt;/h2&gt;

&lt;p&gt;The editor preview and the live app are two different things. According to &lt;a href="https://docs.base44.com/Getting-Started/app-editor-overview" rel="noopener noreferrer"&gt;Base44's editor docs&lt;/a&gt;, the live app only changes when you click Publish, and the preview is visible only to you and your collaborators. So my first question is always the boring one: did you click Publish after the change, and did you see the success message?&lt;/p&gt;

&lt;h3&gt;
  
  
  Test data is not real data
&lt;/h3&gt;

&lt;p&gt;On the Builder plan and up, the preview can use a separate test database. The published app always uses real (production) data, and the two are completely separate, so records you created while testing will never appear in the live app. Base44 explains this on its &lt;a href="https://docs.base44.com/documentation/managing-app-data/testing-your-data" rel="noopener noreferrer"&gt;test data page&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend functions and the browser cache
&lt;/h3&gt;

&lt;p&gt;Base44's &lt;a href="https://docs.base44.com/Community-and-support/Troubleshooting" rel="noopener noreferrer"&gt;troubleshooting guide&lt;/a&gt; has an official checklist for when published changes don't appear: clear the browser cache, try another browser or device, confirm you clicked Publish, redeploy any backend function you changed, and check the error logs. A backend function you edited keeps running its old version until it's redeployed.&lt;/p&gt;

&lt;p&gt;Sixteen reports were problems that existed only in the editor preview or the browser cache, such as a preview that kept going white after every AI change while the live app worked. Revert the last change first. If both the preview and the live app fail with a 500 error, Base44's guide calls that a server-side problem the builder usually can't fix, and that one is for support.&lt;/p&gt;

&lt;h2&gt;
  
  
  It loads for you but not for your users
&lt;/h2&gt;

&lt;p&gt;This is the most confusing one, because everything looks fine when you test it. As the owner, you can see things your users can't.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check App Visibility
&lt;/h3&gt;

&lt;p&gt;Every app has a visibility setting in Dashboard, then Overview, then App Visibility: Public (anyone, no sign-in), Private (only people you invited) or Workspace (only your Base44 workspace). People who weren't invited to a Private app see an error or a request-access pop-up. Private applies to every page, including a landing page, and since February 6, 2026 new Private apps need the Starter plan or higher. The details are on Base44's &lt;a href="https://docs.base44.com/Setting-up-your-app/Managing-access" rel="noopener noreferrer"&gt;access page&lt;/a&gt;. Invites also do nothing until the app is published, so an invited person can still be locked out of an unpublished app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data that only you can see
&lt;/h3&gt;

&lt;p&gt;On any app with user accounts, this is the one I'd check first. Base44 protects each table with data permissions, and a blocked read doesn't show an error. Its &lt;a href="https://docs.base44.com/developers/backend/resources/entities/security" rel="noopener noreferrer"&gt;security docs&lt;/a&gt; say a denied read behaves as if the data doesn't exist: the list just comes back empty. So a page can be full for you and empty for a normal user, with nothing in the console. The built-in Users table is a common trap: only the owner and collaborators can read it, so member profiles and leaderboards work for you and break for everyone else.&lt;/p&gt;

&lt;p&gt;To see what a normal user sees, use "Act as a user" or "Test Permissions in preview", and run the Security scan, which flags missing or wrong rules. If saving fails with a 403 error, the user doesn't have permission to write to that table.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom domains
&lt;/h3&gt;

&lt;p&gt;Ten reports were custom domain problems: stuck on "Pending verification", or a Bad Gateway error on the domain while the base44.app address worked. Base44's &lt;a href="https://docs.base44.com/Setting-up-your-app/Connecting-an-external-domain" rel="noopener noreferrer"&gt;domain guide&lt;/a&gt; says to remove any AAAA (IPv6) records, allow up to 48 hours, and, if your DNS is on Cloudflare, set Base44's records to "DNS only" (grey cloud). Proxying them through Cloudflare can cause SSL errors and pages that load on base44.app but not on your domain. I always test the base44.app address first: if that works, the problem is the domain, not the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Login and sign-up problems
&lt;/h2&gt;

&lt;p&gt;Login failures were the second biggest group, with 27 reports: verification codes that never arrived, invites that skipped the set-password step, Google sign-in errors and redirect loops. Base44's &lt;a href="https://docs.base44.com/Setting-up-your-app/Managing-login-and-registration" rel="noopener noreferrer"&gt;login docs&lt;/a&gt; cover the common causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Emails in spam.&lt;/strong&gt; Verification and reset emails come from &lt;a href="mailto:no-reply@base44-apps.com"&gt;no-reply@base44-apps.com&lt;/a&gt; unless you set up your own sending domain. Ask users to check spam and allowlist that address.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A renamed reset page.&lt;/strong&gt; Apps built since June 2026 have their own login pages, and the reset link always points to /reset-password. If the forgot-password or reset-password page is renamed or deleted, password resets break.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google sign-in.&lt;/strong&gt; By default the Google window says base44.com. Showing your own domain needs the Builder plan, a connected custom domain, the right redirect address and Google's approval, which can take up to five days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self sign-up on a Private app.&lt;/strong&gt; If people can register but then see access denied, the app is probably Private. Make it Public and protect the pages that need a login.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  It worked yesterday, and an AI edit broke it
&lt;/h2&gt;

&lt;p&gt;Fifteen reports describe the same loop: the app worked, someone asked the AI for a change or a fix, and something else broke. Each new fix broke something new and spent more credits.&lt;/p&gt;

&lt;p&gt;The most important rule comes from Base44's own troubleshooting guide: asking the AI chat to revert never undoes anything. Only the Revert button on a message, or Version History, rolls the app back. Base44 also advises trying the automatic fix once or twice, then switching to Discuss mode (about 0.3 credits a message) to describe the problem instead of letting the AI keep editing. One warning if you use GitHub sync: after you connect it, Version History can't restore versions from before the connection.&lt;/p&gt;

&lt;p&gt;This is the group that worries me most, because the worst report in it wasn't a crash. In a client portal built on Base44, the AI "fixed" a display bug by changing how files were looked up, and two weeks after launch one company could see another company's files. Nothing looked broken, which is why nobody noticed.&lt;/p&gt;

&lt;p&gt;When I built &lt;a href="https://tusharbhendarkar.com/tools/client-approvals" rel="noopener noreferrer"&gt;Client Approvals&lt;/a&gt;, I wrote a test that checks one workspace can never see another workspace's requests. After any AI change that touches data, that's the check I'd run by hand: sign in as a normal user and look at what they can see.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features that quietly stop: credits, plans and keys
&lt;/h2&gt;

&lt;p&gt;Some failures look like bugs but are really limits. Base44 has two kinds of credits, explained on its &lt;a href="https://docs.base44.com/Account-and-billing/Credits" rel="noopener noreferrer"&gt;credits page&lt;/a&gt;: message credits, spent when the AI builds or edits your app, and integration credits, spent when the live app uses Base44's built-in services such as email, AI calls and file uploads.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Running out doesn't take the app down.&lt;/strong&gt; Actions that use credits pause until your next reset. Since August 27, 2026 every email the app sends uses integration credits, so at the limit, workflow emails fail. Reading and writing your own data never uses credits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credits are shared.&lt;/strong&gt; They come from one pool for the whole workspace, so one busy app can use them up for all your apps. The Free plan is capped at 5 credits a day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Some features need a plan.&lt;/strong&gt; Backend functions need the Builder plan or higher. Downgrading, or a disputed card payment that moves the workspace to Free, stops them, along with every integration that depends on them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keys and webhooks.&lt;/strong&gt; API keys for other services go in Secrets. In one report, a function kept using an old key after it was changed, until the app was republished. Webhooks from services that can't sign in, such as Telegram or WhatsApp, return 403 unless the function is public.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Not showing up on Google? What I found on 45 live Base44 apps
&lt;/h2&gt;

&lt;p&gt;Sometimes "not showing" means the app works fine but can't be found or shared. I build free website checkers, so I wanted to see how live Base44 apps look from the outside. I took 45 of them from Base44's own showcase, its templates and Hacker News posts, ran each one through my &lt;a href="https://tusharbhendarkar.com/tools/sitemap-checker" rel="noopener noreferrer"&gt;sitemap checker&lt;/a&gt;, &lt;a href="https://tusharbhendarkar.com/tools/open-graph-checker" rel="noopener noreferrer"&gt;Open Graph checker&lt;/a&gt; and &lt;a href="https://tusharbhendarkar.com/tools/security-headers-checker" rel="noopener noreferrer"&gt;security headers checker&lt;/a&gt;, and fetched each home page the way Googlebot does.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiif9kcsm3m79m31tk9rd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiif9kcsm3m79m31tk9rd.png" alt="Results for 45 live Base44 apps: 8 tell Google not to index the home page, 3 show Google a page with no text, 19 of 40 share images over 300 KB, 14 of 40 use a square share image, 44 send no Content-Security-Policy, all 45 use HTTPS." width="800" height="490"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Eight apps their makers had shared publicly were telling Google not to index them.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Eight of the 45 apps sent Google a noindex instruction on their home page, which tells search engines to leave the page out of their results. These were apps their makers had shared publicly. Base44's &lt;a href="https://docs.base44.com/Performance-and-SEO/SEO-and-search-visibility" rel="noopener noreferrer"&gt;SEO docs&lt;/a&gt; describe the usual source: switching off the main SEO setting adds noindex and stops Base44 from writing your page content into the version crawlers read. Three more apps showed Google a page with no text on it at all. If your app isn't showing up in Google, check that setting first.&lt;/p&gt;

&lt;p&gt;The same docs say Base44's search features (sitemap, robots.txt and social previews) are built for apps on a custom domain. The free base44.app address can be crawled, but it isn't meant for search traffic, so if Google matters to you, connect your own domain.&lt;/p&gt;

&lt;p&gt;Share previews were mostly there but often weak. Base44 builds them from the title, description and logo in your app settings, which is why many apps share a square logo, and many apps show a square image as a small thumbnail instead of a large card. Nineteen of the 40 share images were over 300 KB, and WhatsApp often skips images that big. My Open Graph checker shows exactly what your link looks like before you post it. On security, Base44 sets HTTPS correctly for every app, but 44 of 45 sent no Content-Security-Policy, the header that limits which scripts a page may run.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to stop prompting and get help
&lt;/h2&gt;

&lt;p&gt;Work through the causes in order: Base44 and your network first, then who can see the app, then its data and logins, and only then the code. It's the same rule I use for &lt;a href="https://tusharbhendarkar.com/blog/ai-chatbot-giving-wrong-answers" rel="noopener noreferrer"&gt;chatbots that give wrong answers&lt;/a&gt;: find the stage that breaks, and fix only that.&lt;/p&gt;

&lt;p&gt;If you need Base44 support, send them the app link, the time it happened, the errors from your browser console and what App Logs shows (Dashboard, then Logs, with the Errors only filter). Their guide asks for exactly that.&lt;/p&gt;

&lt;p&gt;If you're thinking of leaving Base44, know what export gives you. Downloading the code or syncing it to GitHub needs the Builder plan, and you get a standard React and Vite project. But that code still talks to Base44 for your data, logins and backend functions, so moving off Base44 means rebuilding the backend, not just downloading files. More guides like this one are in &lt;a href="https://tusharbhendarkar.com/blog/topics/fix-ai-built-app" rel="noopener noreferrer"&gt;Fix an AI-built app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And if you'd rather hand it over, I take these on as client projects: send me the app link and what's broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why is my Base44 app stuck on "Loading your app"?&lt;/strong&gt;&lt;br&gt;
Most often because Base44 itself is having trouble: check status.base44.com and the r/Base44 subreddit. If the outage is over and you still see the loading screen, do a hard refresh or clear your browser cache, which is what Base44 told users after its September 2026 incident. If it only fails on one network, try mobile data, because some internet providers have blocked base44.app addresses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does my Base44 app work in the preview but not when published?&lt;/strong&gt;&lt;br&gt;
The live app only changes when you click Publish, so the usual cause is an edit that was never published. If you use test data, the preview reads a separate test database while the live app always uses real data. Backend functions also need to be redeployed after changes, and a stale browser cache can show an old version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why can other people not open my Base44 app?&lt;/strong&gt;&lt;br&gt;
Check that the app is published and that App Visibility (Dashboard, Overview) is set to Public. A Private app only lets in people you invited, and invites do nothing until the app is published. If you use a custom domain, test the free base44.app address too, and ask the person to try another network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is data missing for some users of my Base44 app?&lt;/strong&gt;&lt;br&gt;
Usually data permissions. When a permission rule blocks someone from reading a table, Base44 returns an empty list with no error, so the page looks empty to them while it looks fine to you as the owner. The built-in Users table can only be read by the owner and collaborators, which breaks member profiles and leaderboards for regular users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does a Base44 app stop working when credits run out?&lt;/strong&gt;&lt;br&gt;
The app stays live, but features that use credits pause until your credits reset: built-in email, AI calls, file uploads and similar. Reading and writing your app's own data does not use credits. Credits are shared across every app in the workspace, so one busy app can use them up for all of them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is my Base44 app not showing up on Google?&lt;/strong&gt;&lt;br&gt;
Check that the app is not telling Google to stay away: Base44 adds a noindex tag when its main SEO setting is switched off, and 8 of 45 live apps I checked had one. Base44's search features are also built for apps on a custom domain rather than the free base44.app address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I export my Base44 app and host it myself?&lt;/strong&gt;&lt;br&gt;
You can download the code or sync it to GitHub on the Builder plan or higher, and you get a standard React and Vite project. But it still talks to Base44 for data, logins and backend functions, so moving fully off Base44 means rebuilding that backend, not just downloading the code.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post first appeared on &lt;a href="https://tusharbhendarkar.com/blog/why-is-my-base44-app-not-working" rel="noopener noreferrer"&gt;tusharbhendarkar.com&lt;/a&gt;. I fix apps built with Lovable, Replit, Bolt and Base44, and I build &lt;a href="https://tusharbhendarkar.com/tools" rel="noopener noreferrer"&gt;free checkers&lt;/a&gt; for live apps. If something here is broken for you, email me at &lt;a href="mailto:tusharbhendarkar44@gmail.com"&gt;tusharbhendarkar44@gmail.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>base44</category>
      <category>ai</category>
      <category>nocode</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Lovable App Not Working? What Breaks Between the Demo and Production</title>
      <dc:creator>Tushar Bhendarkar</dc:creator>
      <pubDate>Thu, 15 Oct 2026 03:46:51 +0000</pubDate>
      <link>https://dev.to/tusharbhendarkar/lovable-app-not-working-what-breaks-between-the-demo-and-production-123o</link>
      <guid>https://dev.to/tusharbhendarkar/lovable-app-not-working-what-breaks-between-the-demo-and-production-123o</guid>
      <description>&lt;p&gt;The Lovable apps in the most trouble were the ones that looked fine. I went through 264 public reports of Lovable apps breaking, from Reddit, Trustpilot, GitHub and Hacker News, and sorted each one by what caused it. The biggest groups were logins and database connections (37), Lovable itself being down (33), and apps that couldn't be found on Google (32). Right behind them were 31 reports about data that was exposed or at risk, which is the one problem a working app can hide completely.&lt;/p&gt;

&lt;p&gt;I've shipped that kind of bug myself. In one of my own products, any logged-in user could see other customers' private data, because the app checked that you were logged in, but not that the data you asked for was yours. Nothing crashed and nobody complained, which is exactly why nobody had noticed.&lt;/p&gt;

&lt;p&gt;That's the real gap between a demo and production. A demo only has to work for you, on your laptop, while you watch. Production has to work for every user, on every network, when Lovable has a bad day, when your credits run out, and when someone tries to open data that isn't theirs.&lt;/p&gt;

&lt;p&gt;Below are the causes in the order I'd check them, each with the fix from Lovable's own documentation. I build on Supabase and Stripe myself, the same backend most Lovable apps run on, so I've added what I check on my own apps. At the end is what I found when I ran 53 live Lovable apps through my free checkers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What 264 broken Lovable apps had in common
&lt;/h2&gt;

&lt;p&gt;The reports run from 2025 to October 2026: 195 Reddit threads, 22 Trustpilot reviews, and the rest from Lovable's status page, GitHub issues, Hacker News, YouTube and security write-ups. I put each one in a group based on the cause the thread confirmed or, when nobody found one, the main symptom.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F48yml07y6y6dczae1l3j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F48yml07y6y6dczae1l3j.png" alt="Bar chart of 264 reports of broken Lovable apps by cause: Supabase connection or login 37, Lovable outage 33, not on Google or wrong link previews 32, data exposed or at risk 31, broke after moving off Lovable 23, AI edit broke working code 21, and smaller groups." width="800" height="520"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Data exposure sits near the top, and it is the one cause you cannot see from inside the app.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;No single cause dominates, which is why "just ask the AI to fix it" fails so often. The top four groups make up about half of all reports, and the AI can't see most of them from inside the editor: an outage, a login setting in Supabase, how Google reads your site, or what a stranger can see in your database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: check Lovable, not your app
&lt;/h2&gt;

&lt;p&gt;When someone reports a published site is down, Lovable's own &lt;a href="https://docs.lovable.dev/features/hosting" rel="noopener noreferrer"&gt;hosting docs&lt;/a&gt; say to check the status page first, and I agree. Open &lt;a href="https://status.lovable.dev" rel="noopener noreferrer"&gt;status.lovable.dev&lt;/a&gt; before you change anything. It tracks published sites (under Hosting) separately from the editor, so you can tell whether the problem is your app or Lovable. Its public history only covers the last 90 days, and in that window it lists 36 incidents, including previews and publishing failing on August 10, 2026 and published TanStack apps going down on August 4.&lt;/p&gt;

&lt;p&gt;The expensive mistake is spending credits during an outage. At least four reports lost credits that way, on prompts and builds that failed because Lovable itself was having problems. And if the app fails for one person but not for others, try their phone on mobile data. Two reports turned out to be an internet provider blocking the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  It works in the preview but not on the live site
&lt;/h2&gt;

&lt;p&gt;The preview is your working copy. The live site is a snapshot from the last time you published. Lovable's &lt;a href="https://docs.lovable.dev/features/publish" rel="noopener noreferrer"&gt;publish docs&lt;/a&gt; are blunt about it: edits aren't live until you open the Publish dialog and click Publish changes, and a small dot on the Publish button means you have unpublished changes. Publishing from that dialog costs no credits, even at a zero balance. If the dialog shows "Build errors need fixing" instead, publishing is blocked until the build passes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sign-in works in the preview, fails on the live site
&lt;/h3&gt;

&lt;p&gt;This is the one I'd check first. Lovable's &lt;a href="https://docs.lovable.dev/features/authentication" rel="noopener noreferrer"&gt;login docs&lt;/a&gt; say sign-in that works in the preview but fails on the published app or a custom domain is usually caused by missing Site URL and redirect URLs. On Lovable Cloud they're updated automatically when you publish. On your own Supabase project they aren't, so you have to add your live address yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  The preview and the live app share one database
&lt;/h3&gt;

&lt;p&gt;This surprises people. In Lovable, the &lt;a href="https://docs.lovable.dev/features/projects/preview" rel="noopener noreferrer"&gt;preview and the published app&lt;/a&gt; use the same backend and the same data, so test records you create in the preview land in your real database. Separate test and live databases were tried in a beta from February 5, 2026, then withdrawn for new Cloud projects on March 24, 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  Login and database problems
&lt;/h2&gt;

&lt;p&gt;Logins and database connections were the biggest group, with 37 reports: Google sign-in breaking, password resets failing, confirmation emails that never arrived, and projects that couldn't connect to Supabase. These are the usual causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Confirmation emails stop arriving.&lt;/strong&gt; Lovable's &lt;a href="https://docs.lovable.dev/features/email-auth" rel="noopener noreferrer"&gt;login emails&lt;/a&gt; have an hourly sending limit, which is the first thing to check when many people sign up at once, and the default sender often lands in spam.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google sign-in shows Lovable's name.&lt;/strong&gt; With Lovable Cloud's built-in Google login, the sign-in screen can say Lovable instead of your app. The fix one thread landed on was setting up your own Google OAuth client and consent screen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-confirm is still on.&lt;/strong&gt; Turning off email confirmation makes testing easier, but Lovable warns it lets anyone sign up with an address they don't own. Turn it back on before launch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Supabase project is paused.&lt;/strong&gt; A paused or still-provisioning Supabase project can't be connected to Lovable, according to its &lt;a href="https://docs.lovable.dev/integrations/supabase" rel="noopener noreferrer"&gt;Supabase docs&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The failure that does not look broken: your data is open
&lt;/h2&gt;

&lt;p&gt;Most Lovable apps keep their data in a Supabase database. Each table has access rules, called row level security (RLS), that decide who can read or change which rows. If a rule is missing or too open, anyone who opens your app can read data that isn't theirs, and everything still looks normal to you.&lt;/p&gt;

&lt;p&gt;Lovable says this plainly. Its &lt;a href="https://lovable.dev/blog/a-founders-guide-to-lovable-security" rel="noopener noreferrer"&gt;security guide&lt;/a&gt; calls misconfigured RLS the most common cause of data exposure in Supabase-backed apps, and its Supabase docs tell you to check the rules yourself before going live. It does scan for it: a &lt;a href="https://docs.lovable.dev/features/security" rel="noopener noreferrer"&gt;quick security scan&lt;/a&gt; runs on every publish and looks for tables without RLS and rules that let everyone through. But findings don't block publishing unless a workspace admin turns that on, the deeper code review is something you start by hand, and Lovable itself says its scans can't guarantee security.&lt;/p&gt;

&lt;p&gt;One thread is worth knowing about: users could see each other's data even though RLS showed as enabled, because database functions, views and storage rules can get around it. A green check on one table isn't proof for the whole app.&lt;/p&gt;

&lt;p&gt;The check I trust is the one I use on my own apps. When I built &lt;a href="https://tusharbhendarkar.com/tools/client-approvals" rel="noopener noreferrer"&gt;Client Approvals&lt;/a&gt;, I wrote a test that checks one workspace can never see another workspace's requests. You can do the manual version in a few minutes: create two normal accounts, add something private in each, then try to open one account's records while signed in as the other. Both attempts must fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  It worked yesterday, and an AI edit broke it
&lt;/h2&gt;

&lt;p&gt;Twenty-one reports describe the same loop: a small fix breaks two working things, and undoing it costs four or five more messages. Lovable's own &lt;a href="https://docs.lovable.dev/prompting/prompting-debugging" rel="noopener noreferrer"&gt;debugging advice&lt;/a&gt; is to use Try to fix once or twice, describe the bug precisely, switch to Plan mode to find the cause, and revert if the fixes have tangled the code. Plan mode costs one credit a message and doesn't change code.&lt;/p&gt;

&lt;p&gt;Every change creates a version you can restore from the &lt;a href="https://docs.lovable.dev/features/projects/history" rel="noopener noreferrer"&gt;History panel&lt;/a&gt;. But reverting restores code only: it doesn't roll back your database or its migrations, so check your data after a revert. My habit is to stop prompting after the second failed fix and read the actual error instead. For a blank published site, Lovable says to paste the browser console error into the chat, because the agent can't see errors outside the preview.&lt;/p&gt;

&lt;h2&gt;
  
  
  Credits ran out, and the backend went to sleep
&lt;/h2&gt;

&lt;p&gt;Credits are the production surprise nobody plans for. Lovable's &lt;a href="https://docs.lovable.dev/introduction/credits-and-usage" rel="noopener noreferrer"&gt;credits page&lt;/a&gt; says that when credits run out, building stops, AI features in your live app fail, and the Cloud database, storage and logins pause shortly after. The pages stay up, but anything that needs the backend stops. Adding credits may not restart it: you have to click Wake up under More, Cloud, Overview.&lt;/p&gt;

&lt;p&gt;Since June 18, 2026, Lovable splits spending into Build credits, for prompting, and Run credits, for hosting and the backend of the live app. That's when people started reporting idle projects draining credits. In one thread, a background job sending email every five seconds kept a near-idle project busy.&lt;/p&gt;

&lt;p&gt;I learned this on one of my own products: its AI features had no spending limit, so one bad day could cost any amount. I added a hard cap and a switch to turn the feature off. On Lovable, the equivalent is the usage limits and alerts added on October 7, 2026. Set them before you launch, not after the first surprise.&lt;/p&gt;

&lt;h2&gt;
  
  
  It broke when you moved it off Lovable
&lt;/h2&gt;

&lt;p&gt;Twenty-three reports were apps that worked on Lovable and broke on Vercel, Netlify or Cloudflare. Most trace back to one change: since May 13, 2026, new Lovable apps are built with TanStack Start, which renders pages on a server. Lovable's &lt;a href="https://docs.lovable.dev/tips-tricks/external-deployment-hosting" rel="noopener noreferrer"&gt;self-hosting guide&lt;/a&gt; says these apps need a host that runs server code. That's why so many exported apps built fine and then returned 404 on every page.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Newer apps (TanStack Start):&lt;/strong&gt; deploy to a host that runs server code, and check which host the build targets. In one report the build defaulted to Cloudflare, and switching it fixed Vercel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Older apps (React and Vite):&lt;/strong&gt; add a rewrite that sends every path to index.html, or direct links and refreshes return 404.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Settings:&lt;/strong&gt; set VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY on the new host before you build, and add the new domain to your login redirect URLs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you take with you:&lt;/strong&gt; exports include your code and database migrations, but not your data or stored files, and built-in AI calls keep running through Lovable's credits until you replace them with your own accounts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Not on Google? What I found on 53 live Lovable apps
&lt;/h2&gt;

&lt;p&gt;Thirty-two reports were about search: apps that never showed up on Google, or shared links with the wrong title and picture. Lovable explains the cause on its &lt;a href="https://lovable.dev/blog/building-apps-using-tanstack-start" rel="noopener noreferrer"&gt;own blog&lt;/a&gt;: apps built before May 2026 send a nearly empty page and draw everything with JavaScript, and link previews are read from that raw page.&lt;/p&gt;

&lt;p&gt;Lovable now pre-renders those older pages for Google and other verified crawlers, and since July 28, 2026 you can &lt;a href="https://docs.lovable.dev/features/upgrade-to-tanstack-start" rel="noopener noreferrer"&gt;upgrade an older project&lt;/a&gt; to server rendering in place, typically for 10 to 35 credits. It also has a free &lt;a href="https://docs.lovable.dev/features/seo-aeo" rel="noopener noreferrer"&gt;SEO and AI search review&lt;/a&gt; that flags a missing sitemap, robots.txt and metadata.&lt;/p&gt;

&lt;p&gt;I wanted to see what that looks like on real apps, so I took 53 live Lovable apps from Lovable's own pages, Product Hunt, Hacker News and Reddit launch posts, and ran them through my free &lt;a href="https://tusharbhendarkar.com/tools/sitemap-checker" rel="noopener noreferrer"&gt;sitemap checker&lt;/a&gt;, &lt;a href="https://tusharbhendarkar.com/tools/open-graph-checker" rel="noopener noreferrer"&gt;Open Graph checker&lt;/a&gt; and &lt;a href="https://tusharbhendarkar.com/tools/security-headers-checker" rel="noopener noreferrer"&gt;security headers checker&lt;/a&gt;. I only read what any browser receives.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv8ae87ewf7yvkteicgo8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv8ae87ewf7yvkteicgo8.png" alt="Results for 53 live Lovable apps: 21 have no sitemap, 9 still share Lovable's default image, 4 point their share image at a file that does not load, 13 share images over 300 KB, 51 send no Content-Security-Policy, all 53 use HTTPS." width="800" height="490"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Twenty-one apps give Google no list of their pages, and nine still share Lovable's default image.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Twenty-one of the 53 had no sitemap at any of the usual addresses, so Google gets no list of their pages. Nine still shared Lovable's default image, so a link to the app shows Lovable's branding instead of its own. Four pointed their share image at a file that doesn't load, and 13 used images over 300 KB, which WhatsApp often skips.&lt;/p&gt;

&lt;p&gt;Two sitemaps listed pages on addresses that don't respond, one of them a custom domain that no longer loads while the app's lovable.app address still works. Lovable's &lt;a href="https://docs.lovable.dev/features/custom-domain" rel="noopener noreferrer"&gt;domain docs&lt;/a&gt; list the usual causes: a leftover AAAA record, records left over from the previous host, or Cloudflare proxying the domain (set those records to DNS only). On security, all 53 were on HTTPS, but 51 sent no Content-Security-Policy and 50 could be loaded inside another site's frame. If you host the app yourself, my security headers checker shows which headers to add.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually changes between a demo and production
&lt;/h2&gt;

&lt;p&gt;Every cause in this post has the same shape. The demo worked because it only had to work once, for you. Production has to keep working for strangers, with an empty credit balance, on a bad day for Lovable, and when someone tries data that isn't theirs.&lt;/p&gt;

&lt;p&gt;I've been caught by a version of this too. One of my own products ran a daily job that published broken content about nine days a month, and nobody was told. It never threw an error. What fixed it wasn't a smarter job: it was checking the result before publishing, and sending an alert when it failed.&lt;/p&gt;

&lt;p&gt;So before you launch: publish and open the live address in a private window, sign up as a brand-new user, try to open another user's data, set credit limits and alerts, and check what your link looks like when it's shared. If you also build on Base44, I did the same research in &lt;a href="https://tusharbhendarkar.com/blog/why-is-my-base44-app-not-working" rel="noopener noreferrer"&gt;why is my Base44 app not working&lt;/a&gt;, and there are more guides in &lt;a href="https://tusharbhendarkar.com/blog/topics/fix-ai-built-app" rel="noopener noreferrer"&gt;Fix an AI-built app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And if you'd rather hand it over, I take these on as client projects: send me the app link and what's broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why does my Lovable app work in the preview but not when published?&lt;/strong&gt;&lt;br&gt;
The live site is a snapshot from your last publish, so open the Publish dialog and click Publish changes; a dot on the Publish button means you have unpublished edits. If sign-in fails only on the live site, your login redirect URLs are probably missing the live address. And remember the preview and the live app share one database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is my Lovable app showing a blank page?&lt;/strong&gt;&lt;br&gt;
In the preview, click Refresh, then Shift+Refresh to restart it; a blank preview usually means an error in the app. On the published site, copy the error from your browser console into the chat, because the agent cannot see errors outside the preview. If the blank page is on Vercel or Netlify, see the next answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does my Lovable app break on Vercel or Netlify?&lt;/strong&gt;&lt;br&gt;
Apps created since May 13, 2026 use TanStack Start, which needs a host that runs server code, so a plain static deploy builds and then returns 404. Older React and Vite apps need a rewrite that sends every path to index.html. Either way, set your Supabase variables before the build and add the new domain to your login redirect URLs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is my Lovable app's data safe?&lt;/strong&gt;&lt;br&gt;
Only if your database access rules (row level security) are right. Lovable runs a quick scan on every publish, but findings do not block publishing by default and Lovable says its scans cannot guarantee security. Test it yourself: sign in as two normal users and try to open each other's records. Both attempts must fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when Lovable credits run out?&lt;/strong&gt;&lt;br&gt;
The pages stay up, but AI features in the live app fail and the Cloud database, storage and logins pause shortly after. Adding credits may not restart the backend: click Wake up under More, Cloud, Overview. Set usage limits and alerts before launch so it does not happen by surprise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is my Lovable app not showing up on Google?&lt;/strong&gt;&lt;br&gt;
Apps built before May 2026 draw their pages with JavaScript. Lovable now pre-renders them for verified crawlers, and you can upgrade an older project to server rendering in place. Run Lovable's free SEO and AI search review, and make sure you have a sitemap: 21 of the 53 live Lovable apps I checked had none.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why do my Lovable app's links show the wrong picture when shared?&lt;/strong&gt;&lt;br&gt;
Lovable uses the share image you set, or a screenshot of the app, and older projects can still carry Lovable's default image: 9 of 53 apps I checked did. Set your own 1200 x 630 image, publish, then refresh the preview in each platform's debugger, since Slack and LinkedIn keep old previews.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This post first appeared on &lt;a href="https://tusharbhendarkar.com/blog/lovable-app-not-working" rel="noopener noreferrer"&gt;tusharbhendarkar.com&lt;/a&gt;. I fix apps built with Lovable, Replit, Bolt and Base44, and I build &lt;a href="https://tusharbhendarkar.com/tools" rel="noopener noreferrer"&gt;free checkers&lt;/a&gt; for live apps. If something here is broken for you, email me at &lt;a href="mailto:tusharbhendarkar44@gmail.com"&gt;tusharbhendarkar44@gmail.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>lovable</category>
      <category>supabase</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
