Aller au contenu principal
Jahia Store
FR

GEO Readiness

community
Télécharger 1.4.0

Informations

Identifiant du module
geo-readiness
Identifiant de groupe
org.jahia.community
Statut
community
Catégorie
SEO & GEO, Content Features, Admin Features
Auteur
smonier@jahia.com
Site web du développeur
http://www.jahia.com
Nécessite Jahia
8.2.0.0
Mis à jour
2026-09-16
Source
scm:git:git@github.com:Jahia/geo-readiness.git
Étiquettes
  • geo
  • seo
  • ai-crawlers
  • llms.txt
  • robots.txt
  • structured-data
  • sitemap
  • crawlability
  • site-audit
  • jcontent

GEO Readiness answers a question nothing else in the back office can: when GPTBot, ClaudeBot or
PerplexityBot ask for one of your pages, what do they actually get? It adds a GEO readiness action
to jContent for a single page, and a site-wide dashboard under Additional > SEO.

It is deliberately not a browser audit. The editor's browser is authenticated, runs JavaScript and
is never challenged by the firewall, and a crawler is none of those three. So every question is
answered server-side - one fetch per crawler user agent, no cookies, no session, redirects not
followed - and it reads the initial HTML rather than a rendered DOM, because a page whose content
only appears after JavaScript runs looks perfect to an editor and empty to a crawler.

The page drawer fetches the published page sixteen times: once as a normal browser for a control,
then once as each of fifteen AI crawlers - GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot,
Claude-SearchBot, PerplexityBot, Perplexity-User, Google-Extended, GoogleOther, Bingbot, CCBot,
Bytespider, Amazonbot, Applebot-Extended and meta-externalagent. For each it reports the status, the
time, the word count and what the initial HTML contains. Then the disagreements, which are the part
worth reading: blocked but allowed, where robots.txt permits the crawler and the server refuses it
anyway - usually a firewall rule nobody remembers adding; reachable but disallowed, the same
contradiction the other way round; thin, where a crawler receives far less text than the browser
did, which points at rendering that depends on JavaScript; and invisible, where no anonymous
visitor can read the page at all, so no crawler ever will. A page that has never been published
says so rather than showing an error.

The site dashboard is ten panels in four groups. Overview holds the site score and, when a provider
is configured, the written report. Can a crawler reach it holds invisible content, internal links
and addresses. Is what arrives usable holds structured data, languages and freshness. Site-level
files holds sitemap vs reality, robots.txt and llms.txt. Everything except the site scan is a
repository query, so most panels answer immediately rather than waiting for a walk of the site.

Findings roll up to the template that produced them. A check is blamed on the template only when it
fails on nearly every page that template renders: three failures out of four hundred is three
authors, four hundred out of four hundred is the template. Nothing outside Jahia knows which
template rendered a page, which is what makes this a platform feature rather than a page tool.

The score is a count of checks, not a rating out of 100. Eighteen checks in three groups, each
reading one observed fact and showing that fact beside the verdict, so an editor can disagree with a
specific line rather than with a number they cannot see inside. Being disallowed in robots.txt is
deliberately not counted as a failure - refusing a crawler is a legitimate decision. A disagreement
between the policy and the server is, because one of the two is then wrong and somebody owns the
fix.

The robots.txt panel lists each of the fifteen crawlers, what it does, and what blocking it would
cost: seven answer questions, so refusing one takes the site out of that assistant's replies, and
seven only train models, so refusing those costs no visibility at all. The llms.txt panel generates
the file from the published page tree - deterministically, with no model call and no external
service, so the same site produces the same file every time, and pages a visitor cannot read or
that are marked noindex are left out.

Sitemap vs reality resolves every sitemap.xml entry back to a node instead of fetching it, so a site
of any size costs no requests to check: pages the map never mentions, entries resolving to nothing,
dates that contradict the content, pages advertised while their own markup says noindex. Internal
links reads the link graph from the rendered HTML of every page the scan already fetched, so a menu
built from the page tree, a listing, a rich text link and a configured button all count alike, with
navigation and content links counted apart - being in a menu that lists everything is not the same
as somebody choosing to link to you. Addresses reports every address a page answers on. Structured
data derives schema.org from the content type rather than inferring it from the words on a page.
Languages shows coverage and score per language side by side, so a strong language cannot average
out a weak one. Freshness shows when the site was last touched, by content type and section, and
every published page oldest first.

