I publish FlowClique on Blogger, and I wanted my affiliate links labeled the way Google asks. Almost every tutorial I found was written for WordPress, so I stopped reading and tested it myself.
Blogger’s link box can add
rel="nofollow". For sponsored, switch to HTML view and type rel="sponsored" into the link yourself. Blogger kept it in the editor and on the published page in my test.
- Blogger post editor in Chrome on a desktop, with the interface set to French
- A draft I never published, with test links pointing to example.com
- Three places checked: the HTML view, the preview, and the page source
- Not tested: links in visitor comments, the Blogger mobile app, and themes other than mine
In this post
- The problem: Blogger’s link box stops at nofollow
- The fix: add sponsored in HTML view
- Does Blogger keep it? I checked the page source
- Which label goes on which link
- What I found in my own affiliate posts
- After you publish: audit your blog and fix what blocks indexing
The problem: Blogger’s link box stops at nofollow
Select some text, click the link button and paste a URL. In my editor the box showed two checkboxes: one to open the link in a new window, and one to add rel="nofollow". With both ticked, Blogger wrote this:
<a href="https://example.com" rel="nofollow" target="_blank">your text</a>
That’s all it offers. There’s no option for sponsored or ugc.
Why does that matter? Google’s documentation on qualifying outbound links says ads and paid placements should carry sponsored. It also says nofollow is still acceptable for paid links, just less specific. An affiliate link pays me when someone buys, so I treat it as a paid link and label it sponsored. That’s my own editorial rule, and it’s the cautious reading of Google’s guidance.
So the checkbox gets you part of the way, and the label you actually want isn’t in the box.
The fix: add sponsored in HTML view
- Create the link as usual in the normal writing view.
- Click the
<>icon at the top left of the toolbar to switch to HTML view. - Find your link and add
rel="sponsored"inside the opening tag.
<a href="https://example.com" rel="sponsored" target="_blank">your text</a>
Other combinations you might need:
<!-- Affiliate or paid link -->
<a href="https://example.com" rel="sponsored">Text</a>
<!-- Sponsored and nofollow together -->
<a href="https://example.com" rel="sponsored nofollow">Text</a>
<!-- Link posted by a visitor -->
<a href="https://example.com" rel="ugc nofollow">Text</a>
If you also want the link to open in a new tab, you can add noopener to the same attribute:
<a href="https://example.com" target="_blank" rel="sponsored noopener">Text</a>
noopener is a security setting for new-tab links. It isn’t an SEO label and it doesn’t replace sponsored.
Does Blogger keep it? I checked the page source
A setting can look right in the editor and still change before it reaches your readers. So I checked three things:
- I typed
rel="sponsored"in HTML view, switched to the normal view, and switched back. The attribute was still there. - I opened the preview and read the page source. The link still said
rel="sponsored" target="_blank". - I looked for anything my theme might have added to that link. It hadn’t added
noopenerornoreferrer. It does putrel='nofollow'on the share buttons andrel='noopener'on the author link, which is a good reason to trust the page source over the editor.
One small oddity: in an earlier attempt Blogger added a non-breaking space ( ) at the start of my link text. It was probably because my text selection included a space. It’s worth a glance at your link text in HTML view.
Which label goes on which link
| Link type | Label | Why |
|---|---|---|
| A source you trust, like an official help page | None | A normal editorial link |
| Your own posts | None | Internal links need no label |
| Affiliate link with your tracking ID | sponsored | You earn a commission |
| Paid post, ad or sponsored placement | sponsored | Money or compensation is involved |
| A site you don’t want to vouch for | nofollow | No endorsement |
| A link left by a visitor | ugc | User-generated (I didn’t test Blogger comments) |
You may have seen “dofollow” in SEO articles. It isn’t an attribute Google documents. A normal link with no rel value is what people mean by it, and typing rel="dofollow" does nothing.
What I found in my own affiliate posts
After the test, I checked the page source of my list of 17 beginner affiliate programs. I looked at the links to Amazon Associates, ClickBank, Impact, Kit, Shopify and Systeme.io. Every one had rel="noopener noreferrer" and target="_blank". None had nofollow or sponsored. If you’re still choosing networks, I compared Digistore24 and ClickBank for creators outside the US.
Those links go to each program’s public sign-up page. They carry no personal affiliate ID, so they’re ordinary editorial links and I left them alone. The rule I follow from now on is simple: the moment a link carries my affiliate ID and I earn from it, it gets rel="sponsored".
Try the same audit on your own posts. Open the page source, search for the domain of each program, and read the rel value.
Mistakes to avoid
- Using nofollow to keep a page out of Google. It doesn’t do that. Use
noindexfor indexing control. - Forgetting banners and image links. They’re links too. Put the attribute on the
<a>tag that wraps the image. - Labeling every outbound link. Links to trustworthy sources can stay normal.
- Relying on the label alone. The
relvalue talks to search engines. Readers need a plain note too. My theme adds one at the top of every post, with a link to my disclaimer. - Thinking a label excuses a link scheme. Buying or selling links to influence rankings still breaks Google’s spam policies, labeled or not.
Questions people ask
Does Blogger have a sponsored checkbox?
Not in my editor. The link box offered only “open in a new window” and “rel=nofollow”. Add sponsored in HTML view.
Is nofollow enough for affiliate links?
Google says nofollow is still acceptable for paid links. sponsored describes them more precisely, so that’s what I use.
Do I have to fix all my old posts?
If an old post has links with your tracking ID and no rel value, add one when you next edit it. If they already use nofollow, they’re not wrong.
Can I use sponsored and nofollow together?
Yes. Google accepts several values in one rel attribute, separated by a space.
Will these links help my rankings?
Don’t count on it. Google says links marked with these values are generally not followed. A link can still send real visitors, and that has value of its own. Keep in mind that tracking can also break when links are shared in messaging apps: see why WhatsApp breaks normal affiliate tracking.
After you publish: audit your blog, then fix what blocks indexing and AdSense
Most indexing and AdSense advice is the same list for every blog. I’d rather measure mine. The script below reads every public post on a Blogger blog and reports what it finds, so you fix real problems instead of guessing. Nobody can promise indexing or approval. This only shows you where your blog is weak.
Run the audit (about 2 minutes)
- Open your blog’s home page in Chrome, on a computer.
- Press F12, then click the Console tab.
- Chrome blocks pasting at first. Type
allow pastingand press Enter. - Copy the full script below, paste it into the console and press Enter.
- Wait until the console says two CSV files were downloaded: one for posts, one for affiliate-like links.
The script only reads your public pages. It doesn’t log in, change anything or send data anywhere. It’s about 251 lines, so you can read it before you run it. As a rule, never paste code into your console that you haven’t read.
Show the audit script
/* FlowClique Affiliate Link & Post Health Audit v1.0
Run in the browser console on YOUR OWN Blogger blog.
Read-only: it fetches your public pages and sends nothing anywhere.
Affiliate detection is a heuristic. Review every flagged link yourself. */
(function (root) {
'use strict';
var CONFIG = {
// Hosts you know are affiliate links (add your own, e.g. 'go.yourbrand.com')
extraAffiliateHosts: [],
// Hosts to never flag (your own domains, trusted sources)
ignoreHosts: [],
// Known affiliate tracking hosts
affiliateHostPatterns: [
/(^|\.)amzn\.to$/, /(^|\.)hop\.clickbank\.net$/, /(^|\.)awin1\.com$/,
/(^|\.)shareasale\.com$/, /(^|\.)(sjv\.io|pxf\.io|evyy\.net|ojrq\.net)$/
],
affiliatePathPatterns: [
{ host: /(^|\.)digistore24\.com$/, path: /^\/redir\// }
],
// URL parameters that often carry an affiliate ID ('tag' is handled for Amazon only)
affiliateParams: ['aff', 'aff_id', 'affid', 'affiliate', 'affiliate_id', 'ref',
'tracking_id', 'clickid', 'subid', 'partner', 'via', 'fpr', 'a_aid',
'irclickid', 'cjevent', 'lmref', 'hop'],
postSelector: '.post-body, #post-body, .entry-content, article',
minWords: 300, // rule of thumb for manual review, NOT a Google threshold
concurrency: 4
};
function relSet(a) {
return new Set((a.getAttribute('rel') || '').toLowerCase().split(/\s+/).filter(Boolean));
}
function bareHost(h) { return h.toLowerCase().replace(/^www\./, ''); }
function isInternal(url, pageUrl) {
var own = bareHost(new URL(pageUrl).hostname);
var h = bareHost(url.hostname);
return h === own || h.endsWith('.' + own);
}
function hostMatches(host, list) {
return list.some(function (h) { return host === h || host.endsWith('.' + h); });
}
function classify(url, cfg) {
var host = bareHost(url.hostname);
if (hostMatches(host, cfg.extraAffiliateHosts)) return 'listed host';
if (cfg.affiliateHostPatterns.some(function (re) { return re.test(host); })) return 'known affiliate host';
if (cfg.affiliatePathPatterns.some(function (p) { return p.host.test(host) && p.path.test(url.pathname); })) return 'known affiliate path';
var found = '';
url.searchParams.forEach(function (v, k) {
if (found) return;
var key = k.toLowerCase();
if (key === 'tag') { if (/(^|\.)amazon\./.test(host)) found = 'Amazon tag parameter'; return; }
if (cfg.affiliateParams.indexOf(key) !== -1) found = 'tracking parameter: ' + key;
});
return found;
}
function stripQuery(u) { return (u || '').split('#')[0].split('?')[0].replace(/\/$/, ''); }
// Audit one parsed document (pure function: easy to test)
function auditDocument(doc, pageUrl, userCfg) {
var cfg = Object.assign({}, CONFIG, userCfg || {});
var body = doc.querySelector(cfg.postSelector);
if (!body) return { url: pageUrl, error: 'post body not found' };
var rows = [], external = 0;
Array.prototype.forEach.call(body.querySelectorAll('a[href]'), function (a) {
var url;
try { url = new URL(a.getAttribute('href'), pageUrl); } catch (e) { return; }
if (url.protocol !== 'http:' && url.protocol !== 'https:') return;
if (isInternal(url, pageUrl)) return;
if (hostMatches(bareHost(url.hostname), cfg.ignoreHosts)) return;
external++;
var reason = classify(url, cfg);
if (!reason) return;
var rel = relSet(a);
var verdict = rel.has('sponsored') ? 'OK'
: rel.has('nofollow') ? 'nofollow only (prefer sponsored)'
: 'NEEDS sponsored';
rows.push({
page: pageUrl,
link: url.href,
text: (a.textContent || '').trim().slice(0, 60) || (a.querySelector('img') ? '[image]' : ''),
rel: Array.from(rel).join(' '),
reason: reason,
noreferrer: rel.has('noreferrer'),
verdict: verdict
});
});
var text = (body.textContent || '').replace(/\s+/g, ' ').trim();
var words = text ? text.split(' ').length : 0;
var imgs = Array.prototype.slice.call(body.querySelectorAll('img'));
var missingAlt = imgs.filter(function (i) { return !(i.getAttribute('alt') || '').trim(); }).length;
var meta = doc.querySelector('meta[name="description"]');
var metaLen = meta ? (meta.getAttribute('content') || '').trim().length : 0;
var canon = doc.querySelector('link[rel="canonical"]');
var canonHref = canon ? canon.getAttribute('href') : '';
var disclosure = !!doc.querySelector('.cf-aff-note, .affiliate-disclosure, [class*="disclosure"]') ||
/affiliate|commission/i.test(text.slice(0, 800));
return {
url: pageUrl,
title: (doc.title || '').trim(),
words: words,
thin: words < cfg.minWords,
images: imgs.length,
missingAlt: missingAlt,
hasMetaDescription: metaLen >= 50,
canonicalOk: !!canonHref && stripQuery(canonHref) === stripQuery(pageUrl),
externalLinks: external,
affiliateRows: rows,
hasDisclosure: disclosure,
missingDisclosure: rows.length > 0 && !disclosure
};
}
function summarize(results) {
var ok = results.filter(function (r) { return !r.error; });
var rows = [].concat.apply([], ok.map(function (r) { return r.affiliateRows; }));
return {
postsScanned: ok.length,
postsFailed: results.length - ok.length,
postsWithAffiliateLikeLinks: ok.filter(function (r) { return r.affiliateRows.length; }).length,
affiliateLikeLinks: rows.length,
needsSponsored: rows.filter(function (r) { return r.verdict === 'NEEDS sponsored'; }).length,
nofollowOnly: rows.filter(function (r) { return r.verdict.indexOf('nofollow only') === 0; }).length,
alreadySponsored: rows.filter(function (r) { return r.verdict === 'OK'; }).length,
affiliateLinksWithNoreferrer: rows.filter(function (r) { return r.noreferrer; }).length,
postsMissingDisclosure: ok.filter(function (r) { return r.missingDisclosure; }).length,
thinPosts: ok.filter(function (r) { return r.thin; }).length,
imagesMissingAlt: ok.reduce(function (n, r) { return n + r.missingAlt; }, 0),
postsWithoutMetaDescription: ok.filter(function (r) { return !r.hasMetaDescription; }).length,
postsWithCanonicalMismatch: ok.filter(function (r) { return !r.canonicalOk; }).length
};
}
async function listPostUrls(origin, fetchFn) {
var urls = [], start = 1, step = 150;
try {
while (true) {
var r = await fetchFn(origin + '/feeds/posts/default?alt=json&max-results=' + step + '&start-index=' + start);
if (!r.ok) throw new Error('feed ' + r.status);
var j = await r.json();
var entries = (j.feed && j.feed.entry) || [];
entries.forEach(function (e) {
var l = (e.link || []).find(function (x) { return x.rel === 'alternate'; });
if (l) urls.push(l.href);
});
if (entries.length < step) break;
start += step;
}
if (urls.length) return urls;
} catch (e) { /* fall back to the sitemap */ }
var locs = function (xml) {
var out = [], re = /<loc>([^<]+)<\/loc>/g, m;
while ((m = re.exec(xml))) out.push(m[1].trim());
return out;
};
var sm = await fetchFn(origin + '/sitemap.xml');
var first = locs(await sm.text());
var pages = first.filter(function (u) { return /sitemap\.xml\?page=/.test(u); });
var all = first.filter(function (u) { return !/sitemap\.xml/.test(u); });
for (var i = 0; i < pages.length; i++) {
var pr = await fetchFn(pages[i]);
all = all.concat(locs(await pr.text()));
}
return all.filter(function (u) { return /\/\d{4}\/\d{2}\/[^\/]+\.html$/.test(u); });
}
async function cfAudit(opts) {
opts = opts || {};
var fetchFn = opts.fetchFn || fetch.bind(root);
var Parser = opts.Parser || DOMParser;
var origin = opts.origin || root.location.origin;
var cfg = Object.assign({}, CONFIG, opts.config || {});
var urls = await listPostUrls(origin, fetchFn);
console.log('[FlowClique audit] ' + urls.length + ' posts found. Scanning...');
var results = new Array(urls.length), next = 0;
async function worker() {
while (next < urls.length) {
var i = next++;
try {
var res = await fetchFn(urls[i]);
if (!res.ok) throw new Error('HTTP ' + res.status);
var doc = new Parser().parseFromString(await res.text(), 'text/html');
results[i] = auditDocument(doc, urls[i], cfg);
} catch (e) {
results[i] = { url: urls[i], error: String(e.message || e) };
}
}
}
var workers = [];
for (var w = 0; w < cfg.concurrency; w++) workers.push(worker());
await Promise.all(workers);
var summary = summarize(results);
var rows = [].concat.apply([], results.filter(function (r) { return !r.error; }).map(function (r) { return r.affiliateRows; }));
var problems = rows.filter(function (r) { return r.verdict !== 'OK'; });
console.log('[FlowClique audit] SUMMARY');
console.table(summary);
if (problems.length) {
console.log('[FlowClique audit] Affiliate-like links that need a look:');
console.table(problems.map(function (r) {
return { page: r.page.replace(origin, ''), text: r.text, rel: r.rel, reason: r.reason, verdict: r.verdict };
}));
}
var weak = results.filter(function (r) { return !r.error && (r.thin || r.missingAlt || !r.hasMetaDescription || !r.canonicalOk || r.missingDisclosure); });
if (weak.length) {
console.log('[FlowClique audit] Posts with page-health flags:');
console.table(weak.map(function (r) {
return { page: r.url.replace(origin, ''), words: r.words, missingAlt: r.missingAlt,
metaDescription: r.hasMetaDescription, canonicalOk: r.canonicalOk, missingDisclosure: r.missingDisclosure };
}));
}
var failed = results.filter(function (r) { return r.error; });
if (failed.length) console.warn('[FlowClique audit] Could not scan:', failed);
if (opts.download !== false && typeof document !== 'undefined') {
var q = function (v) { return '"' + String(v).replace(/"/g, '""') + '"'; };
var head = ['page', 'words', 'thin', 'missing_alt', 'meta_description', 'canonical_ok', 'external_links', 'affiliate_like_links', 'missing_disclosure'];
var csv = [head.join(',')].concat(results.filter(function (r) { return !r.error; }).map(function (r) {
return [q(r.url), r.words, r.thin, r.missingAlt, r.hasMetaDescription, r.canonicalOk, r.externalLinks, r.affiliateRows.length, r.missingDisclosure].join(',');
})).join('\n');
var linksCsv = ['page,link,text,rel,reason,noreferrer,verdict'].concat(rows.map(function (r) {
return [q(r.page), q(r.link), q(r.text), q(r.rel), q(r.reason), r.noreferrer, q(r.verdict)].join(',');
})).join('\n');
[['flowclique-posts.csv', csv], ['flowclique-affiliate-links.csv', linksCsv]].forEach(function (f) {
var a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([f[1]], { type: 'text/csv' }));
a.download = f[0];
document.body.appendChild(a); a.click(); a.remove();
});
console.log('[FlowClique audit] Two CSV files downloaded.');
}
return { summary: summary, results: results };
}
var api = { cfAudit: cfAudit, auditDocument: auditDocument, summarize: summarize, listPostUrls: listPostUrls, classify: classify, CONFIG: CONFIG };
root.cfAudit = cfAudit;
if (typeof module !== 'undefined' && module.exports) module.exports = api;
})(typeof window !== 'undefined' ? window : globalThis);
if (typeof window !== 'undefined') { cfAudit(); }
What each flag means, and what to do
| Flag | Why it matters | Fix |
|---|---|---|
| NEEDS sponsored | The link looks like an affiliate link (a known network, or an ID in the URL) and has no sponsored label. | Add rel="sponsored" in HTML view, as shown earlier. |
| nofollow only | Google accepts it for paid links, but sponsored is more precise. | Switch it the next time you edit the post. |
| noreferrer on an affiliate link | noreferrer stops the browser from telling the destination which page sent the visitor. Most programs track by the ID in the URL, but I haven’t checked how each one treats a missing referrer. | On links that carry your ID, use rel="sponsored" alone. Current browsers already treat target="_blank" as noopener. If a program’s terms mention referrers, follow them. |
| Missing disclosure | The post has affiliate-like links but no disclosure note near the top. | Add a plain note before the first link. Advertising rules in many countries require clear disclosure, so check yours. |
| Thin (under 300 words) | This is my rule of thumb for manual review, not a Google threshold. A short post with little original input is the first place to look. | Add what you tested yourself, merge it into a stronger post, or leave it if it fully answers its question. Check Search Console performance before deleting anything. |
| Images without alt text | Alt text describes the image to readers who can’t see it and to search engines. | Add a short, literal description to each image. |
| No meta description | No usable snippet text under 50 characters. | Fill in “Search description” in the post settings. |
| Canonical mismatch | The page names a different canonical URL than its own. | Run the URL in Search Console’s URL Inspection and compare the canonical Google chose. Check your theme if it keeps happening. |
What the audit found on FlowClique
I ran the script on my own blog on October 26, 2026. It found 39 public posts and scanned all of them, with no failures. Only one post had affiliate-like links: two links, both with an empty rel value, so both were flagged NEEDS sponsored. One went to a known affiliate host and the other carried a tracking parameter. No affiliate link had noreferrer, and no post with affiliate-like links was missing a disclosure note.
The page-health checks were mostly clean: no post under 300 words, no missing meta description and no canonical mismatch. The only other flag was two images without alt text, in two different posts. I fixed the two links and the two images, then ran the script again on October 27, 2026. It reported 0 links needing sponsored and 0 images without alt text, and no post was flagged for page health.
Find the indexing pattern on your own blog
- In Search Console, open the Pages report under Indexing and export the list of pages that aren’t indexed. The report has an export option.
- In Google Sheets, put that list next to
flowclique-posts.csvand match the rows by URL. - Look for a trait the non-indexed posts share: short, no meta description, no alt text, no disclosure, canonical mismatch.
- If a trait stands out, fix it across those posts first, then request indexing for each URL once.
If no pattern shows up, don’t force one. Google may simply not have crawled those posts yet. Add internal links to them from related posts and give it time.
AdSense pre-flight, using your own data
- No post is left under “Missing disclosure”.
- You’ve reviewed every thin post and decided what to do with it.
- Your About, Contact, Privacy Policy and Disclaimer pages exist and are reachable from the menu or footer.
- You’ve read Google’s eligibility and readiness pages (links in Sources) and checked any guide that quotes a fixed number of posts or visitors against them.
- If a review fails, fix what the message points to, run the script again, then reapply.
Where the script falls short
I first tested it against sample pages with the patterns above (plain links, tagged links, uppercase rel values, image links, malformed URLs). I then ran it on flowclique.com: 39 posts scanned, no selector errors. That is one blog and one theme, so it isn’t proof for every Blogger theme.
- Affiliate detection is a heuristic. A link with
?ref=in it may not be affiliate at all, and a network the script doesn’t know can slip through. Add your own hosts toextraAffiliateHostsat the top of the script. - It reads post bodies only, not comments, sidebars or static pages.
- It needs your public feed or your sitemap. If it reports “post body not found”, change
postSelectorto match your theme.
Key takeaways
- Blogger’s link box adds
nofollow. It doesn’t offersponsored, so add that in HTML view. - Blogger kept
rel="sponsored"through the editor and onto the published page in my test. - Check the page source, not the editor. Your theme can add its own values.
- Put
sponsoredon any link that carries your affiliate ID. - After publishing: run the audit script, fix the flags, and compare them with your Search Console export.
Louiza Mark runs FlowClique, a Blogger-based site about affiliate marketing and AI tools. Each guide says what was tested and when. If Blogger changes its editor, this post and the date at the top will be updated.
Sources
- Google Search Central: Qualify your outbound links to Google
- Google Search Central: Spam policies for Google web search
- Google Search Central Blog: Evolving “nofollow”: new ways to identify the nature of links (September 2019)
- Google AdSense Help: Eligibility requirements for AdSense
- Google AdSense Help: Make sure your site’s pages are ready for AdSense
- MDN Web Docs: rel="noopener" (covers when the Referer header is sent)
- Google Search Console: Search Console
.png)



0 Comments