Website Launch Blueprint - From Zero to AdSense

🧠 On This Page

This guide provides a comprehensive, step-by-step roadmap to building a passive income stream through micro-tool websites. By finding small, low-competition problems and using AI to build fast, SEO-optimized utilities, you can rank on Google and earn revenue via Google AdSense.

Strategy Overview

Real-World Example: The author's portfolio at mohitarora.me was built following this exact process: React + TypeScript, Cloudflare Pages, multi-theme design with CSS variables, auto-deploy from GitHub, JSON-LD structured data for SEO, custom 404/500 error pages, Sentry error monitoring, PWA with service worker caching, and a custom SPA router with scroll restoration. Every technique in this guide is battle-tested on a live production site.


Step 1: Finding a Niche Problem and Keywords

The goal is to find a small problem with low competition that users are actively searching for.

  1. Find an Idea: Think of small everyday problems (e.g., needing to measure something without a physical ruler). Search Google for simple micro-tools.
  2. Analyze the Competition: Open the top-ranking micro-tool. What is it missing? Does it have a bad UI? Does it fail on mobile? (e.g., if a ruler tool says "Unknown Device" or the scale is on the wrong side, note these flaws down so you can explicitly tell the AI to fix them).
  3. Keyword Research: Go to the Ahrefs Free Keyword Generator.
    1. Enter your keyword idea (e.g., "online ruler"). Verify the search volume. Even 1,000 monthly US searches is good to start.
    2. Switch to the Questions tab and copy all the common user questions. Save these for your FAQ section later.
    3. Note down Supporting Keywords (e.g., "free online ruler", "online ruler in cm"). You can also find these using Google Autocomplete (type your keyword and press space).

Step 2: Choosing the Perfect Domain Name

Your domain name is a crucial ranking factor.

  1. Go to Instant Domain Search to brainstorm.
  2. Rules for Domains:
    • MUST be a .com domain.
    • Must contain your primary keyword (e.g., realonlineruler.com contains online and ruler).
    • Avoid hyphens.
  3. Keep it Short: Avoid overly long domains. If freeruler.com is taken, try adding context like realonlineruler.com.
  4. When to Buy: CRITICAL: Do NOT buy the domain yet! Build the tool with AI first. Sometimes an idea turns out to be too complex or requires paid APIs. Only buy the domain after the code is fully working on your local computer so you don't waste ₹1,000.
  5. Where to buy later: Spaceship (recommended - sister company of Namecheap with modern UI, low entry & renewal rates, and free WHOIS privacy), Namecheap (established veteran, but renewal prices spike after year one), or traditional registrars like GoDaddy, BigRock, or Hostinger. Use promo code SPSR86 on Spaceship for deep discounts on first-year .me domains.

Step 3: Setting Up the Development Environment

Prepare your local machine for the AI agent to build the tool.

  1. Install Git for version control.
  2. Install Visual Studio Code (VS Code) as your code editor.
  3. Install Node.js (required to run Astro JS locally).
  4. Install an AI Coding Assistant. Claude Code is highly recommended. If you have API access, run:
    npm install -g @anthropic-ai/claude-code
    
    *(Why? This command uses the Node Package Manager (npm) to install Claude Code globally (-g) on your system, allowing you to use the claude command from any terminal folder.)* Alternatively, use free alternatives like Cursor AI or Gemini CLI / Google OpenCode Minimax.
  5. Create a new empty folder on your computer named after your domain (e.g., realonlineruler.com).
  6. Drag this folder into VS Code to open it.
  7. Create a GitHub Repository:
    1. Go to github.com and sign in (create a free account if needed).
    2. Click the + icon (top-right) → New repository. 💡 What is happening: A "repository" is like a project folder in the cloud. GitHub stores your code and lets Cloudflare auto-deploy it.
    3. Name it after your domain (e.g., realonlineruler.com), set it to Public, and do not initialize with a README, .gitignore, or license. 💡 Why Public? GitHub offers free private repos, but Cloudflare Pages can only connect to public repos on the free plan. Your code is already public on the internet via your website anyway.
    4. Click Create repository. Keep this browser tab open - you will need the remote URL in the next step.