An optional written report asks a model to read those measurements and say what they add up to:
where the site stands, a compliance status per area, what to fix first with who can fix it and how
much effort, quick wins, and a now/next/later roadmap, exportable as Markdown or JSON. Anthropic,
OpenAI and DeepSeek are supported. The provider receives a digest of the measurements - scores,
failing check ids, page paths and titles, counts - never page bodies and never the key, which is
configured server-side and never reaches the browser. With no provider configured the tab does not
exist and nothing leaves the platform.

Generating llms.txt and setting crawler rules are the only two things this module writes. Both show
a highlighted line diff against what is stored and need a second confirming click, and what gets
written is exactly the text on screen, so hand edits made before applying survive. Structured data
is shown as a snippet for someone to place; it is never injected into a page.

The dashboard and the drawer meet WCAG 2.2 AAA on contrast, carry real headings and announce a
running scan to a screen reader. The charts are plain HTML and CSS in Moonstone's own colours - no
chart library - and carry their numbers as text inside the list and table structures around them.
The interface ships in English and French.

The dashboard requires the publish permission on the site, and the endpoints behind it require the
same one rather than trusting the screen to hide itself. The page drawer asks less: it opens for
anyone who can read that page in the editing workspace. A scheduled scan runs as the account that
saved it and re-checks that account's permission on every run, so revoking the permission stops the
schedule rather than merely hiding the screen.

Requires Jahia 8.2+, with the jcontent, robots and llms modules present. Enable the module on each
site where it should appear.

Captures d'écran

Dépendances

Modules dépendants

Aucun module n'en dépend.

Versions

1.4.0

  • Closed a flaw where any logged-in account could read the site's configured AI provider, model and crawler list.


 

Nécessite Jahia 8.2.0.0Mis à jour 2026-09-16

1.3.2

A maintenance release with no change a user of the module will see. It clears the quality gate that had been red on main since before 1.3.1 was prepared, and removes the first of the obstacles that stop the release running in CI. An existing install upgrades in place.

  • Cleared the eight maintainability issues and the reliability rating holding main's quality gate red. A field left dead when 1.3.1 stopped reading JSON-LD with a regex and started parsing it; fifteen private helpers moved into the nested classes they actually serve, in GeoScore, LinkGraph and SiteScorer; and Snapshot's thirteen-parameter constructor replaced by one that reads the configuration map, which is also what lets its defaults be an empty map instead of a second copy of the same thirteen values that had to be kept in step by hand. Behaviour is unchanged — sorting the stripped lines of SiteScorer before and after gives the same 612 lines, nothing added and nothing removed, and the 354 unit tests pass.
  • The four chart boxes that carry the pointer tooltip now declare role="none". The accessibility work in 1.3.1 took the tab stops off them and moved their numbers into the surrounding table and list structures, which is the right shape, but left them reading as interactive elements a keyboard could not reach. The role says what is true: the box draws, and the value spans inside it are what assistive tech reads. Nothing is hidden.
  • languageFlag now reads a code point where it read a code unit. The two agree for every region letter that reaches this line, so no flag changes — the premise was wrong, not the answer.
  • Two control characters in SiteScopeTest were stored as raw bytes rather than escapes, which made git classify the whole file as binary. Four kilobytes of tests had merged with no diff anyone could read. Same inputs, same cases, readable file.
  • The release workflow declares the checked-out workspace trusted, so the release action's git commands can run as root inside the build container instead of failing on detected dubious ownership before anything is released. This is necessary but not sufficient: a CI release also needs the CI identity added as a bypass actor on the Prevent tag deletion and support-baseline rulesets, neither of which can be changed from a workflow file. Until then the release stays a manual job.

