16 Strapi SEO Checks Before Your Website Goes Live

Check Strapi 5 SEO from API responses to public HTML. Verify metadata, published content, locales, sitemaps, internal links and cache updates before launch.

Editorial policy

Strapi SEO depends on what reaches your public website. A complete SEO component in the admin panel is only the starting point: your API must return the right content, your frontend must turn it into useful HTML, and publishing changes must reach the live page.

Start with one published article and follow it through those layers. Compare its Strapi response with the public page's content, metadata, canonical URL and sitemap entry. When something differs, fix the layer that introduced the mismatch.

These 16 checks focus on the standard Strapi 5 REST API and a separate public frontend. They are based on current documentation, with illustrative examples rather than results from a tested Strapi installation. Collection and field names vary by project; adapt the examples to your model and installed version. An SEO plugin may help editors manage inputs, but it cannot replace the frontend work that produces the page.

Trace SEO data through the Strapi API

An API response and a public page answer different questions. The response shows what the frontend can use; the HTML shows what it actually delivered.

Symptom First place to investigate Evidence to keep
Article exists in admin, but the read fails Endpoint, authentication and read permissions HTTP status and requested content type
Article arrives without its SEO component Population query and field names Response with the required component populated
API has the correct title, public page has the old one Frontend mapping, build and cache API observation and public response from the same publication
Only some articles appear in the sitemap Collection pagination and URL-selection rules Total pages read and final canonical URL set

1. Read the Strapi 5 response shape correctly

The standard Strapi 5 REST response puts content fields directly on the document. A collection result uses data as an array; an individual document request returns an object. Code written for a Strapi 4 path such as data.attributes.title can therefore miss a title that is present in the response.

Use the actual contract for your endpoint. For a standard collection response, an article title is at data[0].title, and documentId identifies the document. Do not confuse that identity with a public slug or a numeric version ID. Strapi's REST API reference explains the response shape and endpoints.

Compare one known article against the frontend's loader before editing metadata fields. Custom controllers may return a different shape, so inspect their response rather than applying this path blindly.

2. Verify read access without exposing private content

Strapi content types are private by default. A missing article can be an authorization problem rather than a missing SEO value. Check the permission used by the actual reader: a public role, an authenticated user, or a server-side API token.

Give that reader only the access it needs. A server-rendered website can fetch content privately; you do not need to make every collection publicly readable. Keep API tokens out of browser bundles, page source and diagnostic output.

Also check access to related content types. A readable article does not prove that its author or another populated relation is readable under the same permissions. The population documentation calls out this dependency.

3. Populate the SEO component and its nested media

The REST API does not populate relations, media, components or dynamic zones by default. Selecting scalar fields with fields does not replace population. And populate=* covers one level, so it should not be treated as a request for every nested image or relation.

For a concrete check, assume an articles collection with a slug, a non-repeatable seo component and a metaImage media field inside it. This server-only Node.js example requires a runtime with built-in fetch, a Strapi origin in STRAPI_URL, and the named fields in your schema. It reads one published article; it does not change content.

JavaScript
if (!process.env.STRAPI_URL || !process.env.ARTICLE_SLUG) {
  throw new Error('Set STRAPI_URL and ARTICLE_SLUG before running this check');
}
const url = new URL('/api/articles', process.env.STRAPI_URL);
url.search = new URLSearchParams({
  'filters[slug][$eq]': process.env.ARTICLE_SLUG,
  status: 'published',
  'populate[seo][populate][0]': 'metaImage',
}).toString();

// Add a locale only for a content type with localization enabled.
if (process.env.ARTICLE_LOCALE) {
  url.searchParams.set('locale', process.env.ARTICLE_LOCALE);
}

const headers = process.env.STRAPI_READ_TOKEN
  ? { Authorization: `Bearer ${process.env.STRAPI_READ_TOKEN}` }
  : {};
const response = await fetch(url, { headers });
if (!response.ok) throw new Error(`Article read failed: HTTP ${response.status}`);

const payload = await response.json();
if (!Array.isArray(payload.data) || payload.data.length !== 1) {
  throw new Error('Expected exactly one published article for this route');
}
const article = payload.data[0];
if (!article.documentId || !article.seo) {
  throw new Error('Document identity or populated SEO component is missing');
}
console.log({
  documentId: article.documentId,
  title: article.title,
  seo: article.seo,
});

Set the article slug explicitly before running it. Adapt the endpoint if your API uses a different prefix. Inspect the returned SEO values and image, then compare them with the public page. If the component is absent, fix the model, query or permissions; do not silently replace it with metadata from another article.

Select the right published document

Once the API returns the expected fields, confirm that they belong to the version and language your public route should serve.

4. Separate a saved draft from the published version

Strapi's Draft & Publish feature is enabled per content type and is disabled by default. Check your type's configuration before relying on publication states.

With that feature enabled, saving a revised title does not mean visitors should see it immediately. A document can have a published version and newer draft changes. Compare the published response with the public page, rather than treating an editor's saved copy as proof of a failed deployment.

For standard REST reads, published content is the default; making status=published explicit documents your intention. Strapi's server-side Document Service has a different default: draft. The status reference explains that distinction. If custom backend code replaces a REST request, recheck its publication policy.

Try a harmless title change in an authorized test document: save it, observe the public version, then publish and observe again. Keep draft previews authenticated and separate from the public fetch path.

5. Match the request locale to the public route

On localized content types, an omitted locale selects the configured default locale. That default can be changed; do not assume it is always English. Strapi documents the behavior in its REST locale guide.

For an English and French route, record the requested locale, returned locale, slug and document identity. Confirm each URL receives the intended published text and SEO component. Check related content too: a missing published translation can affect what the page can render.

Decide explicitly what happens when a translation is unavailable. A French route should not quietly present an English article as though the translation exists. Fix the routing/content policy and expose an appropriate unavailable state. Public language-alternate tags are a separate check below; requesting a locale does not generate them.

Align public URLs and search signals

The correct API document still needs a coherent public identity. Inspect these signals on the website origin, not just on the Strapi server.

6. Make slug changes work as real route changes

A slug field stores an input for routing. It does not create a frontend route or preserve an old URL by itself. Check that two different documents cannot accidentally resolve to the same public path, including across content types and localized route prefixes.

If an article moves from /blog/old-guide/ to /blog/new-guide/, implement a permanent redirect from the old public URL to the new one. Update internal links and the sitemap. Request the old path directly and confirm the redirect reaches the intended article without a chain or loop.

Keep a record of the previous route before overwriting its slug. A later publication event containing only the current slug may not tell your redirect system where the old page lived. Google's canonicalization guidance explains how redirects and canonical signals work together.

7. Render page-specific titles, descriptions and headings

Compare two articles using the same frontend template. Each should have a meaningful document title, an accurate description and a visible main heading that matches its subject. Identical default metadata across both pages usually points to a template or mapping issue.

Check the completed HTML response and the rendered page. If SEO fields arrive from Strapi but never reach the head, changing those fields again will not repair the template. If initial HTML differs from the final DOM, investigate the rendering path and what crawlers can access.

Google can render JavaScript, but rendering adds dependencies. Its JavaScript SEO guidance is more useful than a blanket claim that client-rendered pages cannot rank. Server-rendered or generated article HTML can make the content and metadata easier to deliver consistently.

For a Next.js frontend, the separate guide to dynamic-page metadata mistakes covers framework-specific mapping and inheritance problems. Here, the check is whether the Strapi values survive into the page.

8. Resolve canonicals against the public website origin

Check that the canonical identifies the intended public page, with your chosen protocol, hostname and path convention. A value pointing to the Strapi admin origin, a preview host or the homepage is a different identity from the article you want discovered.

Use a controlled public-origin setting when constructing URLs. If editors can override a canonical, validate the override and make deliberate cross-domain exceptions visible to the team. Avoid competing canonical tags emitted by different parts of the frontend.

Compare the canonical with internal links, redirects and sitemap output. Consistent signals make your preference clearer, although a canonical tag does not guarantee that Google will select that URL.

9. Keep indexing rules and preview protection separate

Inspect both HTML robots directives and any X-Robots-Tag response header. An otherwise correct public article may still carry noindex from a staging configuration or inherited template.

Do not use a robots.txt block as a substitute for noindex: Google must be able to crawl a page to see its indexing directive. Nor should robots.txt be your security boundary for drafts. Protect previews with authentication and ensure they do not leak through public listings or sitemaps.

Google's noindex documentation explains the crawl-access requirement. Also avoid shipping an initial noindex and relying on JavaScript to remove it later; Google may skip rendering after seeing that initial instruction.