Step 4: Initializing the Project

  1. Open the terminal in VS Code (Terminal > New Terminal).
  2. Run the following command to install Astro JS:
    npm create astro@latest .
    
    (Why this command? npm create fetches the latest Astro installation script. The . at the end tells it to install directly into your current empty folder rather than creating a new subfolder. Astro is chosen because it outputs static HTML, which is lightning-fast and highly favored by Google's search algorithms. 💡 What you will see: The terminal will show a progress bar downloading packages, then ask you a few questions. This is normal - it should take 20-60 seconds depending on your internet speed.)
  3. Press Enter for all defaults. Choose the "Basic" template.
  4. Say Yes when asked to initialize a Git repository. (Purpose: Git tracks every change you make to your files. If you or the AI break something, Git allows you to instantly revert back to a working version.)
  5. Enable TypeScript Strict Mode: Astro just created a tsconfig.json file. Open it in VS Code (you will see it in the file explorer on the left). Inside the compilerOptions section, add these four settings. Your file will look like this:
    {
      "compilerOptions": {
        "strict": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "noFallthroughCasesInSwitch": true
      }
    }
    (Why strict mode? TypeScript catches entire categories of bugs at compile time: null references, missing returns, unused variables that indicate dead code. Without strict: true, TypeScript allows many unsafe patterns that silently become runtime errors. Setting this up early means the AI generates cleaner code from the start.)
  6. Push to GitHub:
    1. Link your local project to the GitHub repository you created in Step 3:
      git remote add origin https://github.com/YOUR_USERNAME/YOUR_REPO_NAME.git
      
      (Replace YOUR_USERNAME and YOUR_REPO_NAME with your actual GitHub username and repository name. 💡 Where to find these: Your GitHub username is in the top-right corner of GitHub. Your repo name is what you named it in Step 3. The full URL is shown on your repo page in the "Quick setup" section - just copy it from there.)
    2. Push your initial code:
      git push -u origin main
      
      (What this does: git remote add connects your local folder to the GitHub repository. git push -u origin main uploads your code and sets the upstream, so future pushes can be just git push.)

Environment & Tooling Setup

Before handing the project to your AI agent, set up these foundational files that every production website needs. They keep secrets safe, catch bugs, and make your CI pipeline fast.

  1. Environment variables: Create a .env file (added to .gitignore — never commit it) for API keys, analytics tokens, and other secrets. Also create .env.example as a template with placeholder values so other developers (or future you) know what variables are needed:
    # .env.example — copy to .env and fill in your values
    VITE_SENTRY_DSN=
    VITE_ANALYTICS_ID=
    SENTRY_AUTH_TOKEN=
    

    For Vite projects, all client-exposed variables must start with VITE_ — variables without this prefix are only available during build (server-side). This is a security boundary that prevents accidentally leaking server secrets to the browser.

  2. Package security overrides: Vulnerable transitive dependencies are a common problem in Node.js projects. Use overrides in package.json to pin specific versions and patch known CVEs:
    {
      "overrides": {
        "brace-expansion": "5.0.6"
      }
    }

    Run npm audit regularly to check for vulnerabilities. When one is found, add it to overrides and commit the fix.

  3. Testing setup: Install Vitest with a DOM environment for component testing. Create a vitest.config.ts and a test setup file:
    npm install -D vitest happy-dom
    // vitest.config.ts
    import { defineConfig } from 'vitest/config'
    export default defineConfig({
      test: {
        environment: 'happy-dom',
        include: ['src/**/*.test.ts', 'src/**/*.test.tsx'],
      },
    })
    // src/test/setup.ts — runs before every test
    import '@testing-library/jest-dom'

    Add test scripts to package.json:

    "scripts": {
      "test": "vitest run",
      "test:watch": "vitest"
    }
  4. Cloudflare Pages config (optional alternative to manual UI): Instead of configuring build settings through the Cloudflare dashboard, create a wrangler.toml in your project root. This is declarative, version-controlled, and avoids manual click-through every time you set up a new project:
    name = "your-project-name"
    pages_build_output_dir = "dist"

    If you use this file, Cloudflare Pages will detect it and auto-configure the build output directory. You still need to connect the GitHub repo through the dashboard once, but all build settings come from this file.

Prompt the AI to initialize these:

Set up the project tooling: create a .env.example file with VITE_ prefixed placeholders for API keys, add npm overrides for known vulnerable packages, initialize Vitest with happy-dom for testing, and create a wrangler.toml for Cloudflare Pages build output configuration.

Step 5: Equipping the AI with Design Skills

Give the AI the context it needs to create professional, modern designs.

  1. Create a Design Reference File: This tells the AI what "good design" looks like. Create a new file in VS Code named @DESIGN.md in your project root folder (right-click in the VS Code file explorer on the left and select New File). Paste this into it:

    Design Requirements:
    - Clean, modern, professional look inspired by Vercel
    - Dark theme with accent colors (blue/purple gradient)
    - Mobile responsive - works on phones and tablets
    - Fast loading, minimal JavaScript
    - Accessible with proper contrast ratios
    - Rounded corners, subtle shadows, smooth transitions

    (Why? The AI needs written design guidelines because it cannot see. Without these instructions, the AI might generate ugly default styles. The @ prefix makes the file easy to reference in your prompts.)

  2. Launch Your AI Agent: In the VS Code terminal, start your CLI:

    • For Claude Code:
      claude
      
    • For Antigravity:
      antigravity
      
      (Alternatively, if using Cursor AI, just open the Composer/Chat panel).
  3. Install Skills / Provide Context:

    • If using Claude Code:
      claude mcp add web-design-guidelines
      claude mcp add tailwind-v4-docs
      
      (Source Links: Web Design Guidelines | Tailwind v4 Docs)
    • If using Antigravity: Antigravity natively supports advanced skills and plugins. You can just prompt it: "Use your skills for modern web design guidelines and Tailwind CSS v4."
    • If using Cursor AI: Use the @Docs feature and search for "Tailwind CSS v4" to give it context.

    (Purpose: AI models are trained on past data. Adding live context or MCP "skills" ensures the AI writes modern code instead of outdated versions, preventing frustrating bugs.)

  4. Astro JS MCP Server: Use the Astro JS MCP Server so the AI knows the latest Astro syntax.

    • For Claude Code: Run this command in your terminal:
      claude mcp add --transport http "Astro docs" https://mcp.docs.astro.build/mcp
      
    • For Antigravity: You can either simply instruct it via prompt: "Fetch context from the Astro Docs MCP server at https://mcp.docs.astro.build/mcp", OR add the server configuration directly to your MCP settings file:
      {
        "mcpServers": {
          "Astro docs": {
            "type": "http",
            "url": "https://mcp.docs.astro.build/mcp"
          }
        }
      }
      

Step 6: Generating the Website Structure

Paste the following master prompt into your AI agent (adapt the bracketed text for your tool):

I have initialized a new AstroJS project. Use your available web design skills, plugins, or read modern Tailwind v4 documentation. Also use @DESIGN.md. Keep the website design like Vercel.

Name: [Your Tool Name]
Domain: [yourdomain.com]

Create a [describe your tool in detail]. My competitor is [URL] - analyze it, identify its weaknesses,
and build a better version. Use MPA (multi-page application) architecture for best SEO.

Real-World Prompt Example (from realonlineruler.com):

Create an online ruler website that will have ruler on the edges, user can select where to place the ruler. we want these 3 calibration methods Method 1: Auto-Detect Device Method 2: Screen Diagonal Method 3: Credit Card Calibration

My competitor website is https://anruler.com/ and it have some features which we need and we need to make a website better than it. Give me ideas how to make it better. go on to this website and check what exactly we need to make. Do not copy design or ui from that website.

Iterating:

  1. Start Local Server: In the terminal, run:
    npm run dev
    
    (What this does: It starts a live preview of your website on your computer. Any changes the AI makes will automatically refresh in your browser so you can test them instantly.)
  2. Open http://localhost:4321 in your browser.
  3. Test Mobile Responsiveness: Right-click the browser page -> Inspect -> click the mobile/device icon. Ensure your layout doesn't break on phone screens. If elements go off-screen, tell the AI: "The website is not mobile responsive, fix the layout."
  4. Undoing Mistakes (Git): If the AI writes bad code and breaks your site, go to the Source Control tab in VS Code (the branch icon on the left) and click the Discard Changes (undo arrow) button to instantly reverse what the AI just did. Then you can clear your chat and try a new prompt.
  5. Save Tokens: Before asking the AI to add a new major feature, always type the following in the AI agent (or start a new chat session) to clear its memory. This prevents context overload and saves API tokens:
    /clear
    
  6. Must-Have Features: Ask the AI to add a Dark Mode toggle and ensure the layout is perfectly Mobile Responsive. (Why Dark Mode? Dark mode reduces eye strain in low-light environments, saves battery on OLED screens, and is now a standard user expectation: sites without it feel dated. Implementing it also teaches you CSS custom property theming, which makes future design changes much easier.)
    Add a Dark Mode toggle and ensure the layout is perfectly Mobile Responsive.

Accessibility Basics

Making your site accessible is not just about compliance; it improves SEO, broadens your audience, and is often required for AdSense approval. Ensure the AI includes these fundamentals:

  1. Semantic HTML: Use proper heading hierarchy (h1h2h3), <nav> for navigation, <main> for primary content, and <footer> for footer content. Screen readers rely on this structure.
  2. ARIA Attributes: Add aria-label to icon-only buttons, aria-expanded to toggle/menu buttons, and role attributes where semantic HTML is insufficient.
  3. Focus Management: Ensure all interactive elements are keyboard-focusable (using tabindex where needed) and visible focus indicators are present (:focus-visible styles).
  4. Alt Text: Every <img> needs descriptive alt text. Inform the AI: "Add descriptive alt text to all images based on their content."
  5. External Links: All links that open in a new tab must have rel="noopener noreferrer" for security and aria-label indicating they open externally.

Prompt the AI:

Make the website fully accessible: add proper ARIA labels, semantic HTML structure, keyboard navigation support, alt text on images, and rel="noopener noreferrer" on all external links.

CSS Architecture Best Practices

These patterns prevent common CSS bugs that plague AI-generated sites and ensure your styles hold up across browsers and devices:

  1. Sticky positioning fix: NEVER set body { overflow-x: hidden } — it breaks position: sticky by clipping the sticky element's container. If you need horizontal overflow hidden, set it on <html> instead:
    /* ✅ Correct */
    html { overflow-x: hidden; }
    
    /* ❌ Wrong — breaks sticky positioning */
    body { overflow-x: hidden; }
  2. Respect user motion preferences: Some users have vestibular disorders where animations cause dizziness. Wrap all animations in a prefers-reduced-motion media query to disable them when the user requests it:
    @media (prefers-reduced-motion: reduce) {
      html { scroll-behavior: auto; }
      *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
      }
    }
  3. Print styles: Users will print your pages. Hide interactive elements (checkboxes, buttons, nav), force light backgrounds and black text, and collapse expandable sections:
    @media print {
      nav, button, .no-print, input[type="checkbox"] { display: none !important; }
      body { background: #fff !important; color: #000 !important; }
      a { color: #000 !important; }
      pre { background: #f5f5f5 !important; border: 1px solid #ddd; }
      .collapsed-section { max-height: none !important; opacity: 1 !important; }
    }
  4. CSS variable fallbacks: Older browsers (Internet Explorer, old Safari) don't support CSS custom properties. Always provide a fallback value so the site remains usable:
    .card {
      background: #0a1122;               /* fallback for old browsers */
      background: var(--bg-secondary);   /* modern override */
      color: #94a3b8;                    /* fallback */
      color: var(--text-secondary);      /* modern override */
    }
  5. Responsive grids without media queries: Use auto-fill / auto-fit with minmax() to create responsive layouts that automatically adjust to container width:
    .card-grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
      gap: 1.5rem;
    }

Prompt the AI to apply these CSS improvements:

Apply these CSS fixes: move overflow-x: hidden from body to html, add a prefers-reduced-motion media query that disables all animations, add @media print styles that hide interactive elements, add CSS variable fallbacks for older browsers, and use auto-fill/minmax for responsive grids.

Reusable Component Patterns

These battle-tested component patterns solve common UX problems that AI code often gets wrong:

IntersectionObserver Lazy Loading

Instead of React's built-in lazy() (which loads when the component mounts, not when it's visible), use IntersectionObserver to trigger imports only when the user scrolls near the section. The rootMargin starts loading before the user reaches it, eliminating perceived delay:

const LazyOnViewport = ({ importFn, fallback }) => {
  const [Component, setComponent] = useState(null);
  const [error, setError] = useState(null);
  const ref = useRef(null);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;

    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting) {
          observer.disconnect();
          importFn()
            .then(mod => setComponent(() => mod.default))
            .catch(err => setError(err));
        }
      },
      { rootMargin: '300px 0px', threshold: 0 }
    );

    observer.observe(el);
    return () => observer.disconnect();
  }, [importFn]);

  if (error) return <div>Failed to load. <button onClick={() => setError(null)}>Retry</button></div>;
  return <div ref={ref}>{Component ? <Component /> : fallback}</div>;
};

Pair this with shimmer skeletons — animated gradient placeholders that match the section's dimensions — instead of a plain loading spinner:

.shimmer {
  background: linear-gradient(90deg, rgba(255,255,255,0.04) 25%, rgba(255,255,255,0.08) 50%, rgba(255,255,255,0.04) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
  border-radius: 12px;
}
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

FAQ Accordion

A proper FAQ accordion avoids display: none (which breaks height transition). Instead, use a max-height transition for smooth animation, close the current item when opening another, and add a hover timer for a premium feel:

const FaqAccordion = ({ items }) => {
  const [openIndex, setOpenIndex] = useState(null);
  const hoverTimer = useRef(null);

  return items.map((item, i) => (
    <div
      key={i}
      className="faq-item"
      onMouseEnter={() => {
        hoverTimer.current = setTimeout(() => setOpenIndex(i), 1000);
      }}
      onMouseLeave={() => clearTimeout(hoverTimer.current)}
    >
      <button
        className="faq-question"
        onClick={() => setOpenIndex(openIndex === i ? null : i)}
        aria-expanded={openIndex === i}
      >
        {item.question}
      </button>
      <div
        className="faq-answer"
        style={{
          maxHeight: openIndex === i ? '500px' : '0',
          overflow: 'hidden',
          transition: 'max-height 0.35s ease',
        }}
      >
        <p>{item.answer}</p>
      </div>
    </div>
  ));
};

Copy-to-Clipboard

Every code block and contact detail should have a copy button. Use navigator.clipboard.writeText() with a brief "Copied!" feedback state:

const CopyButton = ({ text }) => {
  const [copied, setCopied] = useState(false);

  const copy = async () => {
    try {
      await navigator.clipboard.writeText(text);
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch (err) {
      console.error('Copy failed:', err);
    }
  };

  return (
    <button className="copy-btn" onClick={copy}>
      {copied ? 'Copied!' : 'Copy'}
    </button>
  );
};

Prompt the AI to implement these patterns:

Implement these component patterns: lazy-load sections with IntersectionObserver using shimmer skeleton placeholders and a 300px rootMargin, build a FAQ accordion with smooth max-height transitions and hover-to-auto-open, and add copy-to-clipboard buttons on all code blocks and contact information.

Multi-Theme System (Advanced)

🟡 OPTIONAL - SKIP THIS for now. Your site works fine with a simple dark/light mode. This advanced multi-theme system (dark, light, matrix, cyberpunk) is something you can add after your site is live. Do not get stuck here.

Beyond basic dark/light mode, you can build a full multi-theme system that lets users choose their preferred look (dark, light, matrix, cyberpunk, etc.). This sets your micro-tool apart from competitors and creates a memorable user experience.

  1. CSS Custom Properties: Define all colors as CSS variables in :root, then override them per theme:
    :root {
      --bg-color: #040914;
      --text-primary: #f8fafc;
      --accent-color: #00f0ff;
      --accent-rgb: 0, 240, 255;
    }
    
    [data-theme="matrix"] {
      --bg-color: #000000;
      --text-primary: #00ff41;
      --accent-color: #00ff41;
      --accent-rgb: 0, 255, 65;
    }
    
    [data-theme="light"] {
      --bg-color: #ffffff;
      --text-primary: #0f172a;
      --accent-color: #0284c7;
      --accent-rgb: 2, 132, 199;
    }
  2. The RGB Trap: Every CSS variable that uses rgba() (like rgba(var(--accent-rgb), 0.4)) must be redefined in every theme block. A missing variable silently falls back to the default theme, which looks broken. Always verify all theme blocks have the complete set of variables.
  3. Dark Reader Prevention: Browser extensions like Dark Reader will override your carefully designed themes. Add this meta tag to your <head> to prevent it:
    <meta name="darkreader-lock" />
  4. localStorage Persistence: Save the user's theme choice and restore it before the first paint to prevent Flash of Unstyled Content (FOUC):
    <script>
      (function() {
        var t = localStorage.getItem('site_theme');
        if (t) document.documentElement.setAttribute('data-theme', t);
      })();
    </script>
  5. Theme Toggle UI: Add a button in your navbar that cycles through themes or toggles dark/light. Save the selection to localStorage on each change.
  6. color-scheme Meta Tag: Add <meta name="color-scheme" content="light dark" /> to your <head>. This tells the browser your site natively supports both color schemes. Without it, Chrome may forcibly override your custom themes with its own dark mode algorithm, breaking your carefully designed colors.
  7. Prevent Chrome Force Dark: Chrome has a built-in "Force dark mode" feature that ignores your CSS variables. Add this inline script early in <head> to opt out:
    <script>document.documentElement.style.setProperty('forced-color-adjust', 'none');</script>
  8. theme-color Meta Tag: Set the browser chrome color per theme. This changes the address bar and tab strip color on mobile browsers. Include it dynamically:
    <script>
      (function() {
        var meta = document.createElement('meta');
        meta.name = 'theme-color';
        // Match your default theme's background color
        meta.content = '#040914';
        document.head.appendChild(meta);
      })();
    </script>
    On theme change, update meta.content to the new theme's --bg-color.
  9. Smooth Theme Transitions (Advanced): CSS-only transitions often flicker because the browser paints before the new variables apply. Use a requestAnimationFrame (RAF) state machine for paint-guaranteed crossfades:
    // Returns an inline style object that blocks rendering until the transition starts
    function getThemeTransitionStyle(isTransitioning) {
      if (!isTransitioning) return {};
      return {
        animation: 'themeFade 0.45s cubic-bezier(0.25, 0.8, 0.25, 1)',
        // The keyframes simply crossfade opacity
      };
    }
    // RAF double-tick guarantees the browser has painted the new theme colors
    // before the reveal animation starts

Performance Optimization via Code Splitting

⚠️ For React, Vue, or Svelte SPAs only. Astro generates static HTML pages by default - there is hardly any JavaScript to split. If you are using Astro (as this guide recommends), you can skip this entire section. This is only relevant if you chose a JavaScript framework like React.

For sites built with frameworks (React, Vue, Svelte), loading all JavaScript upfront slows down the initial page load. Split your code so only essential code loads first:

  1. Lazy-Load Non-Critical Sections: Use React.lazy() or dynamic import() for sections below the fold (e.g., testimonials, blog, FAQ). This shrinks the initial bundle by 30-50%.
  2. Priority-Based Loading: Load the section the user navigated to (via URL hash) immediately. Load remaining sections in priority order with staggered delays to avoid network congestion:
    // Priority order: lower number = loads sooner
    const priorityMap = {
      projects: 1,
      education: 2,
      recommendations: 3,
      resume: 4,
    };
  3. Manual Chunking: Split vendor libraries into separate files so they cache independently:
    // vite.config.ts
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('react-dom')) return 'vendor-react';
          if (id.includes('framer-motion')) return 'vendor-motion';
          if (id.includes('react-icons')) return 'vendor-icons';
        }
      }
    }
  4. IntersectionObserver Strategy: Trigger section loading when the user scrolls near it, using the browser's IntersectionObserver API. This defers all non-visible code until needed.
  5. Measure the Impact: Open Chrome DevTools → Network tab → check DOMContentLoaded and Load timings before and after implementing code splitting. The difference is often 2-3x faster initial load.

Prompt the AI:

Split the website into lazy-loaded sections. The hero, about, and experience sections should load immediately. Projects, education, recommendations, and resume should use dynamic imports.

Critical Rendering Path Optimizations

Beyond code splitting, these techniques improve how quickly the browser renders the initial viewport — directly impacting Core Web Vitals (LCP, FCP, TBT) and Google rankings:

  1. Preload the LCP image: The Largest Contentful Paint is often the hero image. Preload it in <head> with fetchpriority="high" so the browser discovers it immediately instead of waiting for the CSS to load:
    <link rel="preload" as="image" href="/hero.webp" type="image/webp" fetchpriority="high" />
  2. Preconnect to critical origins: Add <link rel="preconnect"> for every third-party origin your page fetches from (Google Fonts, analytics, CDNs). This lets the browser start the DNS + TCP + TLS handshake earlier:
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  3. dns-prefetch for less critical origins: For APIs and third-party services used further down the page, use dns-prefetch — lighter than preconnect but still resolves DNS early:
    <link rel="dns-prefetch" href="https://api.example.com" />
    <link rel="dns-prefetch" href="https://cdn.thirdparty.com" />
  4. Prevent scroll restoration flash: Browsers try to restore scroll position on reload, which fights with your SPA initialization. Disable it before React mounts:
    <script>history.scrollRestoration='manual';window.scrollTo(0,0);</script>
  5. noscript branded fallback: Users with JavaScript disabled see nothing in a React/Vue/Svelte app. Provide a branded fallback with your name, key links, and contact info:
    <noscript>
      <div style="padding:2rem;background:#040914;color:#f8fafc;font-family:system-ui;text-align:center;">
        <h1>Your Site Name</h1>
        <p>Email: <a href="mailto:you@example.com" style="color:#00f0ff;">you@example.com</a></p>
        <p>GitHub: <a href="https://github.com/yourhandle" style="color:#00f0ff;">github.com/yourhandle</a></p>
      </div>
    </noscript>
  6. CSS minifier choice: If using Vite, prefer esbuild over lightningcss as the CSS minifier. lightningcss strips -webkit- prefixed properties like -webkit-backdrop-filter, breaking glassmorphism effects on Safari:
    // vite.config.ts
    build: {
      cssMinify: 'esbuild', // preserves -webkit-backdrop-filter
    }

Prompt the AI to apply these for your specific project:

Add preconnect links for Google Fonts, dns-prefetch for third-party APIs, preload the main hero image with fetchpriority high, add a branded noscript fallback, and disable automatic scroll restoration. Also set the CSS minifier to esbuild in vite.config.ts.

Step 7: Branding and Analytics

