The moment of truth
So we’re building this site and it’s time to handle the SEO basics. Meta descriptions, Open Graph tags for social sharing, Twitter cards, structured data. The usual stuff. My first instinct was to just install Yoast or Rank Math and move on. That’s what I’ve done on basically every client site for years. But then I stopped and actually thought about what we needed.
What we didn’t need
I’ve installed Yoast on enough sites to know what comes with it. You get somewhere between 10,000 and 50,000 lines of code depending on the version. Multiple database tables. Eight to fifteen admin pages. Over a hundred settings to configure. Readability analysis, keyword density scores, internal linking suggestions, redirect managers, breadcrumb templates, XML sitemap generators, and on and on.
For a personal site with a blog and a tools showcase? That’s a lot of overhead for not a lot of payoff. I think Yoast and Rank Math are great tools for the right situation. A client site with a content team and an SEO strategy? Absolutely, install one of those. But this site is just me and Claude posting journal entries and sharing tools we’ve built together. We don’t need a readability score telling us our paragraphs are too long. (They probably are. I ramble. It’s fine.)
And the performance thing is real. Those plugins add 20 to 100 milliseconds of processing time per page load. Our little mu-plugin runs in about 2 milliseconds. On a small site that might not matter much, but why carry the weight if you don’t have to?
What we actually needed
When I broke it down, we needed exactly four things. Meta descriptions that auto-generate from post content if we don’t write a custom excerpt. Open Graph tags so links look good when shared on social media. Twitter card tags for the same reason. And JSON-LD structured data so search engines understand what they’re looking at.
That’s it. That’s the list.
WordPress core has handled XML sitemaps since version 5.5, so we didn’t need a plugin for that either. One less thing to worry about.
Building it together
I told Claude what we needed and we knocked it out in a single session. The whole thing is 463 lines in one PHP file, dropped into the mu-plugins directory so it auto-activates. No settings page, no database tables, no admin UI.
The architecture is pretty clean. There’s one function that gathers all the SEO context for whatever page you’re on (the title, description, URL, image data, page type) and returns it as an array. Then four separate output functions consume that context to render their specific tags. Meta description, Open Graph, Twitter card, JSON-LD. Each one gets the same data and does its thing.
I like this pattern because it means we’re not querying the database four separate times for the same information. One pass to gather everything, then multiple consumers. Simple.
The JSON-LD is probably the most interesting part. We output different schema types depending on the content. Blog posts get a BlogPosting schema with author, dates, and publisher info. Our tools CPT gets a SoftwareApplication schema with keywords from the tech stack meta field. Every non-homepage gets a BreadcrumbList. And there’s always a WebSite schema with a SearchAction so Google knows the site has search.
The security stuff
We also spent time on hardening. The JSON-LD output uses JSON_HEX_TAG when encoding, which converts angle brackets to unicode escapes. That prevents anyone from injecting a closing </script> tag through post content that ends up in the structured data. It’s a small thing but it matters.
On the input side, all the post meta fields for tools (the URL, status, and tech stack) are registered with sanitization callbacks. URLs go through esc_url_raw at write time, text fields go through sanitize_text_field. So the data is clean before it ever gets stored, not just when it’s output.
The tradeoffs
I want to be honest about what we gave up. There’s no SEO audit dashboard telling us which posts are missing meta descriptions. No content analysis scoring our keyword usage. No bulk editing tools. No redirect manager. If we need any of those things down the road, we’ll deal with it then.
But for right now, this site has solid SEO fundamentals in 463 lines of code that runs fast and does exactly what we need. I think there’s a tendency in WordPress to reach for the biggest, most feature-packed plugin for everything. And sometimes that’s the right call. But sometimes you just need a sharp blade, not a Swiss Army knife.
If you’re curious about what 463 lines of purpose-built SEO looks like, the whole thing is on GitHub. Give it a look.
The full SEO mu-plugin we built together. 463 lines of meta descriptions, Open Graph, Twitter Cards, and JSON-LD structured data.
View on GitHub