<?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: WobblyDev</title>
    <description>The latest articles on DEV Community by WobblyDev (@_51c72dc747ba876dd3294).</description>
    <link>https://dev.to/_51c72dc747ba876dd3294</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3871291%2F4329b508-99a2-4b11-a45e-69be8e64ccb8.png</url>
      <title>DEV Community: WobblyDev</title>
      <link>https://dev.to/_51c72dc747ba876dd3294</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/_51c72dc747ba876dd3294"/>
    <language>en</language>
    <item>
      <title>I Built a Free AI Background Remover with Next.js 16 &amp; Cloudflare Workers — $0/mo</title>
      <dc:creator>WobblyDev</dc:creator>
      <pubDate>Mon, 13 Apr 2026 08:34:43 +0000</pubDate>
      <link>https://dev.to/_51c72dc747ba876dd3294/i-built-a-free-ai-background-remover-with-nextjs-16-cloudflare-workers-0mo-5b0p</link>
      <guid>https://dev.to/_51c72dc747ba876dd3294/i-built-a-free-ai-background-remover-with-nextjs-16-cloudflare-workers-0mo-5b0p</guid>
      <description>&lt;p&gt;Let me start with some context: I have a graveyard of unfinished side projects. Probably 15+ repos that never saw the light of day. This time I told myself — ship it, no matter how ugly the first version is.&lt;/p&gt;

&lt;p&gt;The idea was dead simple. Upload a photo, remove the background, download a transparent PNG. That's it.&lt;/p&gt;

&lt;p&gt;I ended up building &lt;strong&gt;&lt;a href="https://imagesbackgroundremover.com" rel="noopener noreferrer"&gt;imagesbackgroundremover.com&lt;/a&gt;&lt;/strong&gt; — and the whole thing runs on Cloudflare's free tier. Zero dollars a month in hosting costs. I've also open-sourced the entire codebase so you can see exactly how it's put together:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/turnturn" rel="noopener noreferrer"&gt;
        turnturn
      &lt;/a&gt; / &lt;a href="https://github.com/turnturn/bg-remover-nextjs-cloudflare" rel="noopener noreferrer"&gt;
        bg-remover-nextjs-cloudflare
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Free AI Background Remover built with Next.js 16, Tailwind CSS 4, and Cloudflare Workers. Zero infrastructure cost.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🖼️ BG Remover — AI Background Remover&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Remove image backgrounds instantly with AI. Free, fast, and no signup required.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;🌐 &lt;strong&gt;&lt;a href="https://imagesbackgroundremover.com" rel="nofollow noopener noreferrer"&gt;imagesbackgroundremover.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;✨ Features&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🎯 AI Background Removal&lt;/strong&gt; — Powered by advanced AI that detects subjects and removes backgrounds in seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📸 Drag &amp;amp; Drop Upload&lt;/strong&gt; — Supports JPG, PNG, and WebP (up to 10 MB)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔍 Before/After Comparison&lt;/strong&gt; — Interactive slider to compare original and processed images&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⬇️ One-Click Download&lt;/strong&gt; — Download transparent PNG with a single click&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📱 Fully Responsive&lt;/strong&gt; — Works flawlessly on mobile, tablet, and desktop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🌍 Multi-language&lt;/strong&gt; — English and Chinese (中文) support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔒 Privacy First&lt;/strong&gt; — Images are processed in real-time and never stored&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔍 SEO Optimized&lt;/strong&gt; — JSON-LD, Open Graph, Twitter Cards, semantic HTML, blog&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🚀 Live Demo&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Try it now at &lt;strong&gt;&lt;a href="https://imagesbackgroundremover.com" rel="nofollow noopener noreferrer"&gt;imagesbackgroundremover.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;How It Works&lt;/h3&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Upload&lt;/strong&gt; your image (JPG, PNG, or WebP)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI processes&lt;/strong&gt; and removes the background automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Download&lt;/strong&gt; the transparent PNG…&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/turnturn/bg-remover-nextjs-cloudflare" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Here's the honest breakdown of how it went.&lt;/p&gt;




&lt;h2&gt;
  
  
  The architecture (kept it boring on purpose)
&lt;/h2&gt;

&lt;p&gt;I'm a big believer in boring technology. The fewer moving parts, the fewer things that break at 2am.&lt;/p&gt;

