Frequently asked questions
Can I run AdSense and direct ads in the same zone?
Yes. Each zone has a fill_priority enum: direct, house, or remnant. If you set fill_priority = remnant and add your AdSense code in the zone's adsense_code field, OAds falls back to AdSense when no direct ad matches the targeting filters. Direct ads always win when they're available.
How are clicks attributed if the visitor blocks JavaScript?
Clicks always work: the ad link points at /oads-click/{id}/ and the server logs the click before issuing a 302 redirect to the destination URL. No JavaScript required. Impressions need JS (IntersectionObserver), so visitors with JS blocked won't register impressions but their clicks still count.
Can I A/B test two creatives against each other?
Yes. Create an Ad Group, set rotation to ab, add two ads. OAds serves them with weighted random until any variant hits 100 impressions, then OAds_Groups::maybe_pick_winner() compares CTR and locks the winner (writes the winning ad's ID into oads_groups.ab_winner). From then on the group serves only the winner.
What's the difference between sections and zones?
Sections are loose tags assigned per ad (blog, shop, global). Useful for grouping ads by intent. Zones are named slots with dimensions, fill priority, and optional AdSense fallback. Use zones when you want to standardise placement and pin specific dimensions; use sections for casual grouping.
Do video ads autoplay?
Video ads start playing when they reach 50% visibility (IntersectionObserver) and pause when they leave the viewport. Browsers require muted autoplay for unsolicited video, so video ads are muted by default. Visitors can unmute manually.
Does OAds work with caching plugins?
Yes. The ad markup is rendered server-side and injected into the page response. Impression tracking is via a small JS payload that fires per-page-view regardless of cache hits. Click tracking is via direct /oads-click/{id}/ URLs that bypass cache entirely.

