Skip to content

Fix SEO issues from the SEMrush crawl - #121

Merged
vpetersson merged 4 commits into
sbomify:masterfrom
vpetersson-bot:fix-semrush-seo-issues
Aug 2, 2026
Merged

Fix SEO issues from the SEMrush crawl#121
vpetersson merged 4 commits into
sbomify:masterfrom
vpetersson-bot:fix-semrush-seo-issues

Conversation

@vpetersson-bot

Copy link
Copy Markdown
Contributor

Three site-wide problems from the 2026-08-02 crawl. All three live in shared templates, which is why each multiplied across every page.

1. Internal links had no trailing slash — 299 redirects, 50 chains

The nav in data/main.yml, the footer, the case-study template and several content files linked to /about rather than /about/. Every one 301s:

301  /about        301  /blog       301  /case-studies
301  /pricing      301  /privacy    301  /terms
301  /what-is-sbom 301  /zero-to-hero

Six nav links × 49 pages accounts for almost exactly the 299 "permanent redirects" reported. Now all canonical:

=== internal page links missing a trailing slash ===
(empty)

This one hid from an earlier check of mine because Hugo's minifier strips attribute quotes — the rendered markup is <a href=/about>, so grepping for href="/about" finds nothing.

2. linkedin:* meta tags — structured data errors on all 49 pages

<meta property="linkedin:card" content="summary_large_image">
<meta property="linkedin:title" ...>

There is no linkedin: vocabulary. LinkedIn reads the standard Open Graph tags, which were already sitting directly above these. They look like a copy of the twitter:card block with the name changed — but twitter:* correctly uses name="", while these use property="", which declares RDFa properties in an undefined prefix. That is what validators flag.

Removed. The twitter:* and og:* tags are untouched, so nothing changes for any real consumer.

3. CSS was never minified — 98 findings

minifyOutput = true does not reach assets published through Hugo Pipes, so the fingerprinted bundles shipped raw. Confirmed against production rather than assumed:

live  /css/styles.1f4456...css   76303 bytes, 3827 lines

Byte-identical to an unminified local build. With | minify in the pipeline:

before after
styles.css 76,303 57,418
native.css 4,497 1,982

Worth noting CLAUDE.md claims "Hugo minifyOutput = true handles HTML/JS/CSS" — that is not true for pipeline assets, and is how this went unnoticed.

Verification

  • production build (hugo --minify --environment production) succeeds; all pages present
  • 1069 JSON-LD blocks across 479 pages, 0 malformed
  • no property="linkedin anywhere in the output
  • sitemap unchanged at 314 URLs

Not fixed — these need your call

Cloudflare Email Obfuscation. Every page links to /cdn-cgi/l/email-protection, which returns 404 to crawlers. That is the reported "4xx error" and the "broken internal links". It is a zone-level Cloudflare setting, not a repo change — disabling Email Address Obfuscation fixes it site-wide. The address it protects (hello@sbomify.com) is already published openly, so the protection is buying very little.

Duplicate meta descriptions (12 pages). No exact duplicates exist — the tag/category descriptions differ only by the term (...tagged sbom: vs ...tagged pqc:), so SEMrush scores them as near-identical. Same root cause as "low word count" (7) and "low text to HTML ratio" (17), which are also mostly tag pages. The real choice is whether taxonomy pages should be indexed at all; noindex on them would clear all three groups at once, but that is an SEO judgement call, not a bug fix.

Redirect chains from http:// and www.. http://www.sbomify.com/about takes 3 hops. Edge configuration, not Hugo.

HSTS missing on 2 pages — also edge configuration.

vpetersson-bot and others added 4 commits August 2, 2026 07:08
Three site-wide problems, all in shared templates so each one multiplied
across every page.

Internal links had no trailing slash. The nav in data/main.yml, the
footer, the case-study template and several content files all linked to
/about rather than /about/, and every one of those 301s. Six nav links
on 49 pages is most of the 299 permanent redirects and 50 redirect
chains the crawl reported. Hugo's canonical form is the trailing slash;
everything now uses it.

The linkedin:* meta tags are removed. There is no such vocabulary --
LinkedIn reads the standard Open Graph tags, which were already present
next to them. Emitting them with property="" declared RDFa properties in
an undefined "linkedin" prefix, which is what produced "structured data
that contains markup errors" on all 49 pages. The twitter:* tags are
unaffected: those correctly use name="".

CSS is now minified. minifyOutput=true does not reach assets published
through Hugo Pipes, so the fingerprinted bundles shipped unminified --
confirmed against production, where styles.css is byte-identical to an
unminified local build. 76303 -> 57418 bytes, and native.css 4497 ->
1982.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eg9RgyY4z7SUnf3yNQJnsu
…ptions

Redirect chains. With internal links now canonical, the only multi-hop
links left on every page were the app and Slack CTAs:

  https://app.sbomify.com  -> /login/ -> /accounts/oidc/keycloak/login/
                           -> auth.sbomify.com   (3 hops)
  https://sbomify.com/slack -> join.slack.com -> sbomify.slack.com
                           -> 403               (2 hops)

Both are auth/invite flows, not content, and both are inherent to the
services behind them -- the OIDC handshake cannot be collapsed and the
Slack invite is Slack's own. They are marked rel="nofollow" instead:
crawlers stop following them, which is the correct treatment for a login
endpoint anyway, since it is neither indexable nor a link-equity target.

Taxonomy descriptions. Every term page read "All sbomify blog posts
tagged X: articles on SBOMs, software supply chain security, and
compliance", differing only in X -- 0.952 mean pairwise similarity
across the flagged pages, with all 28 pairs above 0.90. They are now
built from what the term actually holds: the post count and the newest
headline. Mean similarity drops to 0.774 and only 9 pairs remain above
0.90, all of them tags that genuinely share a newest post.

Straight quotes in post titles are folded to single quotes so a headline
like v26.7.1 "Not Affected" cannot terminate the meta attribute early.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eg9RgyY4z7SUnf3yNQJnsu
Reverts rel="nofollow" on the app.sbomify.com CTAs. The Slack invite
keeps it: that one ends in a 403 for crawlers and is a third-party
invite flow, not a destination we want indexed.

The consequence is that app.sbomify.com stays a reported redirect chain,
because its first response is a 302 to /login/ and then on to Keycloak.
That cannot be fixed from this repo, and linking straight to /login/
would be worse -- it would force the login screen on already
authenticated visitors instead of letting / route them to their
dashboard. Collapsing it belongs in the app, where the anonymous "/"
case could redirect to the OIDC endpoint in a single hop.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eg9RgyY4z7SUnf3yNQJnsu
Same omission as the CSS: assets published through Hugo Pipes are not
covered by minifyOutput, so main.js shipped as 897 bytes of readable
source. 897 -> 604 bytes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eg9RgyY4z7SUnf3yNQJnsu
@vpetersson
vpetersson merged commit dd0d929 into sbomify:master Aug 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants