---
title: "The AdSense integration that actually works"
description: "AdSense docs make it sound complicated. The actual integration is one script tag, one ads.txt file, and approval. Here's the full pattern with the gotchas explained."
date_published: 2026-05-04
last_updated: 2026-05-04
canonical: https://vibecodersguidetomvp.help/blog/adsense-integration-actually-works/
author: Titan Alpha
tags: ["adsense","monetization","ads"]
---

# The AdSense integration that actually works

AdSense docs make it sound complicated. The actual integration is one script tag, one ads.txt file, and approval. Here's the full pattern with the gotchas explained.

> Canonical HTML: https://vibecodersguidetomvp.help/blog/adsense-integration-actually-works/
> This is the agent-friendly markdown alternate for the page above.


AdSense feels intimidating because Google's docs cover every possible ad placement, format, and rule. For a typical content site, you don't need most of it. You need:

1. The publisher loader script in `<head>`.
2. An `ads.txt` file at your domain root.
3. Approval from AdSense.
4. Ad slots on your pages.

That's it. Done correctly, the integration is about 20 minutes of work. The hard part is getting approved (which Google does at their own pace) and waiting through that. The integration itself is simple.

Here's the full pattern.

## Step 1: Get a publisher ID

Sign up at [adsense.google.com](https://adsense.google.com). Add your site. Google asks you to add their script to verify ownership. The script looks like:

```html
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-XXXXXXXXXXXXXXXX" crossorigin="anonymous"></script>
```

Your **publisher ID** is the `ca-pub-XXXXXXXXXXXXXXXX` part. Save it. You'll use it in two places.

## Step 2: Add the loader script to `<head>` of every page

Drop the script tag into the `<head>` of every page on your site. For Next.js or any framework with a root layout, that's a one-line addition. For static HTML, it goes in every `.html` file.

Important: the script must be in `<head>`, not `<body>`. AdSense's reviewer specifically checks for this; a script in body will fail review.

For a single-page application (SPA), the `<head>` is the static `index.html`'s head — not anything React renders. Putting the script in a React component won't satisfy AdSense's reviewer because they don't execute JavaScript.

## Step 3: Create `ads.txt` at your domain root

This is the most-skipped step and it costs you serious revenue.

`ads.txt` is the IAB "Authorized Digital Sellers" file. It lives at `https://your-domain.com/ads.txt` and tells programmatic ad buyers which sellers are authorized to monetize your inventory. Without it, automated bidders treat your inventory as "unauthorized seller" and bid less (or not at all). AdSense estimates the impact at 30-50% of revenue.

The file content for AdSense is one line:

```
google.com, pub-XXXXXXXXXXXXXXXX, DIRECT, f08c47fec0942fa0
```

Breaking down each field:
- `google.com` — the SSP (Supply-Side Platform) being authorized.
- `pub-XXXXXXXXXXXXXXXX` — your publisher ID, **without** the `ca-` prefix (this is the format in `ads.txt`, even though the script uses `ca-pub-`).
- `DIRECT` — you have a direct relationship with Google (vs. reselling).
- `f08c47fec0942fa0` — Google's fixed TAG ID. Same value for every AdSense publisher.

Place the file at `public/ads.txt` for Next.js / Vite / Astro projects. Verify after deploy with:

```bash
curl https://your-domain.com/ads.txt
```

Should return the single line as `text/plain`.

## Step 4: Wait for AdSense approval

This is the slow part. Google's reviewer evaluates whether your site has substantive content, a clear purpose, and proper navigation. Typical wait: **2-7 days for the first decision.**

Common rejection reasons:
- **Low value content.** This is the most common one for SPAs because the rendered HTML is just a script tag and an empty `<div>`. The fix is **server-side rendering or pre-rendering** so the HTML response contains real content text.
- **Insufficient content.** A site with one page is harder to approve than a site with substantive pages, a blog, or documentation.
- **Under construction.** Sites with placeholder text or "coming soon" sections.

If you get a rejection, fix the cited issue and request re-review. Each cycle is another 2-7 days.

## Step 5: Create ad units once approved

Once approved, AdSense → Ads → By ad unit → Create new ad unit. Each unit gets a 10-digit slot ID.

For a typical content site, three units cover most placement needs:
- **Top of post** (after the title): 728×90 leaderboard or responsive
- **In-content** (mid-post): responsive
- **End of post** (before footer): 728×90 or responsive

For sidebars on wide layouts:
- **Sidebar**: 160×600 wide skyscraper or 300×600 half page

## Step 6: Insert ad slots in your templates

Each ad slot is an `<ins>` element with a tiny script that pushes the slot to AdSense's queue:

```html
<ins class="adsbygoogle"
     style="display:block; width:100%; max-width:728px; height:90px"
     data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
     data-ad-slot="1234567890"
     data-ad-format="auto"
     data-full-width-responsive="true"></ins>
<script>(adsbygoogle = window.adsbygoogle || []).push({});</script>
```

The `data-ad-format="auto"` and `data-full-width-responsive="true"` attributes let AdSense pick the best size for the available space. Use these unless you have a specific reason to fix the dimensions.

## The optimization: hide on small screens

Sidebar ads in particular should be hidden below a certain viewport width. Cramming a 160×600 ad next to mobile content makes the page unusable.

Tailwind makes this trivial:

```html
<aside class="hidden xl:block w-[180px]">
  <ins class="adsbygoogle" ...></ins>
</aside>
```

`hidden xl:block` means the slot only renders on viewports ≥1280px. Mobile and tablet users get a clean reading experience.

## What you'll earn

For a content site at typical CPMs ($1-3), each 1,000 page views earns roughly **$1-3** in ad revenue. This scales linearly with traffic, so:

- 1,000 monthly page views: $1-3
- 10,000: $10-30
- 100,000: $100-300
- 1,000,000: $1,000-3,000

These numbers wildly depend on niche (finance and software pay better; lifestyle and entertainment pay less), region (US/UK traffic pays more than other geos), and time of year (Q4 is much higher than Q1).

For most MVPs, AdSense is supplementary income, not primary revenue. Don't build for ads; ship the product, add ads as a passive layer once approved.

## Anti-patterns

**Stuffing too many ad slots.** Past 3-4 per page, AdSense itself will downgrade you, and your users will bounce.

**Loading the AdSense script on pages that don't show ads.** Kills perf for no benefit. If you have an admin dashboard or a checkout flow, conditionally skip the script there.

**Forgetting `ads.txt`.** Silently kills 30-50% of revenue. Always check after deploy.

**Re-requesting approval before fixing the cited issue.** AdSense reviewers note repeat applications. Fix the issue first, document the fix in your re-review note.

**Ad blockers in dev.** Ad blockers strip the AdSense script silently in your local browser. If you're testing whether ads load and they don't appear, check your ad blocker before assuming the integration is broken.

## What this site did

`vibecodersguidetomvp.help` has the AdSense loader in `<head>`, an `ads.txt` at the root, and two side-column ad slots on the carousel that hide on viewports below 1280px.

The first AdSense review came back "Needs attention — low value content" because the SPA's HTML response was just an empty shell. The fix is the pre-rendering pattern from sub-skill 13 of the skill bundle: a Puppeteer post-build script that renders the SPA, dumps the resulting DOM, and writes it back to `dist/index.html`. The carousel still works the same in the browser, but the initial HTML response now contains real content text. Re-requested approval.

That's the whole pattern. Loader script, `ads.txt`, real content in the initial HTML response, approval, ad slots. Twenty minutes of integration work plus however long Google takes to approve. Don't make it harder than it is.