Make the site look legitimate and trackable.

  1. Logo & Favicon:
    1. Go to logofa.st and generate a clean icon (use a simple preset and customize colors). Download it.
    2. Go to Real Favicon Generator, upload your logo, and download the generated zip file.
    3. Extract the files and move them into your project's /public folder.
    4. Delete the default Astro favicon.svg file.
    5. Prompt the AI:
      I have added new favicon files to the public folder. Please add the HTML favicon link tags to the global site header.
  2. Color Palette: Ensure your tool uses modern colors (avoid plain #FF0000 red, use HSL tailored colors).
  3. Analytics: Go to Google Analytics.
    1. Create a new web property and copy your unique tracking script/tag.
    2. Prompt the AI:
      Add this Google Analytics tracking code to the global site header.
    3. Once added, push to GitHub to deploy: git add . && git commit -m "Add Google Analytics" && git push so the tracking goes live.

    (⚠️ Important: Cloudflare Pages automatically injects basic edge analytics. If you add Google Analytics too, you might see different numbers and could double-count visits. See Common Pitfall #5 below for details. For now, adding Google Analytics is fine -- just do not add two GA snippets.)

Step 8: On-Page SEO Optimization

Google needs text to understand what your tool does. Pure tool pages don't rank.

  1. Run this SEO prompt in the AI (remember to run /clear first to save tokens):
    Do the on-page SEO of this website for:
    Main Keyword: [your keyword]
    Supporting Keywords: [comma-separated list of keywords you found in Ahrefs]
    
    Add these SEO essentials to the global site header:
    1. A `` tag to prevent duplicate content penalties.
    2. Full Open Graph meta tags including `og:image:width` (1200), `og:image:height` (630), `og:image:type` (image/jpeg), and `og:image:alt` for social share previews.
    3. `twitter:creator` with your @handle and a proper `twitter:card` tag.
    4. ``.
    
    Write 600 words of highly optimized, user-friendly text about the tool on the home page for SEO purposes. Make sure the page URL structure is clean.
    

Step 9: Adding an SEO-Friendly FAQ Section

This captures long-tail search traffic and triggers Google Rich Snippets.

Prompt the AI:

Add an SEO-friendly FAQ section to the homepage using JSON-LD structured data. Use these exact questions:
[Paste the list of questions you copied from Ahrefs (https://ahrefs.com/keyword-generator) and Google's (https://google.com) "People Also Ask"]

Example JSON-LD Format the AI should generate:

<script type="application/ld+json">
  {
    "@context": "https://schema.org",
    "@type": "FAQPage",
    "mainEntity": [
      {
        "@type": "Question",
        "name": "Can I use my phone as a ruler?",
        "acceptedAnswer": {
          "@type": "Answer",
          "text": "<p>Yes, by using the screen diagonal or credit card calibration methods, you can accurately use your phone screen as a ruler.</p>"
        }
      }
    ]
  }
</script>

(Why JSON-LD Structured Data? JSON-LD is a machine-readable code format that Google understands perfectly. Instead of just displaying text on a screen, JSON-LD explicitly tells Google "this page has an FAQ section" using schema.org vocabulary. This often results in Google showing your questions directly in search results as rich snippets, expanding them inline so users see answers without clicking. Beyond FAQs, you can also use JSON-LD for breadcrumbs, reviews, how-to guides, and articles to boost your site's search visibility.)

JSON-LD Structured Data: Person / Organization Schema

Beyond FAQPage, the single most impactful structured data type for a personal site or portfolio is the Person schema. It tells Google who you are, what you do, where you work, and links to all your profiles — directly influencing Knowledge Panel results and E-E-A-T signals.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Person",
  "name": "Your Full Name",
  "jobTitle": "Senior Software Engineer",
  "url": "https://yourdomain.com/",
  "sameAs": [
    "https://www.linkedin.com/in/yourprofile/",
    "https://github.com/yourhandle",
    "https://twitter.com/yourhandle"
  ],
  "email": "mailto:you@example.com",
  "knowsAbout": ["Java", "Spring Boot", "React", "TypeScript"],
  "worksFor": [
    { "@type": "Organization", "name": "Current Company" }
  ]
}
</script>

Prompt the AI to add this to your site's global header:

Add JSON-LD Person structured data to the site header with my name, job title, LinkedIn, GitHub, Twitter profiles, email, skills, and current employer.

For a business or micro-tool site, replace Person with Organization or WebSite schema to describe the business behind the tool. If you have both a personal brand and a business, add both — they can coexist in separate <script> blocks.

Step 10: Essential Pages for Compliance, Trust & SEO

Google AdSense will reject your site without standard legal pages. Beyond AdSense, these pages serve critical purposes for every website. Below is what each file does, why it matters, and how to create them.

Legal & Trust Pages

Page What It Is Why You Need It
Privacy Policy A legal document explaining what personal data your site collects (cookies, analytics, contact forms) and how you use it. Required by law (GDPR in EU, CCPA in California). Google AdSense mandates it. Failing to have one can result in fines.
Terms & Conditions Rules users agree to by using your site: copyright terms, liability disclaimers, acceptable use policy. Protects you legally. Required by AdSense. Without it, users could misuse your tool or claim damages.
About Us A page describing who built the site and why. Builds user trust. Google uses E-E-A-T (Experience, Expertise, Authoritativeness, Trustworthiness) as a ranking signal: an About page proves these.
Contact Us A way for users and legal authorities to reach you (email, form, or address). Legitimacy signal. Required by GDPR (must have a contact point). Users need to report issues or bugs.

Technical SEO Files

File What It Is Why You Need It
sitemap.xml An XML file listing every page on your site with metadata (last updated, priority, change frequency). Google''s primary way of discovering your pages. Without it, some pages may never get indexed, especially on new sites.
robots.txt A text file that tells search engine crawlers which URLs they can and cannot access. Prevents crawlers from wasting quota on admin pages or duplicate content. Must reference your sitemap URL.
404 Error Page The page shown when a user visits a URL that doesn''t exist. Retains users who hit broken links; without it they see a browser error page and leave immediately. A good 404 page guides users back to useful content. Make it engaging — add ambient particle animations, quick-link buttons to main sections, and a prominent "Back to Home" button. A boring 404 page still loses users.
500 Error Page The page shown when the server encounters an internal error. Professional polish. When things break (and they will), a branded 500 page is far better than a raw error message.

robots.txt Content

Your robots.txt file lives at the root of your website's source folder (/public for Astro/static sites, /public for any framework). It should explicitly invite all crawlers to index every page. Your native file should look exactly like this:

User-agent: *
Allow: /
Sitemap: https://your-domain.com/sitemap.xml

Once you have this file in your project's /public folder and deployed the site to Cloudflare Pages, you must disable Cloudflare's own robots.txt generation so they don't override your native file. See Step 12: Cloudflare Visibility & AI Crawler Configuration for details.

Additional Essential Files

Beyond the pages above, these files are critical for every production website:

File What It Is Why You Need It
Favicon A small icon that appears in browser tabs, bookmarks, and search results. Brand recognition. A missing favicon looks unprofessional. Google also displays it next to your site in search results.
OG Meta Tags (Open Graph) HTML meta tags that control how your page appears when shared on social media (Twitter, WhatsApp, LinkedIn). Without OG tags, social shares show a generic title and no image, killing click-through rates. Essential for any site that gets shared.
ads.txt A text file that declares which ad networks are authorized to sell ads on your site. Required by Google AdSense. Prevents unauthorized ad fraud. Without it, AdSense earnings are blocked.
_redirects (Cloudflare Pages) or .htaccess (Apache) A configuration file that defines URL redirect rules and SPA fallback behavior. Essential for single-page apps (React, Vue, Svelte) to prevent 404 errors on page refresh. Also used for permanent redirects without code changes.
_headers (Cloudflare Pages) A configuration file that sets HTTP security and indexing headers on specific paths. Used to block the free .pages.dev subdomain from being indexed (prevents duplicate content SEO penalty), and to set security headers like X-Content-Type-Options.
  1. Run this prompt in the AI to generate all pages and files:
    Create these pages as separate MPA routes for best SEO:
    - Privacy Policy (with GDPR/CCPA compliance language)
    - Terms & Conditions
    - About Us
    - Contact Us (with a working contact form or email link)
    
    Make these pages clearly visible and linked in the home page footer and header.
    Also, generate a custom 404 error page, a 500 error page, a sitemap.xml, and a robots.txt file that links to the sitemap.
    
    Add proper OG meta tags to the global site header and generate a favicon package in the /public folder.
    
  2. Verify each file was created correctly in your /public and /src/pages folders:
    1. Privacy Policy page (GDPR/CCPA compliant)
    2. Terms & Conditions page
    3. About Us page
    4. Contact Us page with working form or email link
    5. Custom 404 error page that guides users back to useful content
    6. Custom 500 error page for server errors
    7. sitemap.xml listing every page with metadata
    8. robots.txt referencing the sitemap URL
    9. OG meta tags in global site header for social sharing
    10. Favicon package in /public folder
    11. ads.txt file for AdSense authorization

    Only if you used React, Vue, or Svelte (not Astro):

    1. _redirects file for SPA routing
    2. _headers file with security headers configuration

    (Why? Astro generates static HTML files, so there is no SPA refresh issue. You still need _headers for security, but it is covered in Step 11 below.)

Step 11: Deploying to Cloudflare Pages

Cloudflare Pages provides free, lightning-fast hosting for static sites. The best approach is to connect your GitHub repository so every push deploys automatically.

⚠️ CRITICAL: Select "Pages" not "Worker"
When you click Create application, Cloudflare's default tab is Worker (not Pages). You must manually switch to the Pages tab before proceeding. Creating a Worker instead of Pages will not work for a static website - this is an extremely common mistake that can waste a lot of your time.

💡 Workers vs. Pages - Which one for your site?
Cloudflare Pages is for hosting complete websites - HTML, CSS, JS, and static assets. It connects directly to your GitHub repo and auto-deploys. This is what you want for your Astro micro-tool site.
Workers is for writing small serverless functions (APIs, redirects, backend logic) that run on Cloudflare's edge. It is not for hosting a full website.
Rule of thumb: If you have a Git repo with HTML files → use Pages. If you are writing a single JavaScript function to handle API requests → use Worker.

  1. Connect GitHub to Cloudflare Pages:
    • Go to Cloudflare Dashboard and log in (create a free account if you don't have one).
    • Two ways to get started:
      • Via homepage (+Add button): On your Cloudflare home screen, click the +Add button (usually top-right) and select Pages from the dropdown - this directly opens the Pages creation flow.
      • Via sidebar menu: In the left sidebar, go to Workers & PagesOverview → click Create application → immediately switch to the Pages tab (the default is Worker).
    • Click Connect to Git.
    • If prompted, authorize Cloudflare to access your GitHub account (you can grant access to all repos or just this one).
    • Select your repository (e.g., realonlineruler.com) and click Begin setup.
  2. Configure Build Settings:
    • Project name: Auto-filled from your repo name (you can change it).
    • Production branch: main.
    • Build command: npm run build.
    • Build output directory: dist.
    • Leave environment variables empty for now.
    • Click Save and Deploy.
  3. Cloudflare will clone your repository, run npm run build, and deploy the output to a free *.pages.dev subdomain. This takes about 1–2 minutes.
  4. Auto-Deploy on Future Pushes: From now on, every time you push changes to GitHub:
    git add . && git commit -m "your update message" && git push
    
    Cloudflare automatically detects the push, rebuilds your site, and deploys the update - no manual deploy commands needed.

SPA Routing (For React, Vue, Svelte Sites)

✅ Astro MPA users: SKIP THIS. Astro generates real HTML files for every page (/privacy/index.html, /about/index.html, etc.). There is no 404-on-refresh issue. You do NOT need _redirects.

If you built a single-page application (SPA) instead of a multi-page static site, navigating directly to a URL like yoursite.com/about or refreshing the page will return a 404, because the server has no real about.html file. The SPA handles routing in the browser, not the server.

The fix: Create a file called _redirects in your /public folder with this exact content:

/* /index.html 200

What this does: It tells Cloudflare Pages to serve index.html for every unknown URL path. Your JavaScript app reads the URL and displays the correct page. The 200 status tells search engines it is a successful response (not a redirect), which is critical for SEO.

404 safety net: Even with SPA fallback, always create a public/404.html file. It only shows in rare edge cases (e.g., a bot hitting a truly invalid path), but it is far better than a raw browser error.

Security Headers and HTTP Configuration

Beyond redirects, every production site should set proper security and caching headers. Create a file called _headers in your /public folder:

# Long-lived asset cache (JS/CSS/fonts with content hash in filename)
/assets/*
  Cache-Control: public, max-age=31536000, immutable

# Static media: cache with stale-while-revalidate so stale copies serve instantly
# while the browser fetches a fresh version in the background
/avatars/*
  Cache-Control: public, max-age=604800, stale-while-revalidate=86400

/favicon.*
  Cache-Control: public, max-age=604800, stale-while-revalidate=86400

/og-banner.* /og-image.*
  Cache-Control: public, max-age=604800, stale-while-revalidate=86400

# Service worker: NEVER cache — must always get the latest version
# Service-Worker-Allowed grants it control over the entire site scope
/sw.js
  Cache-Control: no-store, no-cache, must-revalidate
  Service-Worker-Allowed: /

# Web app manifest: short TTL so icon/name changes propagate quickly
/manifest.webmanifest /manifest.json
  Cache-Control: public, max-age=3600, must-revalidate

# Everything else: no cache, security headers applied globally
/*
  Cache-Control: public, max-age=0, must-revalidate
  X-Content-Type-Options: nosniff
  X-Frame-Options: DENY
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()
  Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

# Block the free .pages.dev subdomain from search engines
https://*.pages.dev/*
  X-Robots-Tag: noindex, nofollow

What these do: Built assets (JS/CSS) get long-term immutable caching for instant repeat visits. Static media (icons, OG images) use stale-while-revalidate — immediately serve the stale copy while refreshing in the background. The service worker (sw.js) is explicitly set to no-store so updates roll out instantly, with Service-Worker-Allowed: / giving it control over the entire site scope. The web app manifest gets a short TTL so icon/name changes propagate quickly without requiring a full SW update. Security headers prevent clickjacking (X-Frame-Options), enforce HTTPS (Strict-Transport-Security), and restrict browser features (Permissions-Policy). The X-Robots-Tag rule blocks the free .pages.dev subdomain from being indexed, preventing duplicate content SEO penalties.

Service Workers and PWA (Progressive Web App)

🟡 OPTIONAL - SKIP FOR NOW. PWA lets users install your site on their phone like an app. It is not required for Google ranking or AdSense. Come back to this after your site is earning money.

Make your site installable on users' home screens and work offline. This gives your micro-tool a native app experience without the App Store.

  1. Install a PWA plugin: If using Vite, run npm install vite-plugin-pwa. For Next.js, use next-pwa.
  2. Create a manifest file: Create public/manifest.webmanifest with your app metadata. SVG icons are preferred over PNG (smaller, resolution-independent, no pixelation on retina displays):
    {
      "name": "Your Site Name - Tagline",
      "short_name": "Short Name",
      "description": "SEO-friendly description of your tool",
      "start_url": "/",
      "display": "standalone",
      "background_color": "#040914",
      "theme_color": "#040914",
      "lang": "en",
      "scope": "/",
      "orientation": "portrait-primary",
      "icons": [
        { "src": "/icons/icon-192.svg", "sizes": "192x192", "type": "image/svg+xml", "purpose": "any" },
        { "src": "/icons/icon-512.svg", "sizes": "512x512", "type": "image/svg+xml", "purpose": "any" },
        { "src": "/icons/icon-maskable.svg", "sizes": "512x512", "type": "image/svg+xml", "purpose": "maskable" }
      ]
    }

    Include a maskable icon (with padding/safe-zone) for Android adaptive icons — they get cropped into circles/squirccles by the OS. The purpose: "maskable" field tells the browser to apply the safe-zone cropping.

  3. Add Apple-specific meta tags for iOS Safari (which ignores the standard manifest):
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
    <meta name="apple-mobile-web-app-title" content="Short Name" />
    <link rel="apple-touch-icon" href="/icons/icon-192.svg" />
    <link rel="manifest" href="/manifest.webmanifest" />

    Without these, iOS users get no splash screen, a generic icon, and the browser chrome (URL bar) stays visible — defeating the app-like experience.

  4. Configure runtime caching with multiple strategies: Different APIs need different caching approaches. Add these to your PWA plugin config:
    runtimeCaching: [
      {
        // NetworkFirst: Try network first, fall back to cache.
        // Best for API calls where freshness matters.
        urlPattern: /^https:\/\/api\.example\.com\/.*/i,
        handler: 'NetworkFirst',
        options: {
          cacheName: 'api-cache',
          expiration: { maxEntries: 50, maxAgeSeconds: 3600 },
          networkTimeoutSeconds: 5,
        },
      },
      {
        // StaleWhileRevalidate: Serve cached instantly, refresh in background.
        // Best for avatars, CDN assets, media files.
        urlPattern: /^https:\/\/cdn\.example\.com\/.*/i,
        handler: 'StaleWhileRevalidate',
        options: {
          cacheName: 'cdn-cache',
          expiration: { maxEntries: 100, maxAgeSeconds: 604800 },
        },
      },
    ]

    The Service Worker (SW) is auto-generated by the plugin. To ensure the SW always updates instantly when you deploy new code, add these configuration flags:

    // vite.config.js / next.config.js
    VitePWA({
      registerType: 'autoUpdate',     // SW updates silently in background
      workbox: {
        skipWaiting: true,            // Activate new SW immediately
        clientsClaim: true,           // Take over all open tabs
        cleanupOutdatedCaches: true,  // Remove old cache versions
      },
    })
  5. Test offline mode: In Chrome DevTools → ApplicationService Workers, check Offline and refresh. Your tool should still load core functionality. Verify the manifest loads at yourdomain.com/manifest.webmanifest and shows the install prompt (a + icon in the address bar).

Why PWA matters: Google rewards fast, installable experiences. A PWA-enabled micro-tool loads instantly for returning visitors. Users can add it to their home screen like a native app, increasing engagement and repeat usage, both positive signals for SEO.

Prompt the AI to set up PWA for your project:

Set up PWA for this project: create a manifest.webmanifest file in /public with SVG icons (192, 512, maskable), add Apple-specific meta tags for iOS, configure runtime caching with NetworkFirst for API calls and StaleWhileRevalidate for CDN assets, and enable auto-update service worker with skipWaiting and clientsClaim.

Branch Controls & Preview Deployments

🟡 SKIP THIS for your first site. You only need the main branch. Cloudflare Pages will auto-deploy everything you push to main. Branch previews are useful for teams, not for a single-person micro-tool project. Come back here later.

Cloudflare Pages can deploy different branches to different environments. This lets you test changes before they go live:

BranchDeploys ToWhen to Use
mainYour production domain (yourdomain.com)Only fully tested, ready-to-ship code
devA preview URL (project-name.pages.dev)Daily work-in-progress; test before merging to main
Feature branchesNo deployment (skip preview)Experimental or single-feature work

How to set up: In Cloudflare Dashboard → your project → SettingsBuilds & DeploymentsBranch Controls. Remove the wildcard * branch rule, then explicitly add only dev and main. This prevents every random branch from triggering a preview, saving build minutes.

GitHub Actions CI/CD Pipeline

⏭️ SKIP THIS for your first website. Cloudflare Pages already auto-deploys every time you push to GitHub (you set this up in Step 11). You do NOT need a separate CI/CD pipeline. Come back here once your site is live and you want extra pre-deploy checks.

Cloudflare Pages auto-deploys on every push, but what if your code has TypeScript errors or failing tests? Set up a GitHub Actions pipeline to catch problems before deployment:

  1. Create the workflow file: In your repo, create .github/workflows/ci.yml with this config:
    name: CI
    
    on:
      push:
        branches: [ main, master ]
      pull_request:
        branches: [ main, master ]
    
    jobs:
      build-and-lint:
        runs-on: ubuntu-latest
    
        steps:
        - name: Checkout code
          uses: actions/checkout@v4
    
        - name: Set up Node.js
          uses: actions/setup-node@v4
          with:
            node-version: 20
            cache: 'npm'
    
        - name: Install dependencies
          run: npm ci
    
        - name: Lint code
          run: npm run lint
    
        - name: Run tests
          run: npm test
    
        - name: Build project
          run: npm run build
  2. Push and verify: Commit and push the file. Go to your GitHub repo → Actions tab and confirm the workflow runs successfully.
  3. Add pre-commit hooks (optional): Run npx husky-init && npm install to set up Git hooks that run lint and tests before every commit, catching issues even earlier.
  4. Save build minutes: Add [skip ci] to commit messages for documentation-only changes (this is already covered in Common Pitfalls #8).
  5. Speed tip: The cache: 'npm' line caches node_modules between CI runs, cutting install time from ~60s to ~10s on repeat runs. Without it, every CI run downloads all dependencies from scratch.

(Why CI/CD matters: Cloudflare Pages only shows you build failures after a deploy attempt. GitHub Actions catches failures in the PR stage, before anything goes live. This is especially important when AI generates code that might have subtle TypeScript errors or broken imports.)

Step 12: Purchasing and Connecting Your Domain

Now that the site is fully built and deployed, buy your .com domain. Spaceship is the best overall choice - it's a modern platform (sister company of Namecheap) offering low prices on both registration and renewals, a clean dashboard without upsells, and free WHOIS privacy. Alternatives include Namecheap (reliable but high renewal costs), GoDaddy, BigRock, or Hostinger.

💡 Domain Purchasing Tips:

  1. Log in to Cloudflare > Go to Websites > Add a Site > Enter your new .com domain and select the Free plan.
  2. Configure DNS Records:
    • Delete any default presets you see (click the Actions button and select Delete).
    • Click the Add record button to add a placeholder record:
      • Type: A
      • Name: @
      • IPv4 address: 8.8.8.8
    • Press Save, then click Continue to activation.
  3. Update Name Servers:
    • Cloudflare will provide you with 2 nameservers.
    • Go to your domain registrar (e.g., Spaceship, BigRock, Namecheap). Tip: Search Google for "How to change Name Servers on [Your Registrar]" if you are unsure.
    • Replace the existing nameservers with the 2 you received from Cloudflare.
    • Wait for propagation. It generally takes 10-30 minutes (max 24-48 hours). Cloudflare will now manage your DNS configuration.
  4. Link to your Project: In Cloudflare, go to Workers & Pages > Select your project > Settings > Custom Domains > Add your .com domain and activate it (add the www version too).

Verify DNS Propagation

After updating nameservers at your registrar, DNS changes can take anywhere from 10 minutes to 48 hours to propagate worldwide. Verify before proceeding:

  1. Go to dnschecker.org or whatsmydns.net.
  2. Enter your domain name and select NS (nameserver) record type.
  3. If all locations show your two Cloudflare nameservers, propagation is complete. If some locations still show your old registrar''s nameservers, wait longer and check again.
  4. Tip: Run nslookup -type=ns yourdomain.com (or Resolve-DnsName -Name yourdomain.com -Type NS in PowerShell) for a quick local check.

Cloudflare Visibility & AI Crawler Configuration

Now that your site is deployed to Cloudflare Pages and DNS is resolving, optimize it for maximum discoverability by configuring Cloudflare's visibility settings. Perform the following steps in order.

Step 1: Prepare Your Native robots.txt

Before touching Cloudflare, ensure your project's /public directory contains a robots.txt that explicitly invites all crawlers. If you followed the Technical SEO Files section above (Step 10), yours should already have this content:

User-agent: *
Allow: /
Sitemap: https://your-domain.com/sitemap.xml

This file is now in your project source. The next step is to tell Cloudflare to stop overriding it.

Step 2: Configure Cloudflare to Respect Your Native File

  1. Log in to the Cloudflare Dashboard.
  2. From Account Home, go to Domains and select your domain.
  3. In your domain overview page, look at the right side of the page — you will see the DNS section and the Manage AI bot access option.
  4. In the DNS section, click Manage your robots.txt.
  5. Choose Disable robots.txt configuration.
  6. Click Save, then Apply.

Why: Cloudflare's default mode intercepts your native Allow: / and may accidentally set Disallow rules. By disabling their configuration, you ensure your absolute Allow remains and no search engines are accidentally blocked.

Step 3: Grant Unhindered AI Bot Access

  1. On the same domain overview page (right side), locate the Manage AI bot access section.
  2. Under Block AI training bots rule, select Do not block (allow crawlers).
  3. Click Change to confirm.

Why: Choosing "Block on all pages" stops AI assistants (Claude, ChatGPT, Gemini) at the network firewall. By selecting "allow", recruiters and others who use AI search tools or live-summarizing extensions can perfectly view and index your work.

Why This Specific Setup Works

This configuration acts as an open invitation. While a massive corporation would block these bots to save bandwidth, your priority is to be found by the right person using the right tool.

Setting Your Selection The Engineering Impact
Robots.txt Management Disable Cloudflare Override Keeps your native Allow: / active.
AI Training Bots Do Not Block Ensures AI tools can parse your tech and site description for talent search.
Edge Firewall Status Completely Open Bypasses Cloudflare's strict default changes for crawlers.

CRITICAL: Fix Duplicate Content SEO Penalty

Your site is now accessible at both yourdomain.com and yoursite.pages.dev. Google penalizes this duplicate content. (Note: If you decided to use Cloudflare Workers instead of Pages, you must explicitly disable the .workers.dev domain routing in your Cloudflare dashboard after connecting your .com domain).

  1. Create a file named _headers inside your project's /public folder.
  2. Add this exact text (replace with your actual .pages.dev URL):
    https://yoursite.pages.dev/*
      X-Robots-Tag: noindex
    
  3. Push the changes to GitHub. This tells Google bots to ignore the free subdomain and only index your .com domain:
    git add . && git commit -m "Add _headers to block pages.dev indexing" && git push
    
  4. Verify the Fix:
    • Open your .pages.dev URL in Google Chrome.
    • Press F12 to open Developer Tools, then navigate to the Network tab.
    • Refresh the page and click on your website's main request (usually the first item).
    • Look under the Headers section. You should see X-Robots-Tag: noindex (and often other security headers like x-content-type-options: nosniff). This confirms that the duplicate content issue is successfully fixed and Google will ignore this subdomain!

Step 13: Search Engine Submission

Force search engines to crawl your new site.

  1. Go to Google Search Console and add your domain property (e.g., realonlineruler.com - enter it exactly like that, without https:// or www).
    • Press Continue and copy the provided TXT record.
    • Go to your Cloudflare Dashboard > Click your domain name > DNS > Records.
    • Click Add record, set the Type to TXT, type @ in the Name field, and paste the copied TXT record into the Content field. Save it.
    • Go back to Google Search Console, wait 10-15 seconds, and click Verify.
  2. Submit Your Sitemap: Go to the Sitemaps section.
    • How to find your sitemap URL: Open your browser and go to your website's robots file (e.g., https://realonlineruler.com/robots.txt).
    • Look at the very bottom of the text file and copy the sitemap URL (it will usually look like https://realonlineruler.com/sitemap-index.xml).
    • Paste that exact URL into the "Add a new sitemap" field in Google Search Console and click Submit.
    • (Note: Google Search Console often initially shows the status as "Couldn't fetch". Simply refresh the page, and the status should update to "Success".)
  3. Request Indexing: Use the URL Inspection tool.
    • Click the search bar at the top, type your full domain (including https://, e.g., https://realonlineruler.com), and press Enter.
    • Once the page loads, click the Request Indexing button. This explicitly signals Google that your new site is ready to be crawled.
  4. Submit to Bing Webmaster Tools: Go to bing.com/webmasters/about.
    • Click Get Started and sign in with your Google account.
    • Click Import from Google Search Console, then click Import and wait for it to finish.
    • Go to Home > URL Submission > Submit URLs.
    • Paste your complete domain URL (e.g., https://realonlineruler.com) and click submit. (Why? Bing powers DuckDuckGo, Yahoo, and many AI search engines-it's highly worth the 5 minutes).
  5. Promotion: Share your tool on relevant Reddit communities, Quora answers, and social media to get initial human traffic, which signals value to Google.

Congratulations! Your website is now fully live and indexed.

⏳ Timeline Expectations:

Step 14: Performance Audit and Mobile Optimization

Ensure your website is optimized for speed, which is a major Google ranking factor (Core Web Vitals).

  1. Run a PageSpeed Insights Audit:
    1. Once your website is live and indexed, go to PageSpeed Insights (pagespeed.web.dev).
    2. Enter your live website URL (e.g., https://realonlineruler.com) and click Analyze.
    3. Let the tool analyze the mobile and desktop performance of your site. Once complete, copy the analysis URL from your browser's address bar (e.g., https://pagespeed.web.dev/analysis/...).
  2. Leverage your AI Agent for Optimization:
    1. Paste the PageSpeed Insights analysis URL directly into your AI coding assistant (e.g., Claude Code, Antigravity, or Cursor) and prompt it to optimize your site:
      Analyze the Lighthouse report for my live site from this link: [Paste PageSpeed URL]. Fix all highlighted issues to improve my mobile performance score. Focus on minimizing JavaScript bundle size, reducing main-thread blocking time, and optimizing critical rendering paths.
    2. The AI agent will analyze the report, locate performance bottlenecks (such as static/heavy library imports, non-lazy components, or heavy scroll animations), and implement techniques like dynamic SDK loading, section lazy-loading, code-splitting, or converting JS animations to hardware-accelerated CSS animations.
  3. Verify the Speed Gains: Push the AI-optimized changes to GitHub to trigger a new Cloudflare Pages deployment. After a few minutes, re-run the PageSpeed Insights analysis. Your mobile score should show significant improvement (ideally exceeding 90).

Step 15: Verify Google Analytics is Working

Back in Step 7, you added the Google Analytics tracking code to your website. Now that your site is fully live on the internet, you must verify the installation to track your users.

  1. Go back to Google Analytics.
  2. On your installation/data stream screen, click the Test Installation button.
  3. Since your site is now live and the code was deployed, Google will successfully detect the tag.
  4. Click Confirm > Next > Continue to Home > Save.
  5. You will now see your main dashboard. Click on Real-time to see active users currently on your site, what countries they are visiting from, and other live data.

Step 16: Error Monitoring and Crash Reporting

🟡 OPTIONAL - SKIP FOR NOW. For a simple micro-tool, you probably do not need Sentry error monitoring. Your users will email you if something breaks. Come back and set this up after your site is getting traffic.

Your website will have bugs, especially if AI generated most of the code. Catch them before users report them. Error monitoring tools capture JavaScript exceptions, network failures, and unexpected state so you can fix issues proactively.

  1. Choose a Monitoring Service: Sentry (free tier includes 5k events/month) is the industry standard. Create a free account and a new project for JavaScript.
  2. Install the SDK:
    npm install @sentry/react
    
  3. Initialize in your app entry point:
    import * as Sentry from '@sentry/react'
    
    Sentry.init({
      dsn: 'YOUR_DSN_HERE',
      tracesSampleRate: 1.0,
    })
    
    // Optional: mask potentially sensitive URLs from breadcrumbs
    Sentry.addGlobalEventProcessor((event) => {
      if (event.request?.url) {
        event.request.url = event.request.url.replace(/token=.*/, 'token=REDACTED');
      }
      return event;
    });
  4. Add Error Boundaries: Wrap individual page sections so one crash doesn't break the entire site:
    import { ErrorBoundary } from '@sentry/react'
    
    <ErrorBoundary fallback={<div>This section failed to load</div>}>
      <Projects />
    </ErrorBoundary>
  5. Global Unhandled Rejection Handler: Catch async errors that Sentry's automatic integration might miss:
    window.addEventListener('unhandledrejection', (event) => {
      Sentry.captureException(event.reason, {
        tags: { type: 'unhandledrejection' },
      });
    });
  6. Error Buffer Pattern (Advanced): Errors that happen before Sentry finishes initializing (e.g., in inline <script> tags or early module imports) will be lost. Buffer them and replay after init:
    // Buffer to hold errors during SDK init
    const _errorBuffer = [];
    const _captureException = (err, ctx) => {
      _errorBuffer.push({ err, ctx });
    };
    
    // After Sentry.init, replay the buffer
    _errorBuffer.forEach(({ err, ctx }) => {
      Sentry.captureException(err, { tags: { fromBuffer: 'true', ...ctx } });
    });
    _errorBuffer.length = 0; // clear
    
    // Expose globally so any component can report errors
    window.captureException = (err, ctx) => _captureException(err, ctx);

    This also lets you expose window.captureException as a global helper, so any component can report errors without importing Sentry directly.

  7. Lazy Component Error Fallback: When using dynamic import() for lazy-loaded sections, the import might fail (network glitch, deploy mismatch). Wrap each dynamic import with an error state and a Retry button:
    const [error, setError] = useState(null);
    const [Component, setComponent] = useState(null);
    
    useEffect(() => {
      import('./SomeSection')
        .then(mod => setComponent(() => mod.default))
        .catch(err => { setError(err); window.captureException?.(err); });
    }, []);
    
    if (error) {
      return (
        <div>
          <p>Failed to load this section.</p>
          <button onClick={() => setError(null)}>Retry</button>
        </div>
      );
    }
  8. Test Your Setup: Trigger a test error to verify Sentry captures it:
    Sentry.captureException(new Error('Test error - remove before deploy'));

    Check your Sentry dashboard within 30 seconds. If the error appears, monitoring is working. Remove the test line after verification.

Pro tip: Set up email alerts in Sentry so you know the moment a user hits a bug. This turns a potential bad review into a fix deployed within minutes.

Step 17: Google AdSense Monetization

Do not apply for AdSense on day one. You need to wait at least 1 month before applying so your site has time to age and build trust. You MUST also ensure your Google Analytics shows you are getting a minimum of 10+ consistent organic users per day (check this in Analytics under Real-timeUsers). Applying too early with zero traffic will result in an instant rejection.

  1. Go to Google AdSense and log in.
  2. Go to Sites > Add new site and enter your domain name (without https:// or www, e.g., realonlineruler.com).
  3. Add the Script: Copy the provided AdSense code snippet.
    • Prompt the AI:
      Add this AdSense verification script to the global site header: [paste snippet]
    • Push to GitHub: git add . && git commit -m "Add AdSense script" && git push. Wait 10 seconds.
  4. Verify: Go back to AdSense, tick the box saying "I've placed the code", and click Verify.
  5. Click Next > Request Review.
  6. Consent Message: It will ask you to create a GDPR consent message for your website. Click on the first option provided by Google, then click Submit.
  7. Fixing the ads.txt Error: Your application is now submitted and will say "Getting Ready". However, you will likely see a warning saying ads.txt is Not Found.
    • To fix this, click on Verify Site Ownership.
    • Click on the ads.txt option and copy the publisher ID code it gives you.
    • Prompt the AI:
      Create an ads.txt file in the public directory with this content: [paste your AdSense publisher ID text]
    • Push again: git add . && git commit -m "Add ads.txt" && git push.
    • Wait a minute and refresh AdSense. (You can verify it works by visiting yourdomain.com/ads.txt in your browser).
  8. Wait for Approval: You will receive an email regarding your approval.
    • _(Note: Expect to be rejected your first few times! This is completely normal. If rejected, copy the exact issues they flag, paste them into your AI agent to fix, run git add . && git commit -m "Fix AdSense issues" && git push, and reapply. You have to be persistent).*
  9. Turn on Ads: Once approved (which might take a month of site aging), go to Ads > Auto Ads > Edit. Turn it ON, click Apply to Site, and Save.
    • Your ads will now be automatically placed, and your earnings will update daily!

Step 18: Domain Migration & SEO Transfer

If you need to change your website's primary domain, you must transfer search engine authority (link juice) and redirect users seamlessly without hurting your SEO.

🛠️ Interactive Migration Helper: Enter your domains and project target to dynamically customize the code snippets and terminal commands in this step:

⚠️ CRITICAL RULE: Never simply delete your old domain or remove the redirect after a few months. Keep the old domain active and redirecting for at least 12 months (ideally forever). If you let it expire, automated bots will buy it and host spam or malicious pages, ruining your old portfolio links on PDFs, resumes, and forum posts.

  1. DNS Setup on Cloudflare (New Domain):
    • Log in to your Cloudflare Dashboard and click + Add a site. Enter your new domain name newdomain.me and select the Free plan.
    • Cloudflare will scan for existing DNS records. Delete any default temporary records (such as your registrar's default parking page A records).
    • Click Continue to activation.

      💡 Trouble Tip: Deleting the default records will show a warning popup: "Without DNS records, Cloudflare is unable to activate your site..." This is completely normal. Click Confirm because Cloudflare Pages will handle mapping your records later.

    • Copy the two custom Cloudflare nameservers provided.
  2. Update Registrar Nameservers:
    • Log in to your domain registrar dashboard (e.g., Spaceship, Namecheap).
    • Go to your domain settings for newdomain.me, choose Custom Nameservers, delete the default ones, and paste the two Cloudflare nameservers.
    • Verify DNS propagation using tools like dnschecker.org or by running nslookup -type=ns newdomain.me in your terminal. Once active, the domain status in Cloudflare will turn green.
  3. Connect New Domain to Cloudflare Pages:
    • In the Cloudflare Dashboard, navigate to Workers & Pages → click your project → go to SettingsCustom Domains.
    • Click Set up a custom domain, enter your new root domain newdomain.me, and click Continue. Repeat the process for the www.newdomain.me subdomain.
    • 💡 Trouble Tip: If Cloudflare shows a "Verifying/Complete DNS setup" page and asks you to add records manually: Go to your new domain's DNS > Records tab in Cloudflare, click Add record, and add:
      • Type: CNAME | Name: @ | Target: yourproject.pages.dev | Proxy status: Proxied (Orange Cloud ON)
      • Type: CNAME | Name: www | Target: yourproject.pages.dev | Proxy status: Proxied (Orange Cloud ON)
      Then return to the Pages configuration tab and click Check DNS records.

  4. Remove Old Domain from Pages (CRITICAL):
    • On that same Pages Custom Domains settings page, locate your old domain olddomain.me and click the three dots next to it.
    • Click Remove domain (and remove the www.olddomain.me version as well).
    • ⚠️ Why this is critical: Cloudflare Pages hosting always takes priority over Redirect Rules. If your old domain olddomain.me is still mapped directly to Pages, Cloudflare will serve the website directly and bypass any redirect rules you create in the next step.

  5. Set Up the 301 Redirect Rule (Old Domain):
    • Switch to your old domain's (olddomain.me) site dashboard in Cloudflare.
    • Go to RulesRedirect Rules → click Create rule.
    • Fill in the fields:
      • Rule name: Permanent Domain Migration Redirect
      • When incoming requests match: Select All incoming requests
      • Then (Action): Select Dynamic Redirect
      • Expression: concat("https://newdomain.me", http.request.uri.path)
      • Status Code: Select 301 (Moved Permanently)
      • Preserve query string: Check the box to keep query parameters intact.
    • Click Deploy. Verify the redirect works by running curl -I http://olddomain.me (it should return 301 with the location header pointing to the new domain).
  6. Update Codebase Metadata & Cleanup:
    • In your source files, update all domain references to your new domain:
      • Canonical links (<link rel="canonical" href="https://newdomain.me/" />)
      • Open Graph tags (og:url, og:image, etc. pointing to newdomain.me)
      • Sitemap absolute URLs (in sitemap.xml pointing to newdomain.me)
      • Robots.txt sitemap path
      • JSON-LD structured data (update the url property, and add the old domain olddomain.me into the sameAs array as a historical alias).
    • 💡 Trouble Tip (Analytics Cleanup): If you use Cloudflare's Automatic setup for Web Analytics, Cloudflare automatically injects the tracking script. Go to your Pages dashboard settings, delete the environment variable VITE_CLOUDFLARE_ANALYTICS_TOKEN, and redeploy. This prevents double-tracking script conflicts on your new domain.

  7. Google Search Console Migration:
    • Add your new domain newdomain.me to Google Search Console as a Domain property (do not use URL prefix).

      💡 Trouble Tip: Since your domain is hosted on Cloudflare, Search Console will ask you to verify via Cloudflare. Simply authorize the popup; the verification completes automatically without needing manual copy-pasting of TXT strings.

    • Submit your new sitemap.

      💡 Trouble Tip: For Domain properties, Search Console requires you to type the complete URL (e.g. https://newdomain.me/sitemap.xml) to avoid "Invalid sitemap address" errors.

    • Go to your old domain's property (olddomain.me) in Search Console, go to SettingsChange of address.
    • Select your new domain newdomain.me, click Validate and Update, and ignore any warnings about sample redirects not being active yet. Click Confirm Move.
  8. Update External Profiles:
    • Update your website link in LinkedIn, GitHub profile, Twitter/X bio, resume PDF, and other professional pages to ensure recruiters are pointed to the new domain newdomain.me immediately.

Common Pitfalls & Lessons Learned

Based on real-world experience building and deploying websites, here are the most common mistakes and how to avoid them:

  1. Buying the Domain Before Building the Site

    The Mistake: Purchasing a domain before you have a working prototype, then discovering your idea requires paid APIs, complex logic, or does not work as expected.

    The Fix: Build the full tool with AI first, test it locally at localhost:4321, and only then buy the domain. Domains cost real money; do not waste it on an idea you have not validated.

  2. Cloudflare Workers vs Pages Confusion

    The Mistake: Clicking "Create application" in Cloudflare and unknowingly creating a Worker instead of a Pages project. Workers cannot serve static websites; they are for serverless functions.

    The Fix: When clicking "Create application", immediately switch from the default Worker tab to the Pages tab. Pages is for full websites; Workers is for API endpoints. Always double-check which tab you are on.

  3. CSS Theme Variables: The RGB Trap

    The Mistake: Defining CSS custom properties like --accent-rgb: 59, 130, 246 only in :root (the default theme) but forgetting to redefine them in alternate theme blocks like [data-theme="dark"]. This causes colors to silently break when users switch themes because the RGB fallback uses the wrong values.

    The Fix: When implementing dark mode or multiple themes, every CSS variable (including the RGB variants used with rgba()) must be explicitly set in each theme''s CSS block. A missing variable silently falls back to the default, which looks broken.

  4. SPA Refresh Returns 404

    The Mistake: Building a React, Vue, or Svelte site and deploying it without a _redirects file. Refreshing on yoursite.com/about makes the server look for a real /about file, which does not exist, so it returns 404.

    The Fix: Add a _redirects file with /* /index.html 200 to your /public folder. This tells the server to serve the SPA entry point for all unknown paths, letting JavaScript handle routing.

    Note for Astro users: This pitfall does NOT apply to you. Astro generates real HTML files for every page, so there is no 404-on-refresh issue. You can skip this.

  5. Duplicate Analytics Tracking

    The Mistake: Adding a Google Analytics or Cloudflare Web Analytics JS snippet to your HTML while Cloudflare Pages is already injecting its own edge analytics. This causes every page visit to be counted twice.

    The Fix: Cloudflare Pages automatically injects basic edge analytics. Your manual JS snippet can coexist if you need more detailed tracking, but be aware the numbers will differ. Never add two JS snippets from the same analytics provider.

  6. AI Context Overload

    The Mistake: Continuing a long AI conversation for hours without clearing context. The AI''s context window fills up, it starts making mistakes, forgets earlier instructions, and each prompt becomes slower and more expensive.

    The Fix: Run /clear before asking for each major new feature. Commit your working code to Git first so you can always revert if the AI breaks something in the new session.

  7. Not Committing Before AI Changes

    The Mistake: Letting the AI modify your code for an hour without committing to Git. If the AI makes a catastrophic change, you cannot easily revert.

    The Fix: Run git add . && git commit -m "checkpoint before [feature]" before every major AI prompt. Git is your undo button; use it generously.

  8. CI/CD Running on Every Commit

    The Mistake: Pushing documentation changes, README updates, or comment-only commits and triggering a full Cloudflare Pages build + deploy, wasting time and build minutes.

    The Fix: Include [skip ci] in your commit message, e.g. git commit -m "Update README [skip ci]". This tells Cloudflare Pages to skip the build for changes that do not affect the actual website code.

  9. Samsung Internet Overrides Dark Mode

    The Mistake: Samsung Internet has its own built-in dark mode that forcefully overrides your site's carefully designed themes. It inverts colors, breaks contrast ratios, and makes your site look terrible, and users on Samsung devices (a huge Android market share) will see a broken version of your site.

    The Fix: Add <meta name="darkreader-lock" /> to your <head> to prevent Samsung Internet's dark mode from applying. Also add <meta name="color-scheme" content="light dark" /> so the browser knows your site supports both. For stubborn cases, users may need to open internet://flags in Samsung Internet and disable "Force dark mode".

Checklist Summary


Credits:

Website Views