1.3.1

  • The dashboard and the drawer now meet WCAG 2.2 AAA on contrast, carry real headings, and announce a running scan to a screen reader. The charts stopped making every data mark a tab stop — a full failure matrix was upwards of three hundred of them — and instead carry their numbers as text inside the table and list structures they already had, so assistive tech reads the figures and the keyboard passes through in one stop. The drawer is a real <dialog>, and every control that had no accessible name has one.
  • The public base url now takes its host from the repository rather than from the request that matched it. The two were equal under a case-insensitive comparison but not identical, so a Host: header could choose the casing of a base url that the scan servlet then persisted in geoBaseUrl and reused on every later scheduled scan.
  • Sped up reading of fetched pages, sitemaps and site files, which could take minutes on some documents.
  • Moved the module to the org.jahia.community groupId. The Java package, the OSGi configuration file and the bundle name are unchanged, so an existing install upgrades in place and keeps its settings.
  • The runtime configuration is swapped through an AtomicReference, so the operation that replaces it reads as the atomic one it always had to be.
  • Decomposed the four methods Sonar scored furthest over the cognitive-complexity limit — the scoring engine at 43, the robots.txt rule evaluator at 27, the llms.txt freshness comparison at 19 and the robots.txt parser at 16. Behaviour is unchanged, and the characterisation tests added in the previous release are what proves it.
  • Decomposed four more methods over the cognitive-complexity limit — the HTML analyser at 26, the link-graph report at 28, the robots.txt editor's parser at 16 and the guest-visibility classifier — each behind characterisation tests written first. Unit coverage of the module roughly doubled, and nine classes are now at or near complete coverage.
  • Audited every factual claim in the documentation against the code and corrected what was wrong, including two instructions that would have broken a contributor's setup: a yarn e2e command that does not exist, and a CI instance_type the live workflow deliberately omits because it fails. Two of the corrections were code rather than prose — the test harness dropped the notests flag it documented, and a comment claimed the module refuses to fetch a site with no server name when it in fact falls back to localhost:8080.
  • Fixed crawler and site scans so repeated checks remain reliable and sitemap comparisons no longer follow links to other sites.
  • Fixed the $ end-anchor in robots.txt matching, which was wrong in two opposite directions: a pattern with no wildcard checked its start and its end independently, so /foo$ matched /foo/bar/foo, and a pattern ending in *$ rejected every path longer than its literal prefix, so abc*$ did not match abcdef. Sites whose robots.txt uses $ anchors will see corrected verdicts in the drawer's policy cross-check and may see their score move.
  • The language, freshness, sitemap and schema checks are now covered by tests that assert what they report rather than that they answered, and the three methods behind them have been decomposed to sit under the complexity limit.
  • Closed an SSRF where the request's Host header chose the port this module connected to, gated the one endpoint that had no permission check, stopped a browsing node deleting every site's scan schedule from the cluster, and fixed six places where the code did the opposite of its own comment - including a config marker typo that silently reset the administrator's settings on every redeploy.
  • Gave the four servlets one base class instead of four copies of the same seven helpers, which fixed two defects that were living in the drift between them: three of the module's four JSON endpoints were served without X-Content-Type-Options: nosniff while echoing content read from the site being checked, and three of them silently truncated an oversized request body so it came back as "malformed body" instead of being refused for what it was.
  • Nothing escapes a servlet method any more, the servlets hold no mutable shared state, an interrupted crawler check no longer swallows the interrupt, and the language pattern is bounded so a long input cannot overflow the matcher's stack.
  • Fixed seven defects the new characterisation tests had pinned. The largest: a page whose JSON-LD is syntactically broken now correctly reports no structured data, where it used to pass the check because the types were read out of the text with a regex rather than parsed. The robots.txt editor also now keeps the promise it makes in its own documentation — a merge that changes nothing returns a customer's file exactly as it arrived, rather than dropping blank lines, adding a trailing newline or mixing line endings into it.
  • The module has unit tests for the first time: JUnit 5, AssertJ and JaCoCo, with 189 characterisation tests pinning the scoring engine, the robots.txt matcher, the llms.txt freshness comparison and the two security guards. They exist so the decomposition that follows cannot change a score silently — the existing Cypress suite asserts that a scan happened and that the UI reads "n of m checks passed", never which n or which m.
  • Removed ten imports left behind by the decomposition.
  • Cleared what was still keeping the quality gate red on the main branch: a pattern left dead when JSON-LD types stopped being read with a regex, six helpers that belonged inside the class that was already their only caller, and a thirteen-argument constructor that now reads its thirteen defaults out of an empty map instead of a second list of the same values kept in step by hand. In the dashboard, four chart boxes carried a mouse handler with no role to say they are decorative, and the flag helper read a code unit where it meant a code point.
  • Two test inputs wrote their control characters as raw bytes rather than escapes, which made git classify the whole file as binary - four kilobytes of tests that no reviewer could see a diff of, and that no later change to them would show either.

1.3.0

  • Added an optional AI-written GEO report with prioritised recommendations and Markdown or JSON export.

1.2.0

Freshness stops being a picture you have to decode and becomes a report you can act on, the endpoints hold to the permission the screen already declared, and the repository grows the test suite and the shared CI it never had.

