<?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: Pewterkit</title>
    <description>The latest articles on DEV Community by Pewterkit (@pewterkit_hq).</description>
    <link>https://dev.to/pewterkit_hq</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%2F4211824%2Fae2645c9-80ee-4674-ae8e-3db1943d5f5e.png</url>
      <title>DEV Community: Pewterkit</title>
      <link>https://dev.to/pewterkit_hq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pewterkit_hq"/>
    <language>en</language>
    <item>
      <title>How to test your vibe-coded app before going live: 14 things AI builders leave open</title>
      <dc:creator>Pewterkit</dc:creator>
      <pubDate>Wed, 28 Oct 2026 08:32:19 +0000</pubDate>
      <link>https://dev.to/pewterkit/how-to-test-your-vibe-coded-app-before-going-live-14-things-ai-builders-leave-open-4n95</link>
      <guid>https://dev.to/pewterkit/how-to-test-your-vibe-coded-app-before-going-live-14-things-ai-builders-leave-open-4n95</guid>
      <description>&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; before you share an app built with Lovable, Bolt, Replit, v0 or Cursor, check five places: the JavaScript your homepage ships (secret keys, test keys, localhost URLs, source maps), your response headers, your sign-in provider's redirect settings, your domain's DNS, and the template defaults left in &lt;code&gt;index.html&lt;/code&gt;. Each check below takes under a minute with DevTools, &lt;code&gt;curl&lt;/code&gt; or &lt;code&gt;dig&lt;/code&gt;, and most fixes are a few lines.&lt;/p&gt;

&lt;p&gt;Grab the scripts your homepage loads once; several checks reuse them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;APP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://yourapp.com
&lt;span class="k"&gt;for &lt;/span&gt;s &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;$APP&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oE&lt;/span&gt; &lt;span class="s1"&gt;'src="/[^"]+\.js"'&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;'"'&lt;/span&gt; &lt;span class="nt"&gt;-f2&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP$s&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; bundle.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or search them in DevTools → Sources (Cmd+Option+F / Ctrl+Shift+F).&lt;/p&gt;