Render media and article markup correctly

Media and structured data need their own output checks. A populated reference proves availability, not correct rendering.

10. Check the delivered image URL and alt text

Open the image URL the public page actually uses. Confirm it returns the intended image and remains accessible without an editor's session. A populated metaImage is not enough if its URL is resolved against the frontend host by mistake.

Handle relative and absolute asset values deliberately. For example, the following resolves a local upload path against an approved asset origin while preserving an absolute URL returned for externally hosted media:

JavaScript
const imageUrl = new URL(
  article.seo.metaImage.url,
  process.env.STRAPI_ASSET_ORIGIN
).href;

Validate the permitted URL schemes and hosts for your project before rendering user-editable values. This example assumes the media object and URL have already been checked; a missing image should be reported rather than converted into a broken URL.

Inspect the actual <img> output for relevant, concise alt text. An editorial alt value is useful only when the renderer maps it to the image. Decorative images can correctly use empty alt text; an Open Graph description does not replace an article image's alt attribute. Check responsive image delivery and reserved dimensions too. Google's image SEO guidance covers the search-facing considerations.

11. Make Article markup match the visible article

For a blog article, map structured data to the real headline, author, publication date, meaningful modification date and applicable images. Use the type that fits the page; a service landing page does not become an article simply because the CMS has an author field.

Keep an editorial publication date distinct from an internal record-creation timestamp. Likewise, a CMS update does not automatically mean the public article changed significantly. Check which dates your frontend displays and which it emits in JSON-LD.

Prefer a controlled mapping from typed content fields. If editors can supply JSON, validate and serialize it safely instead of inserting unchecked rich text into a script tag. Check the finished markup against Google's Article structured-data documentation. Valid markup helps describe the page; it does not promise a rich result or improved rankings.

Make every published page discoverable

A working article route can still be difficult to discover if the sitemap stops early, navigation hides its links, or language alternatives point to unavailable pages.

12. Read every API page when building the sitemap

Strapi collection responses are paginated. The documented default page size is 25, so a generator that fetches a collection once can quietly miss later articles. Use the pagination metadata rather than assuming the first result contains everything.

This read-only collector uses the same custom articles model as the earlier example. It assumes Draft & Publish is enabled, reuses the server-side headers, and populates the SEO component so the generator can evaluate its project's indexing policy afterward:

JavaScript
async function collectPublishedArticles(origin, headers, locale) {
  const documents = [];
  let page = 1;
  let pageCount = 1;

  do {
    const url = new URL('/api/articles', origin);
    url.search = new URLSearchParams({
      status: 'published',
      'fields[0]': 'slug',
      'fields[1]': 'updatedAt',
      'populate[0]': 'seo',
      'sort[0]': 'slug:asc',
      'sort[1]': 'documentId:asc',
      'pagination[page]': String(page),
      'pagination[pageSize]': '25',
      'pagination[withCount]': 'true',
    }).toString();
    if (locale) url.searchParams.set('locale', locale);

    const response = await fetch(url, { headers });
    if (!response.ok) throw new Error(`Sitemap read failed: HTTP ${response.status}`);
    const payload = await response.json();
    pageCount = payload.meta?.pagination?.pageCount;
    if (!Array.isArray(payload.data) ||
        !Number.isInteger(pageCount) || pageCount < 0) {
      throw new Error('Unexpected collection pagination response');
    }
    documents.push(...payload.data);
    page += 1;
  } while (page <= pageCount);

  return documents;
}

The explicit sort makes traversal more predictable; it is not a snapshot guarantee. Concurrent publications or slug edits can still change page boundaries. Generate from a stable content snapshot where your system supports it, or verify the final URL set against the published inventory and rerun after concurrent changes.

Before writing XML, apply your public route mapping, canonical policy and indexing exclusions. Emit frontend URLs, not /api/articles/... URLs. Exclude drafts, redirects and intentionally non-indexable pages. Do not equate updatedAt with an accurate sitemap lastmod unless it tracks meaningful public-page changes in your workflow.

Strapi's sorting and pagination reference explains the page-based contract and configurable limits. For the output audit, compare your file with the checks in Screpy's XML sitemap guide. A complete sitemap supports discovery; it does not guarantee indexing.

13. Turn relations and content blocks into crawlable links