Added

  • Freshness reads as a timeline. The distribution is drawn left to right from oldest to most recent, so the shape says whether the site has been maintained. The bucket lying entirely past the threshold carries the warning colour, which puts the line an editor chose into the picture rather than leaving it in a caption. Empty buckets draw nothing at all.
  • Every page and when it last changed. Under the groups, one row per published item in the language being measured: title, content type, section, public path, the date it last changed and how long ago. Anything with no date comes first, then oldest to newest, so the list opens on the work. Rows open in jContent and page like every other list here. Cut at five thousand rows, which it says; the counts and the groups still measure everything.
  • An end-to-end test suite. Two releases had shipped with no automated test of any kind. The suite covers the three endpoints across a guest, an editor without the dashboard's permission and a publisher, the values a path-shaped input can take, request validation, the rate-limit window and the two jContent screens. It asserts its own premise as well as its results, because a refusal proves nothing unless the account demonstrably holds the neighbouring rights and lacks only the one under test.
  • The shared Jahia module CI. The build was a checkout and one mvn install, so nothing gated a pull request beyond compilation. The workflows now call jahia/jahia-modules-action: a pull-request gate with static analysis and SonarQube, a merge path publishing a snapshot and an SBOM, a release path, and a scheduled full analysis. Also a pull-request template and the changelog-fragment mechanism the repository's own settings already imply.

Changed

  • The endpoints require the permission the dashboard route declares. That requirement lived only in the front end, so the screen and the server can no longer drift apart. An editor without it gets no dashboard and no robots.txt or llms.txt editor; the page drawer is unchanged and still opens for anyone who can read the page in the editing workspace.
  • A scan's scope must resolve inside the site it was started from, and it is resolved before it is compared rather than matched as a string, so a relative path cannot leave the site it came from. The language a request carries is now required to look like a language, at the endpoints and again where it becomes a node name.
  • The address a check fetches comes from the site or from configuration. A request may say how that host is reached, scheme and port, but only once it is already addressing it: a Host header is written by whoever sent the request. A site that declares no server name now has nothing to fetch until PUBLIC_BASE_URL says so, where before the request decided. A sitemap index names further files, and only a child on the same origin is followed.
  • A schedule belongs to the account that saved it and every run asks again whether that account still holds the permission, so revoking it stops the schedule instead of merely hiding the screen. Triggers are now tied to the bundle: removed when it stops, reinstalled from the repository when it starts, so a redeploy neither strands one nor discards an editor's schedule.
  • The module declares jcontent, robots and llms as dependencies. Jahia holds it back until those are started rather than starting it and failing at the first write.
  • Bundle-SymbolicName and Jahia-Depends come from the parent's properties only. Both were also written into the bundle plugin's instructions, so each had two sources that could disagree.

Removed

  • The range chart. It drew each group from its newest item to its oldest with the median marked, which encoded spread. Nobody asks that of a site, and on most groups the newest and oldest item share a date, so the range collapsed to a dot. Nothing else drew one.

Fixed

  • Seven advisories in the build's own dependencies, five against postcss and two against serialize-javascript, by taking the patched css-loader and copy-webpack-plugin. Neither ships in the bundle, so this is the build's supply chain rather than the running module.
  • Every declarative service component carries service.description and service.vendor, which is what the OSGi console reads when somebody asks what a service is and who ships it.

1.1.0

The visual release. Nothing new is measured; what was already measured is now drawn where a number has a shape worth seeing, and no findings list is capped any more.

Added

  • Charts, without a chart library. Four panels now carry a visual form drawn in plain HTML and CSS: a meter under the site score whose fill carries severity; horizontal bars for the score by section; a histogram of content age plus a range bar per type and section running from newest to oldest with the median marked; coverage and readiness score side by side per language; and a single stacked bar for structured-data coverage. Every mark has a hover and keyboard tooltip. A charting dependency inside a Module Federation bundle is weight every jContent page pays, plus a second theme to keep in step with Moonstone - and the forms needed are bars and a meter.
  • Colors from Moonstone's own tokens, validated rather than eyeballed. The two series hues (dark accent, purple) were run through a colorblind-safety and contrast validator on Moonstone's light surface as an adjacent pair. The plain accent fails 3:1 and is not used for marks; gray is a track, never a series. A missing value is never drawn as a zero-length bar - an unmeasured language renders as text saying so.
  • Pages with findings is a matrix, not a list. Pages down, failing checks across, one cell per pair colored by severity, columns ordered by how many pages fail them. A list of "15/17" chips said how many things were wrong on each page; it could not say which, or that two of them are wrong on every page - and a column that is solid top to bottom is the template roll-up, visible before anyone reads a number. Each cell and column header carries a tooltip; every status color ships with its word in the legend. Paged twenty-five at a time with Moonstone's pagination, the column set computed over every finding so columns do not reshuffle between pages.
  • Every findings list pages, none is capped. Sitemap findings, unlinked pages, address conflicts and invisible content used to stop at ten rows with an "and N more" line - which told the reader something was withheld and gave them no way to reach it. On a site with two hundred stale sitemap dates, a hundred and ninety were invisible. Each group now pages twenty-five at a time with Moonstone's control. Anything up to the chosen page size is shown in full, as is the failure matrix; the control itself appears once a list passes ten rows, so a denser view can be chosen and paged even when nothing is being withheld. A paged group also says "Showing 1–25 of 40" above its rows, so the header's count and the visible rows never disagree without the explanation sitting between them.

