0% прочитано

Laravel robots.txt, Sitemap, and Canonical URLs: My Production SEO Checklist

In ICanUp, robots.txt, sitemap, and canonical URLs were implemented at different stages, but in production they need to describe the same public URL contract. This is the checklist I now use to verify crawl rules, sitemap eligibility, canonical identity, localization, raw HTML, and HTTP responses after deployment.

12 вересня 2026 р. 9 хв читанняLaravel

In ICanUp, robots.txt, sitemap.xml, and canonical URLs were implemented at different stages of the SEO work.

Each part looked simple in isolation. In production, however, I learned that verifying them independently was not enough.

All of these mechanisms need to describe the same reality: which URLs are public, which can be crawled, which should be discoverable through the sitemap, and which URL is the primary identity of a page.

robots.txt, sitemap, and canonical URLs solve different problems. robots.txt controls crawling, sitemap helps crawlers discover public URLs, and canonical identifies the primary URL of the current page.
Laravel production SEO contract connecting robots.txt, sitemap.xml, canonical URLs and public pages
My production SEO checklist starts with one rule: robots.txt, sitemap, and canonical URLs need to use the same public URL contract.

Three Mechanisms, Three Different Roles

The biggest change in my approach was to stop treating these mechanisms as interchangeable.

If a sitemap contains a URL, robots.txt accidentally blocks it, and the page declares another canonical address, the crawler receives conflicting signals.

Mechanism

Primary purpose

What it does not do

robots.txt

Controls crawling permissions for paths

Does not define canonical identity and is not a reliable substitute for noindex

sitemap.xml

Lists URLs the application considers public and indexable

Does not guarantee indexing

canonical

Declares the primary address of the current page

Does not protect private routes

My robots.txt Is a Dynamic Endpoint

I did not want a static file where the production domain or sitemap URL could drift away from the actual application configuration.

/robots.txt is therefore generated by the application and returned as a public text/plain response.

Simplified robots.txt
User-agent: *Disallow: /adminDisallow: /loginDisallow: /registerDisallow: /password Sitemap: https://icanup.com.ua/sitemap.xml

The exact list of restricted paths changes with the application, but the principle remains the same: public content should never be caught by an overly broad Disallow.

robots.txt is not an authorization mechanism. Admin and other private areas still need application-level protection. A Disallow rule should also not be treated as a guaranteed noindex mechanism.

The Sitemap Contains Only Actually Public URLs

My sitemap started with Posts, but more public content types were added as ICanUp grew.

The important criterion is no longer the model type itself but whether the resource is actually available to a normal visitor and a crawler.

  • Public homepage URLs.
  • Active public Categories.
  • Published Posts.
  • Published Pages.
  • Active public PostSeries.
  • Localized URLs only for available language versions.

Drafts, future publications before published_at, deleted records, and other non-public resources should not appear in the sitemap.

I treat the sitemap as a projection of public eligibility, not simply a list of database rows.

XML - Simplified sitemap with localized URLs
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">    <url>        <loc>            https://icanup.com.ua/posts/example-slug        </loc>        <lastmod>            2026-09-10T10:00:00+00:00        </lastmod>    </url>     <url>        <loc>            https://icanup.com.ua/en/posts/example-slug        </loc>        <lastmod>            2026-09-10T10:00:00+00:00        </lastmod>    </url></urlset>

Canonical Defines Page Identity

The sitemap tells a crawler that a URL exists and matters for discovery. Canonical has a different role.

On the page itself, I explicitly declare the URL the application considers primary for the current content and locale.

HTML - Self-canonical for an EN page
<link    rel="canonical"    href="https://icanup.com.ua/en/posts/example-slug">

For a multilingual Post, the EN canonical should not point to the Ukrainian page simply because Ukrainian is the default locale.

Each available language version has its own self-canonical URL.

I covered this contract in more detail in my article about canonical and hreflang for multilingual Laravel pages.

Localization Should Not Create Separate URL Rules for Sitemap and Canonical

In the current architecture, Posts and Categories have one shared slug across locales.

Ukrainian is the default locale and has no prefix. English uses /en.

The URLs should therefore be generated through the same locale-aware routing infrastructure instead of being manually concatenated by individual SEO services.

Locale

Public URL

Canonical

UK

/posts/example-slug

/posts/example-slug

EN

/en/posts/example-slug

/en/posts/example-slug

The same rule is reused for sitemap URLs, hreflang, Open Graph URLs, and other SEO signals.

Public Eligibility Is the Shared Source of Truth

One of the most dangerous designs would be to let every SEO subsystem independently decide whether content is public.

A sitemap could then include a scheduled Post while the public controller returns 404 and IndexNow has already submitted the URL.

The SEO layer should not invent its own definition of published content.

  • A Draft is not included in the sitemap.
  • A scheduled Post is not included before its publication time.
  • Soft-deleted content is excluded.
  • An inactive Category or Series is not advertised as an indexable URL.
  • A missing translation does not create a fake localized URL.
  • A public URL should actually resolve under the same application contract.
Before adding a new content type to the sitemap, I verify its public eligibility, routing, and canonical contract first. XML generation comes after that.

lastmod Should Represent a Real Page Change

Another value I no longer verify superficially is lastmod.

When a Post uses Content Blocks or localized content, the public page can change without an obvious update to the main posts.updated_at.

The lastmod value therefore needs to represent the latest relevant change to that public page version.

I documented a real locale-specific Content Block case in my article about Laravel sitemap lastmod and why the model updated_at value was not always the real page modification time.

I Verify SEO in Raw HTML, Not Only in the Browser

Inertia SSR changed how I verify production SEO.

What matters is not only what DevTools shows after hydration, but also what a crawler receives in the initial server response.

