Fix SEO issues from the SEMrush crawl - #121
Merged
Merged
Conversation
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
approved these changes
Aug 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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/aboutrather than/about/. Every one 301s:Six nav links × 49 pages accounts for almost exactly the 299 "permanent redirects" reported. Now all canonical:
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 forhref="/about"finds nothing.2.
linkedin:*meta tags — structured data errors on all 49 pagesThere is no
linkedin:vocabulary. LinkedIn reads the standard Open Graph tags, which were already sitting directly above these. They look like a copy of thetwitter:cardblock with the name changed — buttwitter:*correctly usesname="", while these useproperty="", which declares RDFa properties in an undefined prefix. That is what validators flag.Removed. The
twitter:*andog:*tags are untouched, so nothing changes for any real consumer.3. CSS was never minified — 98 findings
minifyOutput = truedoes not reach assets published through Hugo Pipes, so the fingerprinted bundles shipped raw. Confirmed against production rather than assumed:Byte-identical to an unminified local build. With
| minifyin the pipeline:Worth noting
CLAUDE.mdclaims "HugominifyOutput = truehandles HTML/JS/CSS" — that is not true for pipeline assets, and is how this went unnoticed.Verification
hugo --minify --environment production) succeeds; all pages presentproperty="linkedinanywhere in the outputNot 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;noindexon them would clear all three groups at once, but that is an SEO judgement call, not a bug fix.Redirect chains from
http://andwww..http://www.sbomify.com/abouttakes 3 hops. Edge configuration, not Hugo.HSTS missing on 2 pages — also edge configuration.