1.0.0

First release, so this section describes the whole module rather than a delta. It needs nothing from any external vendor.

Added

Seeing what a crawler sees

  • Crawler access check. One server-side fetch per AI crawler user agent, fifteen of them with a normal browser first as the control. No cookies, no session, redirects not followed, and the initial HTML is read rather than a rendered DOM. A page whose content only appears after JavaScript runs looks perfect to an editor and empty here, which is the point. Fetches run three at a time, so sixteen requests take about a second rather than two minutes.
  • robots.txt parsing and cross-check. Follows what the major crawlers actually do: consecutive User-agent lines share a group, the most specific group wins, the longest rule wins within it, Allow beats Disallow on a tie, and * and $ are honoured. The matched rule is always shown so the reasoning can be checked rather than trusted. Names the two disagreements between policy and reality: blocked but allowed, and reachable but disallowed.
  • llms.txt report. Presence, title, summary, section and link counts, and the common false positive where /llms.txt returns the HTML page with a 200.
  • Content no AI can ever read. Published pages a visitor with no account cannot open, listed with pages closed on purpose separated from pages closed by accident. A members area is not a defect. Also covers visibility conditions that have expired, and pages marked noindex.

Scoring

  • Eighteen checks in three groups, each reading one fact already observed and showing that fact beside the verdict. Deliberately a count of checks, not a rating out of 100: an editor can disagree with a specific line but not with a number they cannot see inside. Three severities, from "a crawler cannot read this at all" down to "worth doing".
  • Six signals read from HTML already in memory, so they cost no extra request: declared page language, hreflang alternates, sub-heading outline, alt-text coverage, JSON-LD @type breakdown rather than a bare count, and whether a modification date is published.