&lt;p&gt;The whole system is just a browser talking to Cloudflare Workers on the edge. No origin server, no Docker containers, no Kubernetes, no "microservice mesh." Workers handles routing, auth, and API calls. Data goes to D1 and KV. That's it.&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.amazonaws.com%2Fuploads%2Farticles%2F25uo9fhk3lan708ojtgt.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.amazonaws.com%2Fuploads%2Farticles%2F25uo9fhk3lan708ojtgt.png" alt="Architecture diagram"&gt;&lt;/a&gt;&lt;/p&gt;
The "boring" architecture: browser → Cloudflare Workers → D1/KV, with Google OAuth and PayPal as external services.



&lt;p&gt;Here's the stack in plain terms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 16&lt;/strong&gt; — App Router with Edge Runtime. The latest version, because why not live on the edge (pun intended).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; — I don't write JS without types anymore. Life's too short for &lt;code&gt;undefined is not a function&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS 4&lt;/strong&gt; — Say what you will, but I can prototype a decent-looking UI in hours instead of days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Workers&lt;/strong&gt; — The compute layer. Deployed via &lt;code&gt;opennextjs-cloudflare&lt;/code&gt; (&lt;a href="https://github.com/turnturn/bg-remover-nextjs-cloudflare/blob/main/wrangler.toml" rel="noopener noreferrer"&gt;see the config&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;D1&lt;/strong&gt; — Cloudflare's edge SQLite. Stores users, credits, transactions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KV&lt;/strong&gt; — Key-value store for sessions. Think Redis, but free and managed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google OAuth&lt;/strong&gt; — Login. Hand-rolled, not NextAuth (more on this later).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PayPal&lt;/strong&gt; — Payments. Subscriptions and one-time credit packs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Monthly infrastructure cost: &lt;strong&gt;$0&lt;/strong&gt;. Not "$0 for the first 3 months." Just $0.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I left Vercel
&lt;/h2&gt;

&lt;p&gt;I know, I know. "Next.js + Vercel" is like peanut butter and jelly. And honestly, the developer experience on Vercel is excellent.&lt;/p&gt;

&lt;p&gt;But here's what happened. I needed a database for users and credits. I needed session storage. On Vercel's free tier, that means bolting on external services — a hosted Postgres, maybe Upstash for Redis. Suddenly my "free" project had $15-20/month in dependencies.&lt;/p&gt;

&lt;p&gt;Cloudflare gave me all of that for free:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;D1&lt;/strong&gt; — A real SQL database at the edge. Not a toy. Actual SQLite with proper query support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KV&lt;/strong&gt; — Session storage without managing Redis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No cold starts&lt;/strong&gt; — Workers are always warm. Users in Tokyo and users in Berlin get the same fast response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The catch? You need &lt;code&gt;opennextjs-cloudflare&lt;/code&gt; to make Next.js work on Workers. It's an adapter, and it works — but don't expect the Vercel-level polish. Debugging runtime errors on Workers can feel like reading tea leaves sometimes.&lt;/p&gt;

&lt;p&gt;For a solo project with zero budget, the math was obvious.&lt;/p&gt;




&lt;h2&gt;
  
  
  The deploy gotcha that cost me an afternoon
&lt;/h2&gt;

&lt;p&gt;Let me save you some time: &lt;strong&gt;pushing to GitHub does NOT trigger a Cloudflare Workers build.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you've used Cloudflare Pages, you're probably expecting Git-push deploys. Workers doesn't do that. You need two commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx opennextjs-cloudflare build   &lt;span class="c"&gt;# ~45 seconds&lt;/span&gt;
npx wrangler deploy               &lt;span class="c"&gt;# ~45 seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.amazonaws.com%2Fuploads%2Farticles%2Fo2yjm3gxf6w5awegsw0n.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.amazonaws.com%2Fuploads%2Farticles%2Fo2yjm3gxf6w5awegsw0n.png" alt="Deployment pipeline"&gt;&lt;/a&gt;&lt;/p&gt;
The deploy flow: two commands, under 2 minutes from commit to live.



&lt;p&gt;I spent a genuinely embarrassing amount of time refreshing my production URL and wondering why nothing changed. Don't be me.&lt;/p&gt;

&lt;p&gt;Also, in your &lt;code&gt;wrangler.toml&lt;/code&gt;, do not forget this line:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;compatibility_flags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"nodejs_compat"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Without it, Node.js APIs that Next.js depends on will fail &lt;em&gt;silently&lt;/em&gt; at runtime. Not with a nice error message. Just weird, broken behavior that makes you question your life choices.&lt;/p&gt;


&lt;h2&gt;
  
  
  The UI: one rule
&lt;/h2&gt;

&lt;p&gt;I gave myself one rule for the interface: &lt;strong&gt;if someone can't figure out what this tool does within 5 seconds, I failed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So the design is deliberately simple. Upload area dead center. Three-step "how it works" below. FAQ at the bottom. No sidebar, no feature tour, no "watch our 2-minute intro video." Just the thing you came here to do.&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.amazonaws.com%2Fuploads%2Farticles%2F879snavp4miyya1yhhkg.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.amazonaws.com%2Fuploads%2Farticles%2F879snavp4miyya1yhhkg.png" alt="Product screenshot"&gt;&lt;/a&gt;&lt;/p&gt;
The live product — one page, one action, zero confusion.


&lt;h3&gt;
  
  
  The before/after slider
&lt;/h3&gt;

&lt;p&gt;This is probably my favorite piece of UI in the whole project. It looks like it needs a fancy library, but it's actually embarrassingly simple:&lt;/p&gt;

&lt;p&gt;
  Click to see the slider code (React + CSS clip-path)
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"relative overflow-hidden"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Original"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; 
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;processedUrl&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
    &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Processed"&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;clipPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`inset(0 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;% 0 0)`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
    &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"absolute inset-0"&lt;/span&gt;
  &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; 
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"range"&lt;/span&gt; &lt;span class="na"&gt;min&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; 
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;That's it. CSS &lt;code&gt;clip-path&lt;/code&gt; and a native range input. No npm package needed.&lt;/p&gt;
&lt;h3&gt;
  
  
  i18n for two languages
&lt;/h3&gt;

&lt;p&gt;The site supports English and Chinese. I considered &lt;code&gt;next-intl&lt;/code&gt; and &lt;code&gt;react-i18next&lt;/code&gt;, then decided they were massive overkill for two languages. Instead I wrote a simple &lt;a href="https://github.com/turnturn/bg-remover-nextjs-cloudflare/blob/main/src/lib/i18n.ts" rel="noopener noreferrer"&gt;&lt;code&gt;i18n.ts&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Locale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Messages&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Make your images clean, cut-out, and ready to use.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;zh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;让图片更干净，更利落，更适合直接使用。&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Language switching uses &lt;code&gt;?lang=zh&lt;/code&gt; URL params with &lt;code&gt;localStorage&lt;/code&gt; persistence. An &lt;code&gt;applySeo()&lt;/code&gt; function updates all meta tags dynamically.&lt;/p&gt;

&lt;p&gt;Is this the "right" way to do internationalization? No. Does it work perfectly for my use case? Absolutely.&lt;/p&gt;


&lt;h2&gt;
  
  
  Auth: 200 lines vs. a framework
&lt;/h2&gt;

&lt;p&gt;I hand-rolled Google OAuth instead of reaching for NextAuth. Here's why:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;I only need Google login.&lt;/strong&gt; NextAuth is built for "sign in with 47 different providers." That's not my problem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge compatibility.&lt;/strong&gt; Some NextAuth adapters don't play nice with Workers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's three routes.&lt;/strong&gt; That's it.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/api/auth/login           → redirect to Google consent screen
/api/auth/callback/google → exchange code for tokens, create user in D1
/api/auth/me              → look up session in KV, return user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Sessions live in KV with a 7-day TTL. The entire auth system is ~200 lines. Sometimes the best abstraction is no abstraction.&lt;/p&gt;


&lt;h2&gt;
  
  
  Making money (the honest version)
&lt;/h2&gt;

&lt;p&gt;I went with a credits model because it's the simplest thing that could work:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;What you get&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;3 credits on signup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Starter&lt;/td&gt;
&lt;td&gt;$3.49/mo&lt;/td&gt;
&lt;td&gt;10 credits/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;$8.69/mo&lt;/td&gt;
&lt;td&gt;25 credits/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit Pack (5)&lt;/td&gt;
&lt;td&gt;$1.73&lt;/td&gt;
&lt;td&gt;Never expire&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit Pack (10)&lt;/td&gt;
&lt;td&gt;$3.46&lt;/td&gt;
&lt;td&gt;Never expire&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;PayPal handles subscriptions and one-time purchases. The integration was fine until I hit webhook signature verification. PayPal's documentation for verifying webhooks on edge runtimes reads like it was written by someone who has never actually tried to verify a webhook on an edge runtime. I got it working eventually, but it was not fun.&lt;/p&gt;


&lt;h2&gt;
  
  
  SEO for a domain that Google has never heard of
&lt;/h2&gt;

&lt;p&gt;A brand-new domain has zero authority. Zero. Google doesn't know you exist, and it doesn't care that your code is clean.&lt;/p&gt;

