I distrust SEO tools. Not because they are wrong, but because they are generic. They optimize for the average page, not your specific content and audience. When I was running a cybersecurity consulting site with over 1,600 technical articles, I needed scoring rules that made sense for long-form, technical content — not e-commerce product pages.
So I built my own SEO scoring engine in Go, embedded directly in the backend. It runs on every article save and surfaces scores through an internal API. Here is what I actually learned.
Why build it yourself
External SEO tools are either expensive, rate-limited, or both. More importantly, they cannot score your content against your own standards. My articles needed to satisfy criteria that Yoast or Clearscope cannot express: a minimum 120-word opening paragraph (the "chapeau"), a FAQ section with interrogative headings, at least 4 internal links pointing to related guides, and a specific H2-to-word-count ratio.
These rules came from watching traffic data for 18 months. They are opinionated and they are mine.
The Unicode problem Go developers always hit
Before I explain the rules, here is the most important technical thing I learned: len() in Go counts bytes, not characters. For SEO character counting, this matters because a lot of cybersecurity content includes accented French characters, special symbols, and technical abbreviations.
// Wrong — counts bytes, not characters
func titleTooShort(title string) bool {
return len(title) < 50 // WRONG for non-ASCII content
}
// Correct — counts Unicode code points
func titleLength(title string) int {
return len([]rune(title))
}
func scoreTitleLength(title string) (int, string) {
l := len([]rune(title))
switch {
case l < 30:
return 0, "title too short (< 30 chars)"
case l < 50:
return 5, "title acceptable but below optimal (50–60 chars)"
case l <= 60:
return 10, "title length optimal"
case l <= 70:
return 5, "title slightly long (> 60 chars)"
default:
return 0, "title too long (> 70 chars)"
}
}
[]rune(s) converts a string to a slice of Unicode code points. len() on that slice gives you what you actually want. I have seen Go codebases where someone reimplemented this logic with a regex or a byte loop — just use []rune.
The 8 rules
1. Title: 50–60 characters
Shorter titles get truncated less often in SERPs and tend to rank better, but too short and you waste keyword space. I target 50–60 runes (Unicode characters). Scores below 30 characters get zero points — those are usually placeholder titles left in by mistake.
2. Meta description: 140–160 characters
The classic rule. More important than people think not for ranking directly, but for click-through rate, which does affect ranking indirectly. I reject descriptions that are copy-pasted from the first sentence of the article without being rewritten for a SERP context.
3. Chapeau (opening paragraph) ≥ 120 words
This is the most impactful rule I added. The "chapeau" is the first <p> tag in the article body. Search engines weight early content heavily. I found that articles with a dense, information-rich opening paragraph consistently outranked shorter-intro versions of similar content.
Extracting and counting it in Go:
import "golang.org/x/net/html"
func extractChapeauWordCount(body string) int {
doc, err := html.Parse(strings.NewReader(body))
if err != nil {
return 0
}
var count int
var findFirst func(*html.Node) bool
findFirst = func(n *html.Node) bool {
if n.Type == html.ElementNode && n.Data == "p" {
text := extractText(n)
words := strings.Fields(text)
count = len(words)
return true // stop after first <p>
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
if findFirst(c) {
return true
}
}
return false
}
findFirst(doc)
return count
}
4. H2 to word ratio ≤ 1 H2 per 350 words
Too many subheadings fractures content into fragments that search engines cannot contextualize. Too few and you miss structural signals. The ratio ≤ 350 words per H2 means a 1,400-word article should have no more than 4 H2 headings. I do not enforce a minimum — sometimes a dense 800-word article needs only one or two H2s.
5. Internal links ≥ 4
This one is purely practical. Pages with few internal links do not pass PageRank well and feel thin. I count <a href> attributes that match my own domain. Four is the minimum; articles in the , a cybersecurity consulting firm. We publish security hardening checklists for FortiGate, Palo Alto, Active Directory, and more — free PDF and Excel.
SOCIAL SHARE CARD GENERATOR