369 of our 375 pages carry a meta keywords tag, 84 KB of it, for a tag Google stopped reading in 2009
I swept every URL in CogniPrep's sitemap looking for something else, and came back with this instead: sitemap URLs: 375 URLs emitting <meta name="keywords">: 369 keyword strings in th
I swept every URL in CogniPrep's sitemap looking for
something else, and came back with this instead:
sitemap URLs: 375
URLs emitting <meta name="keywords">: 369
keyword strings in those tags: 3,561
distinct keyword strings: 3,162
bytes inside the content attributes: 84,426
longest list: /games 91 keywords
shortest list: one blog article 3 keywords
Google said in 2009 that it does not use the keywords meta tag in web ranking
and has repeated it since. Bing has been blunter, describing it publicly as at
best a signal of keyword stuffing. So this is 84 KB of markup, spread over 369
pages, that no search engine reads and no human sees.
Nobody decided to do this. That is the part worth writing down.
Nothing opted in
Next.js has a keywords field on its Metadata type:
export const metadata: Metadata = {
title: 'Amtico Thomas Assessment Practice',
description: '...',
keywords: [
'amtico assessment',
'amtico careers coventry',
'amtico thomas assessment',
'thomas ppa practice test',
...
],
alternates: { canonical: '/employers/amtico' },
};
Fill it and the framework renders the tag. There is no deprecation warning,
because from the framework's point of view it is a valid HTML meta element and
whether a crawler cares is not its problem. So the field gets filled the way
any field in a metadata object gets filled: the first page copied a pattern,
the next 368 copied the first page.
Four separate code paths feed it here, and only one of them was written with
the tag in mind:
- 132 employer guides, hand-written, 877 keyword strings, between 4 and 12 per page, plus 13 more on the section index.
- 56 provider pages and their hub, hand-written, holding the largest lists on the site: 40 on one provider page and 91 on the hub.
- 57 pages in the cheating cluster, generated from a six-item template plus per-page extras.
-
99 blog articles, where the field is handed the registry's editorial
tagsby a shared builder.
The last one is the only one I would defend. Those tags are real data: 425 tag
strings, 288 distinct, used for the related-article logic and passed to
openGraph.tags, which renders as article:tag and is consumed. The meta
keywords tag is a by-product of a field that happens to be named the same
thing.
The sweep found a bug in the generated ones
The cheating cluster builds its keywords from the provider's search name:
function cheatingKeywords(guide: CheatingGuide): string[] {
const name = guide.searchName;
return [
`can you cheat ${name}`,
`${name} cheat`,
`how to cheat ${name} test`,
`${name} test answers`,
`${name} practice test`,
`${name} test tips`,
...(guide.extraKeywords ?? []),
];
}
extraKeywords is per-page copy, so when somebody adds an extra that the
template already generates, the page ships the same string twice:
curl -s https://cogniprep.app/cheating/shl \
| grep -o '<meta name="keywords" content="[^"]*"'
# ...,shl test answers,shl practice test,shl test tips,shl verification test,
# shl test answers,shl practice test cheat,shl numerical reasoning answers"
shl test answers at position 4 and position 8. Across the live site, 12 of
the 57 cheating pages repeat a keyword inside their own tag, and every one of
the 12 is this collision. No other section has a single one, because no other
section generates half its list and hand-writes the other half.
A [...new Set(...)] in that function fixes all 12, which is the smallest
possible fix to a tag that should not exist. I will take it anyway: a generator
that emits a duplicate is a generator whose output nobody has read.
The six pages without it are the interesting ones
/pricing /about /help /privacy /terms /cookies
Six pages out of 375, and they are exactly the pages that were not generated
from anything. The distribution of a useless tag turns out to be a reliable map
of which parts of a site came from a template and which were written by a
person, because the person writing /terms was thinking about the words on the
page, and the template author was filling in a metadata block.
That is also the honest reason to remove it rather than to leave it. 229 bytes
a page is not a performance problem. An inventory of 3,561 strings is a
maintenance problem: it is 3,561 things that look like they are for something,
on pages where a future contributor will dutifully add the 3,562nd.
See it
Every number above is reproducible from outside. One page:
curl -s https://cogniprep.app/employers/amtico \
| grep -o '<meta name="keywords" content="[^"]*"'
# <meta name="keywords" content="amtico assessment,amtico careers coventry,
# amtico thomas assessment,thomas ppa practice test,thomas hpti practice,
# teique practice test,thomas gia practice test"
The largest list on the site, counted:
curl -s https://cogniprep.app/games \
| grep -o '<meta name="keywords" content="[^"]*"' | tr ',' '\n' | wc -l
# 91
And a page with none, which is the check that the tag is not coming from the
layout:
curl -s https://cogniprep.app/pricing | grep -c 'name="keywords"'
# 0
The whole sweep is the sitemap plus one grep. Ten URLs is enough to see the
shape:
curl -s https://cogniprep.app/sitemap.xml \
| grep -o '<loc>[^<]*' | sed 's/<loc>//' | head -10 \
| while read u; do
printf '%-60s %s\n' "$u" "$(curl -s "$u" | grep -c 'name=\"keywords\"')"
done
If you run it over all 375, the six zeros are the list above.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.