&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Secret keys in your frontend bundle
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt; "add Stripe checkout" can put a secret key in browser code, readable by anyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; &lt;code&gt;grep -oE '(sk|rk)_live_.{4}|sk-proj-.{4}' bundle.js&lt;/code&gt;. For Supabase, paste any &lt;code&gt;eyJ…&lt;/code&gt; key from the bundle into the console as &lt;code&gt;key&lt;/code&gt;; &lt;code&gt;"service_role"&lt;/code&gt; means it skips Row Level Security:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;atob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/-/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/_/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; rotate the key first (deleting it isn't enough), move the call into a server function that reads the new key from a secret, and keep only publishable keys (&lt;code&gt;pk_live_&lt;/code&gt;, Supabase's &lt;code&gt;anon&lt;/code&gt;/publishable key) in the browser.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://pewterkit.com/guides/stripe-secret-key-exposed" rel="noopener noreferrer"&gt;Stripe secret key exposed in your frontend&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Public source maps in production
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt; a served &lt;code&gt;.map&lt;/code&gt; file turns your minified bundle back into readable source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; &lt;code&gt;grep -o 'sourceMappingURL=[^ ]*' bundle.js&lt;/code&gt;, then &lt;code&gt;curl -sI&lt;/code&gt; that &lt;code&gt;.map&lt;/code&gt; file's URL. A &lt;code&gt;200&lt;/code&gt; means it's public.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; in Vite, &lt;code&gt;build: { sourcemap: false }&lt;/code&gt; (the default); in Next.js, remove &lt;code&gt;productionBrowserSourceMaps: true&lt;/code&gt;. For readable stack traces, upload maps privately to your error tracker instead.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://pewterkit.com/guides/source-maps-in-production" rel="noopener noreferrer"&gt;Public source maps in production&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Missing HSTS, nosniff, clickjacking and Referrer-Policy headers
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt; browsers aren't told to stick to HTTPS, and other sites can frame your pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; &lt;code&gt;curl -sI $APP | grep -iE 'strict-transport|frame-ancestors|x-frame|nosniff|referrer-policy'&lt;/code&gt; on your own domain (builder subdomains control their own headers).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; a &lt;code&gt;_headers&lt;/code&gt; file on Netlify or Cloudflare Pages (on Vercel, &lt;code&gt;vercel.json&lt;/code&gt; → &lt;code&gt;headers&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*
  Strict-Transport-Security: max-age=31536000
  Content-Security-Policy: frame-ancestors 'none'
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;'self'&lt;/code&gt; / &lt;code&gt;SAMEORIGIN&lt;/code&gt; if your app frames its own pages.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://pewterkit.com/guides/hsts-header-missing" rel="noopener noreferrer"&gt;HSTS missing from HTTPS server&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Payments
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4. Stripe test mode on the live site
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt; a &lt;code&gt;pk_test_&lt;/code&gt; key means checkout runs in a sandbox, so no real card can be charged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; &lt;code&gt;grep -oE 'pk_test_|buy\.stripe\.com/test_' bundle.js | sort -u&lt;/code&gt; (Clerk development keys share the prefix; check the context).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; swap in &lt;code&gt;pk_live_&lt;/code&gt; and a live server key, then recreate products, prices, Payment Links and webhooks in live mode; sandbox objects don't carry over.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://pewterkit.com/guides/stripe-test-mode-live-site" rel="noopener noreferrer"&gt;Stripe in test mode on your live site&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Stripe checkout success_url pointing at localhost
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt; customers who finish paying are sent to &lt;code&gt;http://localhost:3000/success&lt;/code&gt;, on their own machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; &lt;code&gt;grep -oE '(success_url|cancel_url|return_url).{0,40}' bundle.js | grep localhost&lt;/code&gt;, then the same over your server functions (server-built URLs aren't in the bundle).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; build both URLs from a &lt;code&gt;SITE_URL&lt;/code&gt; setting, for example &lt;code&gt;`${process.env.SITE_URL}/thanks?session_id={CHECKOUT_SESSION_ID}`&lt;/code&gt;, and fulfil orders from a webhook, not the success page.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://pewterkit.com/guides/stripe-success-url-localhost" rel="noopener noreferrer"&gt;Stripe checkout success_url points at localhost&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sign-in
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6. Supabase Google OAuth redirects to localhost in production
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt; Google sign-in succeeds, then users land on &lt;code&gt;http://localhost:3000&lt;/code&gt;, Supabase's starting Site URL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; Supabase dashboard → Authentication → URL Configuration: is Site URL still localhost? Then sign in on your live site in a private window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; set Site URL to your production origin, add your exact callback URL to Redirect URLs, and always pass &lt;code&gt;redirectTo&lt;/code&gt;, built from the current origin, to &lt;code&gt;signInWithOAuth&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://pewterkit.com/guides/supabase-google-oauth-redirects-to-localhost" rel="noopener noreferrer"&gt;Supabase Google OAuth redirecting to localhost&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Google's sign-in screen names firebaseapp.com, not your domain
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt; Google's screen shows &lt;code&gt;your-project.firebaseapp.com&lt;/code&gt; (or &lt;code&gt;&amp;lt;ref&amp;gt;.supabase.co&lt;/code&gt;) instead of your domain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; &lt;code&gt;grep -oE 'authDomain: ?"[^"]+"' bundle.js&lt;/code&gt;, or click "Sign in with Google" in a private window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; serve &lt;code&gt;/__/auth/*&lt;/code&gt; on your domain (Firebase Hosting does this; elsewhere a transparent reverse proxy, not a 302), add &lt;code&gt;https://yourdomain/__/auth/handler&lt;/code&gt; to the OAuth client and your domain to Authorized domains, then change &lt;code&gt;authDomain&lt;/code&gt;. On Supabase, add a custom domain.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://pewterkit.com/guides/change-firebase-authdomain" rel="noopener noreferrer"&gt;Change Firebase authDomain to your own domain&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Email &amp;amp; domain
&lt;/h2&gt;

&lt;h3&gt;
  
  
  8. No SPF record found (and no DMARC)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt; receivers can't confirm your sign-in emails and receipts came from you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig +short TXT yourapp.com | &lt;span class="nb"&gt;grep &lt;/span&gt;spf1
dig +short TXT _dmarc.yourapp.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; one SPF TXT record at &lt;code&gt;@&lt;/code&gt; combining every sender's &lt;code&gt;include:&lt;/code&gt;, ending in &lt;code&gt;~all&lt;/code&gt; (never two records, never &lt;code&gt;+all&lt;/code&gt;). Once SPF or DKIM works, add &lt;code&gt;_dmarc&lt;/code&gt; TXT &lt;code&gt;v=DMARC1; p=none; rua=mailto:dmarc@yourapp.com&lt;/code&gt; and tighten it later.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://pewterkit.com/guides/spf-record-missing" rel="noopener noreferrer"&gt;No SPF record found&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  9. HTTP to HTTPS redirect not working
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt; &lt;code&gt;http://yourapp.com&lt;/code&gt; loads instead of moving visitors to HTTPS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; &lt;code&gt;curl -sI http://yourapp.com | grep -iE '^HTTP|^location'&lt;/code&gt; should show a 3xx redirect to &lt;code&gt;https://&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; turn on your host's redirect (Cloudflare: SSL/TLS → Edge Certificates → Always Use HTTPS) in one layer only, to avoid loops, plus HSTS (item 3).&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://pewterkit.com/guides/http-to-https-redirect" rel="noopener noreferrer"&gt;HTTP not redirecting to HTTPS&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy, rules &amp;amp; accessibility
&lt;/h2&gt;

