Improve SEO and social card wiring
Add SearchAction and optional Product JSON-LD, per-page OG images/alts, and document the og build and cache headers. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -282,6 +282,32 @@ OpenGraph and Twitter tags, a social card, a self-referencing canonical, a full
|
||||
set and a JSON-LD graph. `sitemap.xml` lists every indexable one with its `xhtml:link`
|
||||
alternates, and `robots.txt` sits beside it.
|
||||
|
||||
### Social cards
|
||||
|
||||
`pnpm build` starts with `pnpm og` (`web/scripts/og/build-og.mjs`), which renders one
|
||||
1200x630 PNG per mint and federation into `web/public/og/` from the same API data the
|
||||
pages use — satori lays the card out and turns the text into glyph paths (the six
|
||||
static TTFs under `web/scripts/og/fonts/` are the same faces the site uses),
|
||||
`@resvg/resvg-js` rasterises it. No browser involved; a full run of ~70 cards takes a
|
||||
few seconds, and a rerun with unchanged data renders nothing: each card's inputs are
|
||||
hashed into its filename (`mint.example.com.a1b2c3d4e5.png`) and
|
||||
`web/src/generated/og-manifest.json` records what is already on disk. The hashed name
|
||||
is also the cache-busting story — link-preview scrapers cache an `og:image` by URL, so
|
||||
a mint whose rating moved or that went offline gets a new URL on the next scheduled
|
||||
rebuild, while the stable-named `default.png` (brand plus network stats, used by every
|
||||
non-mint page) is served with a one-hour cache instead (see the nginx block below).
|
||||
|
||||
The images are one English render shared by every locale; titles, descriptions and alt
|
||||
text translate per page. Relative times stay out of the PNGs on purpose — a "3d ago"
|
||||
would go stale inside a static file — so the cards carry absolute month/year stamps,
|
||||
and the one exception, the "Offline {n}d" chip, is derived from a day-granular clock so
|
||||
it regenerates at most once a day.
|
||||
|
||||
`pnpm og:fixtures` renders the six edge cases in `web/scripts/og/fixtures.mjs`
|
||||
(30-character name, no icon, zero reviews, offline, melt only, announced-only
|
||||
federation) into `web/og-fixtures/` at a pinned timestamp; those snapshots are
|
||||
committed, so eyeball them after any template change.
|
||||
|
||||
### What is indexed, and what is not
|
||||
|
||||
One rule, in `web/src/lib/seo.ts`, decides it, and it covers both ecosystems: a page is
|
||||
@@ -739,14 +765,27 @@ server {
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# The social card, the icons and the manifest. Not fingerprinted, since their names
|
||||
# are referenced from outside the site, so a week with revalidation rather than a
|
||||
# year of immutability.
|
||||
# The favicons and the manifest. Not fingerprinted, since their names are referenced
|
||||
# from outside the site, so a week with revalidation rather than a year of immutability.
|
||||
location ~* ^/(og\.png|favicon\.(ico|svg)|apple-touch-icon\.png|icon-\d+\.png|site\.webmanifest)$ {
|
||||
expires 7d;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
|
||||
# The generated social cards (`pnpm og`). The default card keeps a stable name and
|
||||
# changes with the site's stats, so it gets an hour; every per-mint card carries a
|
||||
# content hash in its filename and a stale hash is never referenced again, so those
|
||||
# are immutable. (The hash is also what actually refreshes link previews: the big
|
||||
# scrapers cache an og:image by URL and ignore these headers.)
|
||||
location = /og/default.png {
|
||||
add_header Cache-Control "public, max-age=3600";
|
||||
}
|
||||
|
||||
location /og/ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Same-origin API and icons, matching the default empty PUBLIC_API_URL. Drop these two
|
||||
# blocks only if you build with PUBLIC_API_URL pointing at a separate API host.
|
||||
location /api/ {
|
||||
|
||||
Reference in New Issue
Block a user