BASH - Check SEO tags in the initial HTML
curl -s \  https://icanup.com.ua/en/posts/example-slug \  | grep -E \    'rel="canonical"|hreflang|name="robots"'

This is especially useful for canonical, hreflang, robots metadata, Open Graph, and JSON-LD.

I Treat robots.txt and sitemap.xml as HTTP Endpoints

BASH - Check SEO endpoints
curl -i \  https://icanup.com.ua/robots.txt curl -i \  https://icanup.com.ua/sitemap.xml
  • /robots.txt returns HTTP 200.
  • /robots.txt has the correct text/plain Content-Type.
  • /sitemap.xml returns HTTP 200.
  • The sitemap returns an XML Content-Type.
  • Both endpoints are available without authentication.
  • robots.txt references the current sitemap URL.
  • The sitemap contains no admin or other private URLs.

XML Needs More Than a Visual Check

A sitemap can look fine in a browser while an escaping or XML structure error makes it invalid for a crawler.

BASH - Validate the XML sitemap
curl -s \  https://icanup.com.ua/sitemap.xml \  > /tmp/icanup-sitemap.xml xmllint \  --noout \  /tmp/icanup-sitemap.xml
xmllint is only one convenient local validation option. The important requirement is that the sitemap remains valid XML and passes search-engine tooling.

404 Responses and the Sitemap Must Not Contradict Each Other

Another simple production check is to take several URLs directly from the sitemap and request them.

If the sitemap advertises a URL that returns 404, the problem is no longer the XML. Two application contracts have drifted apart.

BASH - Check the HTTP status of a public URL
curl -s \  -o /dev/null \  -w '%{http_code} %{url_effective}\n' \  https://icanup.com.ua/posts/example-slug

IndexNow Complements the Sitemap but Does Not Replace It

After adding IndexNow, I had another indexing signal available.

Its role is different: the sitemap provides a stable inventory of public URLs, while IndexNow can notify search engines about individual changes faster.

I do not remove the sitemap simply because the application can now submit URLs through IndexNow.

I described that pipeline separately in my article about automatically submitting new and updated Laravel URLs through IndexNow.

Signal

What it communicates

Sitemap

Here is the set of public URLs on the site

Canonical

Here is the primary URL of this specific page

Hreflang

Here are the available language versions of this page

IndexNow

Here is a URL whose state has just changed

My Production Checklist After Deployment

After several SEO changes, I stopped checking only the feature I had just modified.

Even a small routing or publication change can affect several SEO signals at once.

  • Open /robots.txt without authentication.
  • Verify the robots.txt Content-Type.
  • Verify the current Sitemap: URL.
  • Confirm that public routes are not blocked by an accidental Disallow.
  • Open /sitemap.xml.
  • Verify HTTP 200 and the XML Content-Type.
  • Validate the XML.
  • Find several real UK and EN URLs in the sitemap.
  • Confirm that Draft and future content are absent.
  • Verify lastmod after a real content change.
  • Open several sitemap URLs and verify HTTP 200.
  • Inspect canonical in raw HTML.
  • Verify self-canonical separately for UK and EN.
  • Verify hreflang and x-default.
  • Confirm that URLs come from application routing.
  • Verify that SSR exposes SEO metadata before JavaScript runs.
  • After a significant SEO change, check Google Search Console and Bing Webmaster Tools.

Automated Tests Protect the Contract Between These Parts

The production checklist does not replace tests, and tests do not replace a production smoke check.

They protect different layers for me.

  • robots.txt returns the expected status and Content-Type.
  • robots.txt contains the sitemap URL.
  • The sitemap returns a valid XML response.
  • Published content appears in the sitemap.
  • Draft content does not appear.
  • Future-published content is excluded before its publication time.
  • Inactive content is not advertised as indexable.
  • Localized URLs match actual routes.
  • Canonical matches the current locale.
  • SEO metadata exists in the SSR HTML.
  • Content Block changes affect lastmod where appropriate.

The Mistakes I Check First Now

  • Treating Disallow in robots.txt as a complete substitute for noindex.
  • Adding a URL to the sitemap simply because a database record exists.
  • Adding a scheduled Post before its real publication time.
  • Building locale URLs manually instead of using routing infrastructure.
  • Pointing an EN canonical at the UK URL.
  • Assuming one updated_at always represents a complex Content Block page.
  • Checking canonical only after client-side hydration.
  • Not checking the HTTP status of URLs already published in the sitemap.
  • Treating IndexNow as a replacement for the sitemap.
  • Changing routing without rechecking dependent SEO signals.

My production SEO checklist does not start with Google. It starts with whether the application itself describes its public URLs consistently.

What I Kept for Next Time

  • robots.txt, sitemap, and canonical URLs have different responsibilities.
  • Public eligibility should be shared across SEO infrastructure.
  • A sitemap should not advertise a URL that the application itself returns as 404.
  • Canonical URLs should use the same locale-aware routing contract.
  • UK and EN should have their own self-canonical URLs.
  • lastmod should reflect real public-page changes.
  • SEO metadata should be verified in raw SSR HTML.
  • SEO endpoints should be checked as normal HTTP responses.
  • IndexNow complements the sitemap rather than replacing it.
  • Both automated tests and a short production check are useful after deployment.

Conclusion

Implementing robots.txt, sitemap, or canonical URLs individually is not especially difficult. The harder part is keeping them consistent after many later changes to routing, localization, and publication logic.

In ICanUp, I eventually arrived at a simple rule: one public URL contract comes first, while robots.txt, sitemap, canonical, hreflang, and IndexNow describe different aspects of it.

When all of these signals come from the same application reality, production SEO becomes much easier to verify and much harder to break accidentally.