SEO pre Astro blog — OG obrázky, JSON-LD a noindex
Ako som pridal dynamické Open Graph obrázky, Twitter card meta, JSON-LD štruktúrované dáta a noindex pre stránkovanie. Building in public, tretí diel.
Web už má analytics, sitemap aj RSS. Teraz riešim, ako vyzerá, keď ho niekto zdieľa na sociálnych sieťach — a čo o ňom vie Google.
Problém
Keď som zdieľal blog post na Reddit, zobrazil sa len holý link. Žiadny obrázok, žiadny popis. Google zas nevedel, že ide o blog článok s autorom a dátumom. A stránky stránkovania (/blog/2) zbytočne riedili crawl budget.
Tri veci na riešenie:
- Open Graph obrázky — branded preview pri zdieľaní
- JSON-LD štruktúrované dáta — rozšírené výsledky v Google
- noindex — vyčistiť index od stránok bez unikátneho obsahu
Dynamické OG obrázky
Prečo nie statické?
Mohol som vytvoriť obrázky manuálne v Canva alebo Figma. Ale pri každom novom poste by som to musel opakovať — otvoriť editor, exportovať PNG, nahrať do repo.
satori + sharp
Satori od Vercelu konvertuje JSX-like štruktúru na SVG. Sharp potom SVG → PNG. Celé to beží pri builde ako Astro API route.
npm install satori sharp
Jadro generátora v src/lib/og.ts:
import satori from "satori";
import sharp from "sharp";
const interFont = readFileSync(join(process.cwd(), "src/lib/Inter-SemiBold.ttf"));
export async function generateOgImage({ title, date, tags }: OgOptions): Promise<Buffer> {
const svg = await satori(
{ /* JSX-like štruktúra s flexbox layoutom */ },
{ width: 1200, height: 630, fonts: [{ name: "Inter", data: interFont }] },
);
return sharp(Buffer.from(svg)).png().toBuffer();
}
Dizajn
OG obrázok má:
- Dark gradient pozadie s jemnými grid čiarami
- Gradient border po obvode (cyan → violet)
- Glow efekty v rohoch
- Branding
rebjak.coms cyan bodkou hore - Titulok s gradient accent čiarou
- Dátum a tag štítky dole
Takto vyzerá výsledok:
Dôležité: satori nepodporuje .woff fonty — treba .ttf. Stiahol som Inter SemiBold z Google Fonts CDN.
Astro endpointy
Každá jazyková verzia má vlastný endpoint:
src/pages/blog/og/[slug].png.ts → /blog/og/staviame-rebjak-com.png
src/pages/en/blog/og/[slug].png.ts → /en/blog/og/building-rebjak-com.png
src/pages/og-default.png.ts → /og-default.png (fallback)
getStaticPaths prechádza content collection a generuje PNG pre každý post pri builde.
Napojenie na BaseLayout
<meta property="og:image" content={ogImage ?? new URL("/og-default.png", Astro.site).href} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content={ogImage ?? new URL("/og-default.png", Astro.site).href} />
Každý blog post si vypočíta vlastnú URL:
const ogImage = new URL(`/blog/og/${slug}.png`, Astro.site).href;
JSON-LD štruktúrované dáta
Google Schema.org BlogPosting — pridané priamo do stránok blog postov:
const jsonLd = {
"@context": "https://schema.org",
"@type": "BlogPosting",
headline: post.data.title,
description: post.data.description,
image: ogImage,
datePublished: post.data.pubDate.toISOString(),
author: { "@type": "Person", name: "Jozef Rebjak", url: "https://rebjak.com" },
url: canonicalUrl,
inLanguage: "sk",
keywords: post.data.tags,
};
V BaseLayout:
{jsonLd && (
<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
)}
Google tak vie, že stránka je článok s konkrétnym autorom, dátumom a kľúčovými slovami. Môže zobraziť rozšírené výsledky v SERPe.
SERP (Search Engine Results Page) je stránka výsledkov vyhľadávania. V praxi to znamená, že pri vašom článku v Google sa môže objaviť meno autora, dátum publikácie alebo obrázok — namiesto obyčajného modrého linku.
noindex pre stránkovanie a tag stránky
Stránky ako /blog/2, /blog/tag/astro alebo /blog/tags sú navigačné — nemajú unikátny obsah. Keď ich Google indexuje, riedí crawl budget a vytvára duplicitný obsah bez pridanej hodnoty.
Riešenie je jednoduché — noindex, follow:
<!-- BaseLayout.astro -->
{noindex && <meta name="robots" content="noindex, follow" />}
- Stránkovanie (
[...page].astro):noindex={page.currentPage > 1}— prvá strana sa indexuje, ďalšie nie - Tag stránky (
tag/[tag].astro): vždynoindex - Zoznam tagov (
tags.astro): vždynoindex
follow je dôležité — Google stále sleduje linky na týchto stránkach a objavuje nové články.
RSS s OG obrázkami
Bonus: RSS feed teraz obsahuje <enclosure> s OG obrázkom pre každý článok:
customData: `<enclosure url="${ogUrl}" type="image/png" length="0" />`,
RSS čítačky ako Feedly alebo Inoreader tak zobrazia obrázok pri každom článku.
Výsledok
Po builde sa vygeneruje 11 PNG obrázkov (5 SK + 5 EN + 1 default), každý ~60-80 KB. Build trvá ~3 sekundy.
Čo sa zmenilo:
- Zdieľanie na Reddit/X/LinkedIn/Slack/Discord zobrazuje branded preview s titulkom
- Google rozumie štruktúre článkov (BlogPosting schéma)
- Index je čistý — len skutočný obsah, nie stránkovanie a tag stránky
- RSS čítačky zobrazujú obrázky
Čo ďalej?
- Performance audit (Core Web Vitals)
- Breadcrumb schema
- Blog post series schema (isPartOf)