The whole site, not one page

  • Scheduled site scan. A Quartz cron job walks every published page, fetches each once as an AI crawler, and stores an aggregate plus the pages that failed something. The dashboard shows the overall figure, the movement since the last run, a breakdown by section, and the pages with findings. The schedule is built from dropdowns, with an optional scope and an enable toggle, and a running scan appears in the administration job list.
  • Findings roll up to the template that produced them. Templates are ranked by the pages they render, so the biggest single fix is first, and what the template is answerable for is separated from what merely happens on some of its pages. The drawer says so on the affected check: which template, how many pages share it, and that one fix there covers all of them. Nothing outside Jahia can do this, because nothing else knows which template rendered a page.
  • The sitemap is compared against reality. Every sitemap.xml entry is resolved back to a repository node instead of being fetched, so a 378-URL site costs no requests to check. Four disagreements are reported: published pages the sitemap never mentions, entries that resolve to nothing published, lastmod values that contradict the node's real modification date, and pages the sitemap advertises while their own markup says noindex. A fifth, reported as one finding rather than two: the sitemap module does not use vanity URLs, so a page that has one is listed under the address Jahia redirects away from - crawlers sent through a redirect to reach content with a direct address. The published set is built as guest and across every site language, because a sitemap is language-aware and written for anonymous crawlers. It has its own dashboard tab with its own refresh, because it is a comparison and not a score, and a presence test that requires the response to actually be a sitemap: "200 with a tag in it" would accept an HTML error page, parse to zero entries and report every published page as missing. No sitemap, not a sitemap, and could not be reached are three different messages. The dashboard lists each finding with a link to the node, its public URL and, for a wrong date, the two dates. The drawer carries the three findings that are about one page - absent from the map, advertised while saying noindex, or listed with a date that no longer matches - matched on path and language so a French finding never surfaces on the English page.
  • Pages nothing links to. A link graph built from the rendered HTML of every page the scan already fetches, so it costs no extra requests and every way of producing a link counts the same: a menu built from the page tree, a listing, a rich text link, a configured button. Repository references cover sources the scan never rendered. Two findings, because they are two problems: a page nothing links to and the sitemap does not list is unreachable; one reachable only through the menus or only through the sitemap is found but uncommitted. Navigation and content links are counted apart, since being in a menu that lists everything is not the same as somebody choosing to link to you. The drawer says which of the two this page is.
  • One page, several addresses. Jahia owns the vanity URL service, so every address a page answers on is a repository fact rather than something to be crawled for. Three findings: a vanity URL filed under a language the site does not serve, which returns a 404 to everybody; one page answering on several live addresses in the same language, loudest when none is marked default; and a page with aliases whose canonical tag is missing or names something else entirely. Deliberately no check for two pages claiming one address - Jahia renames the second on save, so it cannot happen.
  • llms.txt is checked against the site it describes. The file is written once and served unchanged, so it goes stale silently - an assistant is handed a map of a site that has moved on, and nothing anywhere says so. The served copy is diffed against what regenerating would produce right now, so the finding is exactly as trustworthy as the button offered to fix it, and the reason is named: the address now redirects, the page is no longer part of the generated list, it is gone, or it is published and missing from the file. Not tied to the readiness score, which regenerating does not move.
  • Where the site has gone stale. The age of everything a visitor can read, taken from the repository rather than from the dates pages choose to publish, in five buckets and broken down by content type and by section. Grouped rather than ranked, because a single oldest-first list puts a legal notice next to a news article and gets ignored; each type is judged against its own list instead, and nothing tries to guess which types are meant to age. A group is flagged only when its newest item is past a configurable threshold. A repository query, so it answers without waiting for a scan, and changing the threshold recomputes rather than filtering.
  • Readiness one language at a time. Coverage and score for every language the site declares, side by side, so a strong language cannot average out a weak one. A language that has never been scanned reports as not measured rather than as zero - a zero reads as "this market is broken" when it means "nobody has looked", and the two need opposite responses. Coverage is a repository read separating translated from published, over pages and content items alike. The drawer names the languages this page has no translation in, and any translation that exists but has not been published.
  • Structured data derived from the content model. Every other tool infers schema.org output from the words on a page; the content type already says what the content is, so it is derived instead. Map each type once - defaults only for Jahia's own types, since a custom type is somebody's model and its name is not evidence - and every item of it produces JSON-LD, with property sources found by convention rather than configuration. Required properties with no source are named rather than invented, and a generated name that disagrees with the title the page renders is reported as a conflict, because structured data that contradicts its page is worse than none. The drawer shows the snippet with a copy button; nothing is written into a page.
  • The page drawer now says where a page stands. A score with no reference point is not information, so the drawer carries the site average and the page's own section average beside it, how many pages link here from navigation and from content, and whether the page is in llms.txt.

Finding your way around it

  • Ten panels grouped into four. The dashboard reuses the same three groups the page drawer already sorts its checks into - can a crawler reach it, is what arrives usable, site-level files - so there is one vocabulary to learn rather than two, with the site score standing outside them as the summary of all three.

Writing, behind generate → diff → confirm

  • llms.txt generated from the published page tree. Deterministic: no model call and no external service, so the same site produces the same file every time and the rule can be read and predicted. Honours role visibility and noindex by walking as a guest, so a page a visitor cannot read is never advertised to an assistant.
  • AI crawler control in robots.txt. Allow or block per crawler, merged into the existing file. Everything not asked about is left byte for byte as found, comments and Sitemap lines included. Each crawler carries a one-line description of what it does, and a split between the seven that answer questions and the seven that only train models: refusing a training crawler costs no visibility, refusing an answering one takes the site out of that assistant's replies. The panel warns only for the second kind.
  • Nothing is overwritten unseen. Both write paths show a highlighted line diff against what is stored, unchanged runs collapsed, additions and removals marked with a sign as well as a colour. The apply button needs a second confirming click, and what gets written is exactly the text on screen, so hand edits made before applying survive.

Where it lives

  • A page drawer for one page, and a site settings page under Additional > SEO, beside Robots.txt and Sitemap. Both files belong to the site, so they are edited there rather than from whichever page an editor happens to have open.
  • English and French throughout, including every check label and its explanation.
Nécessite Jahia 8.2.0.0Mis à jour 2026-09-16