
TL;DR
- Sanity outputs no HTML of its own, so your schemas and frontend have to output every tag search crawlers need.
- Define one reusable SEO object type with meta title, description, canonical, robots and social fields, and embed it in every indexable document.
- Resolve metadata through a fallback chain, from SEO overrides to editorial fields to global settings, and return it from generateMetadata so tags ship in the initial HTML.
- Use the same GROQ queries for sitemaps and production routing, and exclude drafts and noindex documents from every sitemap.
- Model noIndex and hideFromLists as two fields, and generate robots.txt dynamically so staging and preview hosts return a blanket disallow.
- Tag every Sanity query, then let Sanity webhooks call a Next.js route that runs revalidateTag for those tags.
How SEO Works in Headless Sanity CMS
In a headless setup, SEO is built by your schemas and your frontend, not by the CMS. Sanity keeps content in its Content Lake and outputs no HTML of its own, so it won’t rescue or ruin your search visibility on its own.
For a CTO, this means SEO stops being a plugin setting and becomes part of your architecture. Schema design, rendering strategy and cache invalidation decide whether pages get indexed, and the risk peaks during a migration. The checklist below is what your team should have in place before launch, and what to ask a vendor to prove. The headless pipeline rests on two pillars:
- Content modeling in Sanity Studio: Editors enter page-level titles, Open Graph images, search directives, and schema fields into schema objects. Sanity doesn’t generate HTML tags out of the box, so your schemas have to define every attribute search crawlers need.
- Frontend rendering and delivery: Frontend frameworks (such as Next.js, Remix, or Nuxt) query Sanity through GROQ or GraphQL. The application code pulls those properties out to assemble crawlable server-side HTML, accurate JSON-LD blocks, hreflang links, and real-time sitemaps.
Teams moving to Sanity often expect that typing a title will fill in the metadata tags in the HTML head. It won’t: if you don’t query those fields in your page layouts, search bots will see empty titles, missing meta descriptions, and default host canonicals. Treat SEO as an engineering requirement from day one. For the Next.js side of the same work, see the Next.js SEO guide.
SEO Best Practices for Sanity CMS Content Models
A Sanity content model for SEO gives every document that resolves to a public URL the same set of search fields: meta title, description, canonical, robots flags and social images. Define them once as a reusable object, validate their length in Studio, and let the frontend fill gaps from editorial fields, so no public page ships without tags.
Building a Reusable SEO Object
Rather than declaring individual meta fields again and again across document types, define one reusable SEO object type. You write it once and embed it in every indexable document. It should cover a few core properties:
- Meta Title and Description: Dedicated string and text fields for search result snippets, kept separate from internal editorial headlines.
- Canonical URL Override: An optional field that lets editors set self-referential or cross-domain URLs when republishing syndicated material.
- Robots Directives: A
noIndexboolean and related flags that tell frontend renderers which indexing meta tags to output. - Social Share Previews: Open Graph and Twitter card fields, including custom social titles, descriptions, and 1200×630 pixel promotional images.
- Structured Data Inputs: Fields for selecting schema types (such as Article, FAQ, or Product) and mapping entities for rich results.
A minimal version of the object, defined with Sanity’s object type:
import { defineField, defineType } from 'sanity';
export const seo = defineType({
name: 'seo',
title: 'SEO',
type: 'object',
fields: [
defineField({ name: 'metaTitle', type: 'string', validation: (Rule) => Rule.max(60).warning('Keep titles under 60 characters.') }),
defineField({ name: 'metaDescription', type: 'text', rows: 3, validation: (Rule) => Rule.min(120).max(160).warning('Aim for 120 to 160 characters.') }),
defineField({ name: 'canonicalUrl', type: 'url' }),
defineField({ name: 'noIndex', type: 'boolean', initialValue: false }),
defineField({ name: 'hideFromLists', type: 'boolean', initialValue: false }),
defineField({ name: 'ogImage', type: 'image' }),
],
});Add it to a document with defineField({ name: 'seo', type: 'seo' }). Embedding this object in public document schemas gives technical teams a predictable data contract. Add a site-wide settings singleton next to it, where editors manage default metadata, site verification tokens and fallback images without touching code.
Sanity doesn’t fix SEO automatically. Your architecture does.
One Search Intent per Document
Each Sanity document should serve one user intent instead of piling unrelated topics onto a single URL. Sanity schemas are unconstrained, which makes it tempting to build sprawling page builders that bundle several topics onto one page; that dilutes topical relevance and leads to keyword cannibalization and thin or duplicate pages. Reserve top-level documents for self-contained topics and use reference fields to link supporting subtopics.
Title and Description Length Limits
Search engines measure snippet space in pixels, not characters, but character counts are still the practical standard for authors in Sanity Studio. For reliable SERP rendering, keep title tags between 50–60 characters and meta descriptions between 120–160 characters. Put your core keyword up front. Google writes its own title links and snippets when yours don’t fit the query, so these limits improve the odds rather than fix the display.
Inside Sanity Studio, you can automate quality control by adding validation rules to the reusable SEO schema fields:
- Meta Title Validation: Add
validation: (Rule) => Rule.max(60).warning('Titles longer than 60 characters risk truncation in search results.')to the title string. Editors get instant feedback without being blocked from publishing. - Meta Description Validation: Set a rule on the description text area, such as
Rule.min(120).max(160).warning('Descriptions between 120 and 160 characters maximize SERP visibility.').
Metadata Overrides and Fallback Order
Content creators shouldn’t have to paste the same headline, summary, and teaser image into several fields just to satisfy search bots and social platforms. A better Sanity schema treats explicit SEO and social fields as optional overrides and leaves the heavy lifting to a predictable fallback chain in your frontend:
- Meta Title Fallback: The renderer checks the dedicated title override first, then the document’s main title or editorial headline, and finally the global site title from global settings.
- Meta Description Fallback: It checks the description override, then the document’s intro summary or post excerpt, and finally a site-wide fallback description.
- Social Share (Open Graph) Fallback: Open Graph tags (og:title, og:description, og:image) look for specific Open Graph overrides first, then the dedicated SEO fields, then the document’s headline, summary, and main hero image, and only then a generic brand fallback asset.
The final resolved metadata has to be built and injected into the document head through server-side rendering (SSR) or static site generation (SSG). In the Next.js App Router, that is the job of generateMetadata: it fetches the document’s SEO fields from Sanity, applies the fallback chain, and returns the title, description, canonical (alternates.canonical), robots and Open Graph values, which Next.js renders into the initial HTML.
How to Structure Content for Search and AI
Search algorithms and generative AI engines split documents into discrete chunks of knowledge, so a Sanity content model that keeps semantic boundaries intact is easier for them to read. In practice that means a strict heading hierarchy in Portable Text, typed fields for FAQs and summaries, and the same GROQ projection behind every format you serve.
Establish a Strict Heading Hierarchy
Every template must output one H1 for the core topic, then H2 subtopics and nested H3 and H4 details. If you skip levels, like jumping from an H1 straight to an H3 inside a modular component, you break the logical chain that parsing bots and screen readers depend on. Configure Portable Text block definitions to allow only the heading levels suited to body text: H2, H3, and H4. For modular landing pages, add an explicit headingLevel select field to the module schemas, so publishers choose whether a module header renders as an H2 or H3 depending on where the module sits in the page outline.
Model Modular Entities: FAQs and Summaries
Give answers their own typed fields instead of burying them in running paragraphs:
- Structured FAQ Objects: Create a dedicated FAQ object schema with separate question (string) and answer (scoped Portable Text) fields. Grouping these items in a parent array lets search indexers pull definitive answers and makes it easy to generate accurate FAQPage JSON-LD. Google limited FAQ rich results to government and health sites in 2023 and stopped showing them in Search in May 2026, so treat FAQ markup as a signal for AI and search parsers, not a snippet.
- Executive Summaries and TL;DR Blocks: Give editorial document types a dedicated summary or
keyTakeawaysarray field, so key takeaways sit at the top of the document for human visitors and automated extractors.
Markdown Endpoints and llms.txt for AI Agents
AI user agents prefer lightweight, token-efficient formats. When a request carries an Accept: text/markdown header, the application can return a Markdown version at the same canonical URL. Both the web page and the Markdown response should query Sanity with the same GROQ projection, so what human users see and what retrieval systems extract stay identical, which removes cloaking risk and avoids duplicate URLs. Send a Vary: Accept header with both responses, so your CDN (Vercel’s Edge Network included) doesn’t serve the cached Markdown version to browsers or the HTML version to agents.
Files like llms.txt act as navigation aids, not proven ranking levers. If you publish one, drive it from a GROQ query, and let editors toggle a boolean field like featuredForLLM in Studio documents to mark which core guides or glossaries belong in it.
Topic Clusters and Internal Linking in Sanity
In Sanity, you can model topic clusters right in your schema, so link equity flows where you want it and crawlers follow contextual paths without hitting broken links or orphaned URLs. A topic reference ties each article to one pillar, and link annotations that point to document IDs keep internal links correct when slugs change.
Model Pillar and Cluster Relationships
A topic cluster pairs one broad core page with 8–12 cluster pages that dig into narrower subtopics. Define a dedicated taxonomy document type, such as topic, or add a single-select primaryTopic reference field to all publishing document types, so each article belongs to exactly one parent subject. Alongside it, add an array of relatedPages references to connect sibling pages within a cluster.
Automate Internal Links and Prevent Link Rot
Hardcoded internal URLs inside rich text break the moment an author tweaks a slug, creating redirect chains or 404 crawl errors. Sanity Studio avoids this with reference annotations in Portable Text:
- Reference Annotations in Portable Text: Set up custom link marks that point to Sanity document IDs instead of static href paths. When an editor changes a destination page’s slug, every internal link to that document resolves to the new URL with no manual work.
- Bidirectional Related Content Modules: Query reverse references in GROQ (such as
*[_type == "post" && references(^._id)]). Showing reciprocal links on child pages strengthens the crawl path back to the pillar and removes orphan pages.
Canonical URLs, Slugs and Redirects in Sanity
Standardizing slug generation, canonical tags, and redirects in Sanity tells search engines which page variant is the canonical one, even when query parameters, trailing slashes, or legacy paths serve the same content. Generate slugs from titles, compute a self-referential canonical by default, and keep redirects as Studio documents that marketing can edit without a deployment.
Programmatic Slug Generation and Nested Architectures
Sanity Studio generates slugs through the core slug schema type, which builds URL segments from source fields such as titles. A custom slugify function can normalize diacritics and force lowercase. For nested page hierarchies, an asynchronous function can query parent document references through GROQ to build full paths like /solutions/enterprise/analytics.
Canonical URL Configuration
Self-referential canonical tags are the main defense against index fragmentation caused by tracking parameters and different entry routes. Keep the manual override in the SEO object, but build fallback logic on the frontend that calculates the default canonical from your primary production domain and the resolved document slug. That removes HTTP and HTTPS inconsistencies and enforces one trailing-slash convention across all rendered pages. Google’s guide to consolidating duplicate URLs covers the signals in detail.
Managing Redirects Through Studio Schemas
Changing a published slug breaks existing external backlinks unless the old address redirects permanently to the new one. To give marketing teams control without a code deployment, create a dedicated redirect schema type in Sanity Studio:
- Custom Document Schema: Define fields for
sourcePath,destination(supporting both internal document references and external URL strings),statusCode(301 for permanent moves, 302 for temporary campaigns), and an optional regex toggle. With internal references, a redirect stays accurate even if the target document’s URL changes again. - Build-Time and Edge Delivery: Fetch the active redirect table with GROQ at build time, or cache it in an edge layer (such as Next.js Proxy, formerly Middleware, or Cloudflare Workers), so the redirect runs before the route renders.
- Wildcard and Regex Support: Support capture groups to migrate whole legacy folders, turning patterns like /blog/archive/:slug into /resources/:slug without writing individual entries for hundreds of older posts.
If you are moving an existing site to Sanity, the headless CMS SEO migration guide covers the redirect map and launch checks.
Sanity Sitemaps, Robots and Noindex Rules
In a decoupled Sanity architecture, sitemaps, robots rules and noindex directives are endpoints built from your dataset, so they stay accurate only when they share one source of truth with the pages. Build the sitemap from the same GROQ queries as your routes, filter out drafts and noindex documents, and block indexing on every staging host.
Programmatic XML Sitemaps with Unified GROQ Queries
An accurate XML sitemap lists only canonical URLs that return a clean 200 HTTP status. If the sitemap uses the same GROQ queries that drive your production routing, the indexable inventory stays in sync. In Next.js, an app/sitemap.ts file can run those queries and return the URL list. Fill lastmod from a revision date that editors set only after real changes (see the freshness section), not from _updatedAt, which every minor save changes:
- Indexable Document Filtering: Exclude drafts and noindex documents right in your projection, such as
*[_type in ["page", "post"] && !(_id in path("drafts.**")) && seo.noIndex != true]. - Sitemap Partitioning: Split large platforms by content type (such as /sitemap-articles.xml and /sitemap-products.xml) so you can track them separately in Google Search Console. Keep each file under 50 MB and 50,000 URLs, grouped under a sitemap index.
Separate noIndex from hideFromLists
Editorial teams often mix up internal site visibility with external indexation directives, so model them as two fields. seo.noIndex adds a meta name="robots" content="noindex, follow" directive to the rendered HTML head, and the sitemap query above filters the same documents out, so you never send contradictory signals. seo.hideFromLists controls only frontend presentation: GROQ queries filter the document out of overview grids, category feeds, recent-post blocks and site search. Google’s noindex documentation explains why a noindex page must stay crawlable for the directive to work.
Dynamic robots.txt and Environment Indexing Guards
Generate robots.txt dynamically from a site settings document in Sanity. In Next.js, an app/robots.ts file (or an edge worker on other stacks) compares the incoming request host against these settings. If the request comes from a staging or preview domain, the endpoint returns a blanket disallow directive:
User-agent: *
Disallow: /For verified production hosts, the handler outputs standard allow rules plus the absolute path to your sitemap index.
On Vercel, check process.env.VERCEL_ENV === 'production' in app/robots.ts instead of matching hostnames. Vercel also sends an X-Robots-Tag: noindex header on preview deployments by default, but it omits that header when a custom domain is assigned to a non-production branch, so a custom staging domain still needs the guard above.
Structured Data and Image SEO in Sanity
Sanity lets the frontend build structured data and image markup from content editors already maintain, so neither needs separate manual entry. JSON-LD is generated at render time from document fields, alt text is required by schema validation, Sanity’s image CDN serves modern formats at the right size, and share images can be generated from each title.
Programmatic JSON-LD from the Sanity Content Graph
The presentation layer should build JSON-LD at render time from document fields already in Sanity, so the structured markup matches what’s visible on the page. Inject the payload with a standard <script type="application/ld+json"> tag; typical types include Article for blog posts, Person for authors, and Organization and BreadcrumbList site-wide. Valid structured data does not ensure a rich result, but it tells search engines in a machine-readable form what the page is about.
Generated Open Graph Images
Editors rarely design a share image for every post. In the Next.js App Router, an opengraph-image.tsx file in a dynamic route can fetch the document from Sanity and render a 1200×630 card with ImageResponse from next/og, so every page has a share image even when nobody uploaded one:
// app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og';
import { client } from '@/sanity/client';
export const size = { width: 1200, height: 630 };
export const contentType = 'image/png';
export default async function Image({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const post = await client.fetch('*[_type == "post" && slug.current == $slug][0]{ title }', { slug });
return new ImageResponse(
<div style={{ display: 'flex', width: '100%', height: '100%', alignItems: 'center', padding: 80, fontSize: 64, background: 'white' }}>
{post?.title}
</div>,
{ ...size }
);
}Next.js caches the generated image by default and adds the og:image tags with their width and height to the page head.
Alt Text Validation and Descriptive Filenames
Configure Sanity Studio schemas to require descriptive alt text before an editor can publish an entry, so no unlabeled image reaches the public site:
defineField({
name: 'altText',
title: 'Alternative Text',
type: 'string',
validation: (Rule) => Rule.required().error('Alt text is required for SEO and accessibility.'),
})Sanity stores media as asset records with hashed filenames, but its image URLs accept a descriptive vanity filename appended to the path, such as hero-blog-seo-best-practices.png.
Next-Gen Formats and Responsive Breakpoints
Sanity transforms images on the fly from its asset CDN, using parameters such as ?w=800&fm=webp&q=80. Adding ?auto=format returns the most optimized format the browser supports, such as WebP or AVIF. Combine the Sanity image URL builder with components such as next/image to request exact dimensions per breakpoint, and use the asset’s aspect ratio and hotspot crop data to avoid layout shifts. The Next.js image component guide covers the frontend side.
Performance and Rendering Strategy for SEO
Pages that need to rank on a Sanity site should reach crawlers as fully rendered HTML, because pure client-side rendering can delay indexing when bots defer JavaScript. Pick a rendering pattern per page type, serve pages from the cache, refresh them through Sanity webhooks when content changes, and keep Core Web Vitals within Google’s thresholds on mobile.
Choose a Rendering Pattern per Page Type
- Static Site Generation (SSG): Pre-renders evergreen articles and resource libraries into static HTML at build time, so CDNs serve crawlers and visitors instantly.
- Incremental Static Regeneration (ISR): Keeps content fresh without full rebuilds by regenerating individual cached pages when editors update Sanity documents.
- Server-Side Rendering (SSR): Renders pages on request. It suits real-time inventory, search filters, or personalized pages.
The Next.js rendering strategies guide compares these three with client-side rendering in detail.
Webhook-Driven Revalidation
Instead of calling the Sanity API on every request, serve pages from the cache and invalidate them when content changes. Revalidation only works on data that carries a cache tag, so tag every Sanity query when you fetch it, for example client.fetch(query, params, { next: { tags: ['post', `post:${slug}`] } }). Sanity webhooks, filtered with GROQ, can then call a Next.js route that runs revalidateTag for those tags:
// Example Next.js App Router on-demand revalidation handler
import { revalidateTag } from 'next/cache';
import { isValidSignature, SIGNATURE_HEADER_NAME } from '@sanity/webhook';
export async function POST(req) {
const body = await req.text();
const signature = req.headers.get(SIGNATURE_HEADER_NAME);
if (!(await isValidSignature(body, signature, process.env.SANITY_WEBHOOK_SECRET))) {
return new Response('Invalid signature', { status: 401 });
}
const { _type, slug } = JSON.parse(body);
revalidateTag(_type, 'max');
if (slug?.current) revalidateTag(`${_type}:${slug.current}`, 'max');
return Response.json({ revalidated: true, now: Date.now() });
}Tag-based revalidation marks only the targeted document and its related listings as stale. With the recommended 'max' profile, the next visitor gets the cached page while a fresh one renders; pass { expire: 0 } instead if an edit must disappear from the cache at once.
Core Web Vitals in Headless Frontends
Core Web Vitals feed Google’s page experience signals, and a headless frontend still has to earn good scores:
- Largest Contentful Paint (LCP): Load the above-the-fold hero elements within 2.5 seconds by prioritizing banner assets, using edge CDN endpoints, and preloading primary font files.
- Interaction to Next Paint (INP): Keep page responsiveness under 200 milliseconds by code-splitting heavy third-party scripts, deferring analytics bundles, and minimizing main-thread JavaScript.
- Cumulative Layout Shift (CLS): Keep visual stability scores below 0.1 by setting explicit aspect ratios on embedded assets and fixed-dimension containers for dynamic Sanity content blocks.
If you host on Vercel, Speed Insights reports performance from real visitors per route, so you can catch a regression from a new Sanity module before it shows up in Search Console; individual Core Web Vitals per route need Speed Insights Plus.
Pagepro moved GP Notebook, a medical reference platform used by UK GPs, from a legacy custom CMS to Next.js and Sanity, and the replatformed site passes Core Web Vitals on mobile, which the legacy site never could (CrUX field data).
Need an editorial setup Google actually loves?
Localization and hreflang in Sanity
Multilingual Sanity sites need explicit URL paths, self-referential canonicals and hreflang tags for every language version, because Sanity doesn’t manage routing out of the box. First choose between field-level and document-level localization, which decides how GROQ fetches each language, then render one crawlable URL per locale with alternates pointing to every sister version.
Field-Level vs. Document-Level Localization
- Field-Level Localization: Embeds language variations in individual field objects within a single document. It suits product catalogs where specifications, IDs, and images stay the same, and only titles and descriptions need translation.
- Document-Level Localization: Duplicates whole documents for each target locale and links the variants through translation metadata or internationalization plugins. It fits localized blogs and regional marketing sites that need different editorial structures, images, or publishing schedules.
GROQ queries both structures: field-level implementations project language-specific values (such as title[$locale]), while document-level setups filter by a locale property (such as *[_type == "page" && locale == $locale]).
hreflang Tags, Canonical URLs, and Routing
Expose a unique, crawlable URL path for each locale (such as /en/, /es/, or /de/); search bots won’t find locale variants switched by cookies or headers. Each regional page should carry a canonical link pointing to its own absolute URL, never back to an English master page, plus hreflang annotations linking to all sister versions and an x-default target.
Author Signals and Freshness for E-E-A-T
Crawlers can’t judge who wrote a page unless you model authors as entities and render that context into visible text and structured data. In Sanity, that means an author document referenced from every post and mapped to Person JSON-LD, plus publication and revision dates that editors set deliberately instead of relying on automatic timestamps.
- Author Schema: Define an author document type with name, job title, credentials, an avatar, a short bio, and links to profiles such as LinkedIn, and reference it from posts instead of free-text author strings.
- Linked Person JSON-LD: Map author fields to schema.org Person data on author pages and article pages, filling the
sameAsarray with verified profile links.
For freshness, don’t let _updatedAt pass for an editorial revision: schema migrations and typo fixes change it too. Add a custom publishedAt and an updated-date field that editors set only after a real revision, show both dates on the page, and use the same values for datePublished and dateModified in JSON-LD and for lastmod in the sitemap.
How to Audit and Govern Sanity SEO
Sanity separates the editorial environment from the presentation layer, so SEO regressions can slip in silently with routine code deployments or schema changes. Two habits catch them: role-based permissions that keep canonical, robots and noindex settings in the right hands, and a weekly crawl plus Search Console checks that confirm what Googlebot actually receives.
Roles and Publishing Controls
Role-Based Access Control (RBAC) in Sanity lets teams limit sensitive SEO controls to the right people:
- Global Metadata Protection: Limit canonical overrides, robots.txt settings, and noindex toggles to administrators or technical SEO managers.
- Author and Contributor Scopes: Give freelance authors edit rights for drafts and article bodies, and withhold publish rights.
- Dataset Isolation: Enforce separate access rules for staging and production datasets, so experimental content doesn’t leak into production feeds.
Technical Crawls and CI Lighthouse Thresholds
Run a crawler such as Screaming Frog or Sitebulb weekly across staging and production builds, and add Lighthouse thresholds to CI to catch performance regressions before deployment. Each crawl should check:
- Index Directives and Status Codes: All public, canonical URLs return a clean HTTP 200, and staging noindex meta tags don’t carry over into production.
- XML Sitemap Integrity: Sitemaps list only canonical URLs and leave out draft documents.
- Canonical Alignment: No duplicate canonical references or conflicting trailing-slash redirects from frontend routing mismatches.
Monitoring with Google Search Console
The URL Inspection tool in Google Search Console shows the exact HTML Googlebot rendered, so you can confirm that dynamic title tags, meta descriptions, and JSON-LD blocks reach the document head. Give new topic clusters several weeks of crawling before you judge the ranking effect.
Pagepro, a Sanity development agency, builds sites on its own Next.js and Sanity framework, built from 20+ real projects with zero drops in Google rankings across all of them. To see your own content on it before you commit, Pagepro offers to rebuild part of your website with your real content in 48 hours, with no cost and no commitment.
FAQ
Define one reusable SEO object type and embed it in every indexable document schema. Include meta title and description fields, robots flags like noindex, a canonical override and an image field for social share previews. Add validation such as Rule.max(60) for titles and Rule.min(120).max(160) for descriptions.
Sanity doesn’t output SEO markup on its own, and community SEO plugins still leave the rendering to your frontend, so most teams define a custom object to store meta titles, descriptions, canonical URLs and structured data. The frontend then turns that content into crawlable HTML that search engines can read.
Keep meta titles between 50 and 60 characters and meta descriptions between 120 and 160 characters. These limits reduce truncation in search results and fit standard snippet sizes. Add validation in Sanity Studio so editors see length feedback before publishing.
Use GROQ to query public slugs and timestamps from the Sanity Content Lake, excluding drafts and noindex documents, then generate the XML on your frontend or server. Take lastmod from a revision date editors set after real changes, and generate the file, for example in app/sitemap.ts, from the same queries as production routing so it stays in sync.
Add a noIndex boolean field to your SEO object and render <meta name=”robots” content=”noindex, follow”> on pages where it is true. Also remove those pages from your sitemap, because listing a noindex page there sends conflicting signals to crawlers.
Server-side rendering (SSR) or static site generation (SSG) are best for SEO because they send fully rendered HTML to crawlers. Client-side rendering (CSR) can delay indexing if bots do not execute JavaScript, while SSG works well for evergreen content and SSR suits frequently changing or personalized pages.