&lt;h3&gt;
  
  
  10. A privacy policy with no effective date
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt; California's CalOPPA asks sites collecting personal information from Californians to post a policy with its effective date and, if you track across sites, your Do Not Track response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; open the "Privacy" link in your homepage footer and search for "Effective date" and "Do Not Track".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; a footer link to its own page, "Effective date:" at the top, and one honest Do Not Track paragraph. Have a lawyer glance at anything business-specific.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://pewterkit.com/guides/privacy-policy-for-app-us" rel="noopener noreferrer"&gt;Privacy policy for an app (US)&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  11. Trackers that load before cookie consent
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt; the banner exists, but the Meta Pixel snippet in &lt;code&gt;index.html&lt;/code&gt; runs on every load; for UK and EU visitors, ad pixels must wait for consent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; private window → DevTools → Network, filter &lt;code&gt;/googletagmanager|connect\.facebook|analytics\.tiktok/&lt;/code&gt;, reload without touching the banner. Any match means they aren't waiting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; load the snippets from your consent callback instead, and give "Accept all" and "Reject all" equal weight.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://pewterkit.com/guides/cookie-banner-before-trackers" rel="noopener noreferrer"&gt;Cookie banner: trackers after consent&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  12. Buttons must have discernible text
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt; icon-only buttons (menu, close, heart) are announced as just "button", and images with no &lt;code&gt;alt&lt;/code&gt; may be read out as file names.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-labelledby&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img:not([alt])&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; &lt;code&gt;&amp;lt;button aria-label="Open menu"&amp;gt;&amp;lt;Menu aria-hidden="true" /&amp;gt;&amp;lt;/button&amp;gt;&lt;/code&gt;; meaningful images get a short &lt;code&gt;alt&lt;/code&gt;, decorative ones &lt;code&gt;alt=""&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://pewterkit.com/guides/buttons-must-have-discernible-text" rel="noopener noreferrer"&gt;Buttons must have discernible text&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Search &amp;amp; launch day
&lt;/h2&gt;

&lt;h3&gt;
  
  
  13. Page title still says "Lovable App" or "Vite + React"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt; the template's title, description and icon show in Google, browser tabs and link previews.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; &lt;code&gt;curl -s $APP | grep -iE '&amp;lt;title&amp;gt;|name="description"|rel="icon"'&lt;/code&gt; and look for "Lovable App", "Vite + React", "Lovable Generated Project" or &lt;code&gt;/vite.svg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; in &lt;code&gt;index.html&lt;/code&gt;, set a real title (&lt;code&gt;&amp;lt;title&amp;gt;Acme Shifts: staff scheduling for cafes&amp;lt;/title&amp;gt;&lt;/code&gt;) and description, and point the icon at your own PNG in &lt;code&gt;public/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://pewterkit.com/guides/page-title-still-lovable-app" rel="noopener noreferrer"&gt;Page title still says Lovable App?&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  14. Excluded by noindex tag
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What goes wrong:&lt;/strong&gt; a &lt;code&gt;noindex&lt;/code&gt; left over from staging tells Google not to list your site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sI&lt;/span&gt; &lt;span class="nv"&gt;$APP&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; x-robots-tag
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;$APP&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'name="robots"'&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;$APP&lt;/span&gt;/robots.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; remove any noindex tag, &lt;code&gt;X-Robots-Tag: noindex&lt;/code&gt; or &lt;code&gt;Disallow: /&lt;/code&gt; from production, redeploy, and request indexing in Search Console.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://pewterkit.com/guides/noindex-left-on" rel="noopener noreferrer"&gt;Excluded by noindex tag&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running it automatically
&lt;/h2&gt;

&lt;p&gt;I built Pewterkit, whose free check covers most of these items, and more, against your live URL. It reads public information only (homepage, scripts, headers, DNS) and shows the line, header or record behind each finding. It also sells filled-in fixes, but every fix above works without them: &lt;a href="https://pewterkit.com" rel="noopener noreferrer"&gt;pewterkit.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What would you add?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was drafted with AI assistance and checked against the linked guides and the official docs they cite.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>beginners</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
