← Blog Tutorial

Setting Up ads.txt and AdSense on a Static Site

Most AdSense guides assume WordPress and a plugin. If your site is plain HTML, the setup is actually simpler, but there are a few decisions the guides skip: where the ad code should go, what to do with an empty slot, and why "Auto ads" can be a bad idea on interactive pages. Here's how I set it up on this site.

1. Add ads.txt

ads.txt is a plain text file at the root of your domain that lists who is allowed to sell ads on it. It stops other people selling fake inventory under your name, and AdSense warns that your earnings are "at risk" until it finds one.

For AdSense it's a single line:

google.com, pub-0000000000000000, DIRECT, f08c47fec0942fa0

The four parts are:

  • google.com, the ad system.
  • Your publisher ID, found in AdSense under Account, Settings. It's the number from your ca-pub- code with pub- in front.
  • DIRECT, meaning you have a direct relationship with Google rather than going through a reseller.
  • f08c47fec0942fa0, Google's certification authority ID. It's the same for every AdSense publisher.

Save it as ads.txt in the root of the site, next to your homepage, so it's served at https://yourdomain.com/ads.txt. Then check that it really is there and served as text:

curl -sI https://yourdomain.com/ads.txt | grep -iE "^HTTP|content-type"
# HTTP/2 200
# content-type: text/plain

AdSense re-checks on its own schedule. It can take a few days for the status to change from "Not found" to "Authorized".

The ads.txt file for glentenorio.com shown in a browser, one line of text
The whole file, as served at glentenorio.com/ads.txt.

2. Create a manual ad unit

AdSense offers two approaches: Auto ads, where Google decides where ads go, and ad units, where you place each ad yourself.

On a blog, Auto ads can be fine. On anything interactive I use manual units, because Auto ads will happily drop an ad into the middle of content your JavaScript is about to redraw. My Website QA tool rebuilds its report several times as results arrive, so an injected ad would either be wiped out or land in the middle of a list of issues.

In AdSense, go to Ads, By ad unit, Display ads, give it a name, choose Responsive, and create it. The only thing you need from the code it shows you is the data-ad-slot number.

3. Place it on the page

This is the standard snippet. The script loads once per page; the <ins> element is the ad slot:

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

<div class="ad-frame">
  <p class="ad-label">Advertisement</p>
  <ins class="adsbygoogle"
       style="display:block"
       data-ad-client="ca-pub-0000000000000000"
       data-ad-slot="1234567890"
       data-ad-format="auto"
       data-full-width-responsive="true"></ins>
</div>
<script>(adsbygoogle = window.adsbygoogle || []).push({});</script>

A few rules I follow when choosing where it goes:

  • Outside anything JavaScript re-renders. If your page rebuilds a section with innerHTML, the ad must sit outside that section.
  • Not next to buttons or navigation. AdSense policy prohibits placements that encourage accidental clicks.
  • Labelled. "Advertisement" or "Sponsored links" is allowed and makes it clear what it is.
  • Not in the print version. I hide ad frames in the print stylesheet.

4. Hide empty slots

Ads don't always fill. When the site isn't approved yet, or there's no ad to show, you're left with an empty labeled box, which looks broken.

AdSense sets a data-ad-status attribute on the slot once it decides: filled or unfilled. You can hide empty slots with one CSS rule:

ins.adsbygoogle[data-ad-status="unfilled"] { display: none !important; }

I go one step further and keep the whole frame, label included, invisible until Google reports the slot as filled. The trick is that AdSense needs to measure the slot's width, so it can't be display: none while it waits:

/* Laid out (AdSense needs the width), but invisible until filled */
.ad-frame.is-pending { visibility: hidden; height: 0; padding: 0; margin-block: 0; overflow: hidden; }
const ins = frame.querySelector('ins.adsbygoogle');
frame.classList.add('is-pending');
new MutationObserver(() => {
  const status = ins.getAttribute('data-ad-status');
  if (status === 'filled') frame.classList.remove('is-pending');
  if (status === 'unfilled') frame.hidden = true;
}).observe(ins, { attributes: true, attributeFilter: ['data-ad-status'] });

If any of your visitors are in the European Economic Area, the UK or Switzerland, Google requires a certified consent message before it serves personalized ads. You don't need a third-party cookie banner for this: AdSense has one built in under Privacy and messaging, European regulations. Create the message there, publish it, and the ad script shows it automatically.

Your privacy policy also needs to say that you use AdSense, that Google and its partners use cookies to serve ads based on previous visits, and how visitors can opt out through Google's Ads Settings. Mine is on the privacy page if you want a reference.

6. Getting approved

None of the above shows a single ad until Google approves the site. Approval is a review of the whole site, not one page, and the most common rejection is "Low value content".

I'll be honest: that's the status this site had the first time I checked. A portfolio with a few pages, some tools and three blog posts didn't give a reviewer much to read.

What the review is looking for, as far as I can tell from Google's own guidance:

  • Original, useful content that someone would come back to, not pages written for ad space.
  • Enough of it. Tool pages and a handful of posts usually aren't enough on their own.
  • Clear navigation, plus an about page, contact details and a privacy policy.
  • No thin or duplicated pages dragging the average down.

Once you've made real improvements, request a review from the Sites page in AdSense. Reviews can take from a few days to a few weeks, and if you're rejected again, add more substance before reapplying rather than resubmitting straight away.

Wrapping up

On a static site, AdSense is three files' worth of work: ads.txt at the root, one script tag, and an ad slot placed somewhere it won't get in the way. The parts that take thought are where the slot goes, what happens when it's empty, and having a site worth approving in the first place.

Keep reading

Frequently asked questions

Do I need ads.txt for AdSense?

Google strongly recommends it. Without it, AdSense shows an "Earnings at risk" warning, and some advertisers won't buy inventory on sites that don't publish one.

Where does ads.txt go on a static site?

In the root folder, next to your homepage index.html, so it's served at yourdomain.com/ads.txt. It must be on the root domain, not in a subfolder.

Should I use Auto ads or ad units?

Auto ads are easiest on a simple blog. On pages with interactive content that JavaScript redraws, manual ad units are safer, because you control exactly where ads appear.

Want this done for you?

I build and fix fast, clean websites in WordPress, Breakdance, Elementor and hand-written code. Send me your site and I'll tell you what I'd fix first.