An article relation inside Strapi is not a public internal link until the frontend renders it. Inspect a category listing, a related-article block and links inside rich text or dynamic-zone components.

Each useful destination should appear as a real anchor with an appropriate href, descriptive text and the correct public path. A click handler on a card or an API response containing a related document is not the same evidence.

Check what happens when a referenced page is unpublished, deleted or moved. Remove stale links or update them to the correct destination. Inspect the component renderer when links work in ordinary rich text but disappear inside a custom block; that is a narrower problem than redesigning your content model.

14. Publish reciprocal language alternatives

For multilingual sites, compare the public alternate URLs with the localized documents verified in check 5. List only suitable, accessible equivalents and include each page itself in the alternate set. The equivalent pages should point back to one another.

Do not generate a French alternate merely because French is enabled in Strapi. Confirm that the intended French page exists, is published and delivers the right content. A draft translation or a route that silently displays the default-language article needs a deliberate policy before it belongs in that set.

Check the language/region codes, full URLs and canonical decisions together. Google's localized-version guidance explains the reciprocal-link requirements. A single-language website does not need invented language variants to complete this checklist.

Keep delivery current and usable

The last two checks address what happens after correct content leaves Strapi: whether publication changes reach every affected page, and whether those pages remain practical to use.

15. Follow publication events through to public output

For a generated or cached frontend, an editor clicking Publish is only the beginning of delivery. Strapi's webhook documentation describes events including entry.publish, entry.unpublish and entry.delete; publication events depend on Draft & Publish being enabled. Media changes can also affect output.

Check which events your receiver handles and which public artifacts each one invalidates. A changed article can affect its detail page, category listings, related-content blocks and sitemap. A successful webhook HTTP response proves receipt, not that the build or cache refresh finished.

Authenticate the receiver, make repeated delivery safe, and record its downstream completion or failure. Map documents to their previous public routes as well as current routes. In Strapi 5, the document identity and numeric version ID have different roles, and delete payloads should not be assumed to contain the same populated relations as other events.

Replay publish, change, unpublish and delete with an authorized test document. After unpublishing, confirm that the public route follows your removal policy instead of continuing to serve the old article from cache. A genuinely missing page should return a meaningful missing-page status, rather than a generic article shell with HTTP 200. Use a redirect only when there is an appropriate replacement.

16. Separate API payload problems from frontend performance

Inspect how much data the page fetches and how much work the browser performs. An unnecessarily broad population query can increase backend work and transfer size; a lightweight API response can still feed an oversized JavaScript bundle or hero image.

Measure those layers separately. First reduce unused fields and nested relationships in the article query. Then inspect response time, the actual image selected for the viewport, unnecessary client JavaScript and layout shifts. Do not remove useful page content simply to make an API response smaller.

The current Core Web Vitals are LCP for loading, INP for interaction responsiveness and CLS for visual stability. Use a lab check to investigate a regression and field data, when available, to understand real visitors. A new page may have no field data yet; that is an unavailable measurement, not a passing result. None of these checks establishes that Strapi, a rendering mode or a plugin automatically makes the website fast.

Repeat the check after a real publishing change

Keep a small record that connects one Strapi document to the response a visitor receives:

Plain text
Document ID / content type:
Locale / published slug:
Public canonical URL:
Change and publication time:
API observation: status, identity, expected content/SEO values
Delivery observation: event received, build/cache action completed
Public observation: HTTP status, visible content, head, schema
Discovery observation: internal link, sitemap, language alternatives
Removal observation: old route and listings after unpublish/delete
Observed at / owner / unresolved issue:

Inspect both an ordinary article and a difficult case: a translation, a deeply nested content block, a renamed slug or an article beyond the first API page. That makes the evidence more useful than checking only the newest, simplest record.

For a public JavaScript page, Screpy's crawlability guide explains how to compare source, rendered output and crawl access. Use Search Console's URL Inspection when you need to examine Google's view of a public URL. Keep that observation separate from your own delivery checks: a correct public page, a submitted sitemap and an accepted indexing request are different facts, and none guarantees rankings.

When a check fails, retain the URL, document identity, expected value and observed response. That gives the content editor, Strapi developer or frontend owner a specific defect to fix instead of another request to “improve SEO.”

Put this guide into practice

Continue with the Screpy tools that match this article's workflow.

Related posts

Keep reading practical SEO guides from the Screpy blog.

View all posts