From 7bcb647c7f2ad28bb6151e6286283de04d8de892 Mon Sep 17 00:00:00 2001 From: iamdoubz <> Date: Fri, 26 Jun 2026 02:55:42 -0500 Subject: [PATCH] SEO + GEO: sitemap, robots, JSON-LD, canonical & article metadata, llms.txt - app/sitemap.ts, app/robots.ts (allows AI crawlers), app/llms.txt route - Person + WebSite JSON-LD sitewide; BlogPosting + BreadcrumbList on posts - Per-page canonical URLs; rich OG/Twitter/article metadata on blog posts - Fix orphaned config.seo.ogImage (/og/default.jpg -> /images/og.png) Strategy docs in designs/ (gitignored): seo-strategy.md, geo-strategy.md --- public/images/favicon.png | Bin 0 -> 24516 bytes public/images/og.png | Bin 0 -> 16376 bytes src/app/blog/[slug]/page.tsx | 46 ++++++++++++++++++++++++++++++++++- src/app/blog/page.tsx | 4 +-- src/app/interests/page.tsx | 4 +-- src/app/layout.tsx | 35 +++++++++++++++++++++++--- src/app/llms.txt/route.ts | 30 +++++++++++++++++++++++ src/app/robots.ts | 17 +++++++++++++ src/app/sitemap.ts | 23 ++++++++++++++++++ src/app/stack/page.tsx | 4 +-- src/components/JsonLd.tsx | 9 +++++++ src/lib/config.ts | 5 ++-- 12 files changed, 165 insertions(+), 12 deletions(-) create mode 100644 public/images/favicon.png create mode 100644 public/images/og.png create mode 100644 src/app/llms.txt/route.ts create mode 100644 src/app/robots.ts create mode 100644 src/app/sitemap.ts create mode 100644 src/components/JsonLd.tsx diff --git a/public/images/favicon.png b/public/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..5656cc8987519544a8d5e36217f02ebb43091579 GIT binary patch literal 24516 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 QjEKeg(eQ`6-&+U#e@?7-8UO$Q literal 0 HcmV?d00001 diff --git a/public/images/og.png b/public/images/og.png new file mode 100644 index 0000000000000000000000000000000000000000..5c62fb9f189e52b46d97d5e19052feccc38cf667 GIT binary patch literal 16376 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 literal 0 HcmV?d00001 diff --git a/src/app/blog/[slug]/page.tsx b/src/app/blog/[slug]/page.tsx index ad1d8c7..5991d93 100644 --- a/src/app/blog/[slug]/page.tsx +++ b/src/app/blog/[slug]/page.tsx @@ -3,6 +3,7 @@ import { getAllPosts, getPostBySlug } from "@/lib/blog"; import { siteConfig } from "@/lib/config"; import { MDXRemote } from "next-mdx-remote/rsc"; import { mdxComponents } from "@/components/blog/MDXComponents"; +import { JsonLd } from "@/components/JsonLd"; import rehypeHighlight from "rehype-highlight"; import Link from "next/link"; import type { Metadata } from "next"; @@ -19,9 +20,29 @@ export async function generateMetadata({ params }: Props): Promise { const { slug } = await params; const post = getPostBySlug(slug); if (!post) return {}; + + const url = `${siteConfig.url}/blog/${slug}`; + const image = post.image ?? siteConfig.seo.ogImage; + return { - title: `${post.title} | ${siteConfig.name}`, + title: post.title, description: post.description, + alternates: { canonical: `/blog/${slug}` }, + openGraph: { + type: "article", + title: post.title, + description: post.description, + url, + publishedTime: post.date || undefined, + authors: [siteConfig.name], + images: [{ url: image, alt: post.title }], + }, + twitter: { + card: "summary_large_image", + title: post.title, + description: post.description, + images: [image], + }, }; } @@ -35,8 +56,31 @@ export default async function BlogPostPage({ params }: Props) { const prev = idx < allPosts.length - 1 ? allPosts[idx + 1] : null; const next = idx > 0 ? allPosts[idx - 1] : null; + const url = `${siteConfig.url}/blog/${slug}`; + const articleSchema = { + "@context": "https://schema.org", + "@type": "BlogPosting", + headline: post.title, + description: post.description, + datePublished: post.date || undefined, + author: { "@type": "Person", name: siteConfig.name, url: siteConfig.url }, + image: post.image ?? siteConfig.seo.ogImage, + mainEntityOfPage: url, + url, + }; + const breadcrumbSchema = { + "@context": "https://schema.org", + "@type": "BreadcrumbList", + itemListElement: [ + { "@type": "ListItem", position: 1, name: "Blog", item: `${siteConfig.url}/blog` }, + { "@type": "ListItem", position: 2, name: post.title, item: url }, + ], + }; + return (
+ + v.startsWith("http"), +); + +const personSchema = { + "@context": "https://schema.org", + "@type": "Person", + name: siteConfig.name, + url: siteConfig.url, + jobTitle: siteConfig.seo.jobTitle, + description: siteConfig.description, + ...(sameAs.length ? { sameAs } : {}), +}; + +const websiteSchema = { + "@context": "https://schema.org", + "@type": "WebSite", + name: siteConfig.name, + url: siteConfig.url, + author: { "@type": "Person", name: siteConfig.name }, +}; + const inter = Inter({ variable: "--font-inter", subsets: ["latin"], @@ -19,9 +42,13 @@ const jetbrainsMono = JetBrains_Mono({ }); export const metadata: Metadata = { - title: siteConfig.name, + title: { + default: siteConfig.name, + template: `%s | ${siteConfig.name}`, + }, description: siteConfig.description, metadataBase: new URL(siteConfig.url), + alternates: { canonical: "/" }, openGraph: { title: siteConfig.name, description: siteConfig.description, @@ -30,7 +57,7 @@ export const metadata: Metadata = { type: "website", images: [ { - url: "/images/og.png", + url: siteConfig.seo.ogImage, width: 1200, height: 630, alt: siteConfig.name, @@ -41,7 +68,7 @@ export const metadata: Metadata = { card: "summary_large_image", title: siteConfig.name, description: siteConfig.description, - images: ["/images/og.png"], + images: [siteConfig.seo.ogImage], }, icons: { icon: [ @@ -66,6 +93,8 @@ export default function RootLayout({ className={`${inter.variable} ${jetbrainsMono.variable}`} > + + ${title}. ${description} + +## Pages + +- [Home](${url}/): About, projects, and contact for ${name} +- [Blog](${url}/blog): Posts on code, design, and building things on the web +- [Stack](${url}/stack): Self-hosted services and homelab infrastructure +- [Interests](${url}/interests): Live feed of activity across the web + +## Blog posts +${posts + .map((p) => `- [${p.title}](${url}/blog/${p.slug}): ${p.description}`) + .join("\n")} +`; + + return new Response(body, { + headers: { "Content-Type": "text/plain; charset=utf-8" }, + }); +} diff --git a/src/app/robots.ts b/src/app/robots.ts new file mode 100644 index 0000000..4e05301 --- /dev/null +++ b/src/app/robots.ts @@ -0,0 +1,17 @@ +import type { MetadataRoute } from "next"; +import { siteConfig } from "@/lib/config"; + +// ponytail: single allow-all rule covers AI crawlers (GPTBot, PerplexityBot, +// ClaudeBot, Google-Extended, etc.) too — we WANT them for GEO. Add per-bot +// rules only if a specific crawler ever needs blocking. +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: "*", + allow: "/", + disallow: "/api/", + }, + sitemap: `${siteConfig.url}/sitemap.xml`, + host: siteConfig.url, + }; +} diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts new file mode 100644 index 0000000..7ecc437 --- /dev/null +++ b/src/app/sitemap.ts @@ -0,0 +1,23 @@ +import type { MetadataRoute } from "next"; +import { siteConfig } from "@/lib/config"; +import { getAllPosts } from "@/lib/blog"; + +export default function sitemap(): MetadataRoute.Sitemap { + const base = siteConfig.url; + + const staticRoutes = ["", "/blog", "/stack", "/interests"].map((path) => ({ + url: `${base}${path}`, + lastModified: new Date(), + changeFrequency: "weekly" as const, + priority: path === "" ? 1 : 0.7, + })); + + const posts = getAllPosts().map((post) => ({ + url: `${base}/blog/${post.slug}`, + lastModified: post.date ? new Date(post.date) : new Date(), + changeFrequency: "monthly" as const, + priority: 0.6, + })); + + return [...staticRoutes, ...posts]; +} diff --git a/src/app/stack/page.tsx b/src/app/stack/page.tsx index 357f812..bfc9ced 100644 --- a/src/app/stack/page.tsx +++ b/src/app/stack/page.tsx @@ -1,10 +1,10 @@ -import { siteConfig } from "@/lib/config"; import type { Metadata } from "next"; import { StackPageClient } from "./StackPageClient"; export const metadata: Metadata = { - title: `Stack | ${siteConfig.name}`, + title: "Stack", description: "Self-hosted services and infrastructure I run.", + alternates: { canonical: "/stack" }, }; export default function StackPage() { diff --git a/src/components/JsonLd.tsx b/src/components/JsonLd.tsx new file mode 100644 index 0000000..829a3d0 --- /dev/null +++ b/src/components/JsonLd.tsx @@ -0,0 +1,9 @@ +// Renders a JSON-LD structured-data script. Used for Person/WebSite/BlogPosting. +export function JsonLd({ data }: { data: Record }) { + return ( +