WordPress makes placing ads easy and maintaining them hard. Every method has a failure mode, and most publishers discover theirs during a theme update.
The four methods
1. Editing the theme directly. Add the code to header.php or single.php.
Works immediately. Wiped by the next theme update unless you are using a child theme. If you go this route, use a child theme — otherwise you will lose your ads and not notice for a week.
2. A header-scripts plugin. Something like WPCode or Insert Headers and Footers holds your tag.
Survives theme updates. Adds a plugin. Good for the site-wide script; less good for placing individual slots.
3. A block or shortcode. A block in the editor, or [ad slot="sidebar-top"] in content.
The most maintainable option for in-content placements. Survives theme changes, is visible to anyone editing the post, and does not require touching code. Requires a plugin that provides the block.
4. CSS selector injection. The ad server is told "put this slot after #main-content h2:first-of-type" and injects it at page load.
Requires no theme edit and no shortcode, and survives content changes. It breaks if the theme's markup changes underneath it — so the selector should target something stable like an id, not a generated class name.
What to actually do
Combine two:
- The site-wide script goes in a header plugin, or a child theme's
header.php. It goes in once and never changes. - Individual slots go in as blocks or shortcodes where you can edit the content, and via CSS selector where you cannot.
That keeps theme updates safe and leaves ad placement visible to whoever is editing.
The generated-class trap
Modern WordPress themes, particularly block themes, produce class names like wp-block-group-is-layout-flow-1a2b3c. Those change when the theme is rebuilt.
If you place an ad by CSS selector, target an id or a semantic element — #content, article > p:nth-of-type(3) — not a hashed class. Otherwise the placement silently stops appearing after an update, and nothing tells you.
Caching
WordPress sites usually sit behind a page cache. That is fine for ad serving as long as the ad is fetched by JavaScript at view time rather than baked into the cached HTML.
If you insert an ad server-side in PHP, the cache will store one advertiser's banner and serve it to everyone until the cache clears — breaking rotation, scheduling and reporting all at once. Client-side insertion avoids the whole problem.
AMP
If you still serve AMP pages, ordinary ad tags will not run there — AMP requires its own ad component. Given AMP's decline in importance, the pragmatic answer for most small publishers is to stop serving AMP rather than maintain two ad setups.
A sane checklist
- Site-wide script in a header plugin or child theme
- In-content slots as blocks or shortcodes
- Selector-based slots targeting ids, not generated classes
- Ads inserted client-side so page caching does not freeze them
- After any theme update, load one page and confirm an ad still appears