We applied for AdSense on citeya.com's blog and got rejected. We fixed what we assumed was the problem and applied again. Rejected again, same reason: low-value content. The third time, instead of guessing, we actually audited what Google's crawlers see when they hit our site — and found a genuinely embarrassing list of problems, several of which had nothing to do with the quality of the writing itself. This is that audit, written up so other SaaS teams hitting the same rejection don't have to rediscover it the slow way.
The First Problem: Crawlers Were Seeing an Empty Page
Our blog is a Vite single-page app. That's a fine architecture for an interactive product, and it was a bad architecture for a content site with no server-side rendering. Every route — homepage, every blog post, every marketing page — shipped the same near-empty `<div id="root"></div>` as the initial HTML response, with the actual content assembled client-side by JavaScript after the fact. A browser never notices, because it executes the JavaScript instantly. A crawler that doesn't fully render JavaScript, or an automated content-quality reviewer working off the raw HTML response, sees nothing — no headline, no body text, no citations, nothing to evaluate at all.
We checked this the direct way: `curl`-ing our own blog post URLs and reading exactly what came back, instead of trusting what we saw in a browser. Every single one came back structurally empty. That's about as literal a match for 'low-value content' as a rejection reason can get — there was no content there to rate as low-value or otherwise, from the crawler's point of view. The fix was server-side prerendering for the homepage and every marketing and blog route, so crawlers now receive real, populated static HTML instead of an empty shell.
A Duplicate Meta Tag Bug That Made Every Post Look Like the Homepage
Once prerendering was in place, we went looking for anything else that could still read as thin or duplicate content, and found a genuinely dumb bug: the homepage's title tag, canonical link, and Open Graph tags were leaking onto every blog post. Each post had different visible written content, but the metadata describing that content — the exact signals a search engine or ad-quality system uses to understand what a page is and whether it's distinct from other pages on the same domain — was identical across dozens of URLs. To an automated system, that's a strong signal of duplicate or templated content, regardless of what the article body actually says. The root cause was a metadata layer that wasn't properly scoped per route. We fixed it so title, canonical URL, and Open Graph tags are generated per post from that post's own data, not inherited from a shared default.
A Sitemap That Hadn't Been Accurate in Months
Our sitemap.xml was hand-maintained, which is another way of saying it was quietly going stale every time we published or reorganized without remembering to update a separate file by hand. New posts were missing. A few old, since-removed URLs were still listed. None of this is dramatic on its own, but a sitemap that misrepresents what's actually on the site is one more small signal, on top of the others, that the site isn't being carefully maintained. We now generate sitemap.xml at build time directly from the same post data that renders the site, so it's structurally impossible for it to drift out of sync again.
Explicitly Allowing Google's Ad-Quality Crawler
Less obvious: our robots.txt didn't explicitly address Mediapartners-Google, the crawler Google documents as visiting sites specifically to assess page content for ad-matching purposes. It wasn't blocked outright, but it also wasn't clearly and explicitly allowed, which matters because this crawler's whole job is determining what a page is about well enough to serve relevant ads against it — and by extension, whether that page clears the quality bar for showing ads at all. We added an explicit allow rule for it, on top of fixing the rendering problem that had been the bigger issue underneath.
The Most Embarrassing Finding: Zero Citations on a Blog Pitching 'Source-Backed Content'
This is the one that actually stung. Citeya's entire product pitch is AI content that cites its sources. Going through our own 31 published blog posts, every single one asserted claims, statistics, and best practices with zero outbound citations. Not thin citations, not weak ones — none. A generic 'Citeya Team' byline sat on top of posts that read exactly like the undifferentiated SEO-content-marketing content produced by thousands of other sites, on a blog for a product whose reason to exist is not producing that kind of content.
Google's own guidance on creating helpful, reliable, people-first content is explicit that demonstrated expertise, clear authorship, and accurate sourcing are core signals of the kind of content it wants to reward — and, by direct extension, the kind of content Google considers worth showing ads against. The AdSense Program Policies and the publisher policies on valuable inventory both center on the same idea from the ad-quality side: publishers need to provide substantive, original content, not pages that exist mainly as a wrapper for ads. An uncited blog for a citation product isn't a subtle miss against either standard. We went back through all 31 posts, added real, verified citations throughout, and replaced the generic team byline with a named, credentialed author.
Confirming the Fixes Actually Landed
We didn't want to reapply on faith that the fixes worked, so we checked the same way we'd found the problems: by looking at what a crawler sees, not what we assumed it saw. That meant re-running `curl` against prerendered post URLs and confirming real headline and body text came back in the raw response instead of an empty shell, diffing the meta tags across several posts and the homepage side by side to confirm titles and canonicals were now distinct per URL, and loading sitemap.xml directly to confirm every published post was present and every removed one wasn't. It's a slower way to sign off on a fix than trusting the browser preview, and it's the only way that actually answers the question a rejection reason is implicitly asking: what does the outside world see when it looks at this page.
What We'd Tell Another SaaS Team Facing This
A few things worth checking before you assume a rejection is about writing quality alone. First: look at what a crawler actually receives, not what your browser renders — `curl` your own pages, or view source, before assuming your content is visible to anything that isn't running your JavaScript. Second: audit meta tags per route, not just per template; a single shared-layout bug can silently duplicate metadata across an entire site while the written content looks completely fine to a human. Third: treat your sitemap as build output, not hand-maintained state, so it can't quietly go stale. Fourth: check your robots.txt from the ad network's crawler's perspective, not only Googlebot's. And fifth, the one that mattered most for us: if your product's pitch depends on some specific quality — sourcing, originality, accuracy, whatever it is — your own marketing content has to visibly demonstrate that quality first. A content-generation company publishing uncited claims about the value of citations is about the easiest inconsistency a human reviewer can spot.
We've shipped all of the above and plan to reapply. We're in no hurry to do it before we're confident it's actually fixed — a third rejection would cost more than the extra weeks spent getting this right the first time.
The most on-the-nose failure of the entire rejection was a plain fact: we were selling 'cites its sources' on a blog that cited none of its own.