&lt;p&gt;Here's what I did:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The basics (one afternoon of work):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proper &lt;code&gt;robots.txt&lt;/code&gt; and &lt;code&gt;sitemap.xml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;JSON-LD structured data (WebApplication schema)&lt;/li&gt;
&lt;li&gt;Open Graph + Twitter Card tags&lt;/li&gt;
&lt;li&gt;Canonical URLs and &lt;code&gt;hreflang&lt;/code&gt; for both languages&lt;/li&gt;
&lt;li&gt;Title &amp;lt; 70 chars, description &amp;lt; 155 chars&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The real work (ongoing):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built a &lt;code&gt;/blog&lt;/code&gt; with three articles targeting long-tail keywords:

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;"How to Remove Image Background for Free in 2026"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Best Background Remover Tools Compared"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Transparent PNG for Ecommerce Product Photos"&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Each post has its own SEO metadata, JSON-LD Article schema, canonical URL, and a CTA back to the main tool&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Technical SEO is table stakes. Everyone has it. Content and backlinks are what actually move the needle — and that's a game measured in months, not days.&lt;/p&gt;


&lt;h2&gt;
  
  
  Five things I'd tell past-me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Ship the smallest version first.&lt;/strong&gt; My v1 had no login, no payments, no blog. Just: upload → remove → download. One page, one button. I added everything else after confirming people actually used it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Cloudflare's free tier is a cheat code.&lt;/strong&gt; D1 + KV + Workers replaces $20-30/month of managed services. For indie devs and side projects, this changes the economics completely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Don't fight the deployment.&lt;/strong&gt; I wasted time trying to get Git-push deploys working with Workers. Just accept the two-command flow and move on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Resist every feature idea.&lt;/strong&gt; I almost added batch upload, color backgrounds, AI enhancement filters. Glad I didn't. The product is better because it does one thing well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. SEO is a slow burn.&lt;/strong&gt; The technical foundation matters, but don't expect Google to notice you for weeks. Write content, build links, be patient.&lt;/p&gt;


&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you need to remove a background from an image — give it a spin. Free, instant, no signup required, works on mobile.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://imagesbackgroundremover.com" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Try the Background Remover for Free&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;The full source code is open:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/turnturn" rel="noopener noreferrer"&gt;
        turnturn
      &lt;/a&gt; / &lt;a href="https://github.com/turnturn/bg-remover-nextjs-cloudflare" rel="noopener noreferrer"&gt;
        bg-remover-nextjs-cloudflare
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Free AI Background Remover built with Next.js 16, Tailwind CSS 4, and Cloudflare Workers. Zero infrastructure cost.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🖼️ BG Remover — AI Background Remover&lt;/h1&gt;
&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Remove image backgrounds instantly with AI. Free, fast, and no signup required.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;🌐 &lt;strong&gt;&lt;a href="https://imagesbackgroundremover.com" rel="nofollow noopener noreferrer"&gt;imagesbackgroundremover.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;✨ Features&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🎯 AI Background Removal&lt;/strong&gt; — Powered by advanced AI that detects subjects and removes backgrounds in seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📸 Drag &amp;amp; Drop Upload&lt;/strong&gt; — Supports JPG, PNG, and WebP (up to 10 MB)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔍 Before/After Comparison&lt;/strong&gt; — Interactive slider to compare original and processed images&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⬇️ One-Click Download&lt;/strong&gt; — Download transparent PNG with a single click&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📱 Fully Responsive&lt;/strong&gt; — Works flawlessly on mobile, tablet, and desktop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🌍 Multi-language&lt;/strong&gt; — English and Chinese (中文) support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔒 Privacy First&lt;/strong&gt; — Images are processed in real-time and never stored&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🔍 SEO Optimized&lt;/strong&gt; — JSON-LD, Open Graph, Twitter Cards, semantic HTML, blog&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🚀 Live Demo&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Try it now at &lt;strong&gt;&lt;a href="https://imagesbackgroundremover.com" rel="nofollow noopener noreferrer"&gt;imagesbackgroundremover.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;How It Works&lt;/h3&gt;

&lt;/div&gt;


&lt;ol&gt;

&lt;li&gt;

&lt;strong&gt;Upload&lt;/strong&gt; your image (JPG, PNG, or WebP)&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;AI processes&lt;/strong&gt; and removes the background automatically&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Download&lt;/strong&gt; the transparent PNG…&lt;/li&gt;

&lt;/ol&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/turnturn/bg-remover-nextjs-cloudflare" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Got questions about the edge deployment, the OAuth flow, or anything else? Happy to go deeper in the comments.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>cloudflarechallenge</category>
    </item>
  </channel>
</rss>
