Title: Unseat Connector
Author: Ken Lundin
Published: <strong>1 juillet 2026</strong>
Last modified: 25 août 2026

---

Recherche d’extensions

![](https://ps.w.org/unseat-connector/assets/banner-772x250.png?rev=3594150)

![](https://ps.w.org/unseat-connector/assets/icon-256x256.png?rev=3594150)

# Unseat Connector

 Par [Ken Lundin](https://profiles.wordpress.org/kglundin/)

[Télécharger](https://downloads.wordpress.org/plugin/unseat-connector.2.16.1.zip)

 * [Détails](https://fr.wordpress.org/plugins/unseat-connector/#description)
 * [Avis](https://fr.wordpress.org/plugins/unseat-connector/#reviews)
 *  [Installation](https://fr.wordpress.org/plugins/unseat-connector/#installation)
 * [Développement](https://fr.wordpress.org/plugins/unseat-connector/#developers)

 [Support](https://wordpress.org/support/plugin/unseat-connector/)

## Description

Unseat Connector is the official WordPress plugin for the [unseat.ai](https://unseat.ai)
content platform from Unseat AI Corporation. It lets the platform publish and manage
SEO-optimized content on your site, and exposes the signals AI crawlers (ChatGPT,
Claude, Perplexity, Gemini) look for.

**What it does:**

 * **SEO Meta Fields** — Registers Rank Math, Yoast SEO, and SEOPress meta fields(
   title, description, focus keyword, social tags) for REST API read/write access.
   Works with or without an SEO plugin installed.
 * **Site Health Endpoint** — Provides a `/wp-json/seo-machine/v1/status` endpoint
   that reports WordPress version, active SEO plugin, permalink structure, and potential
   plugin conflicts.
 * **llms.txt Serving** — Serves your `llms.txt` and `llms-full.txt` pages as plain
   text at their canonical URLs, making your content index machine-readable for 
   AI crawlers.
 * **IndexNow Key** — Serves your IndexNow verification key file as plain text.
 * **AI-Crawler Allowlist** — Injects an explicit `robots.txt` allowlist for GPTBot,
   ClaudeBot, PerplexityBot, CCBot, Google-Extended, Applebot, and other LLM crawlers
   with `Crawl-delay: 0`.
 * **Fallback SEO Meta Tags** — When no SEO plugin is active, emits `<meta name="
   description">`, canonical, OpenGraph, and Twitter Card tags from the connector’s
   meta fields.
 * **Optional GA4 Tag** — Injects a Google Analytics 4 measurement ID via `wp_enqueue_script`
   when configured through the REST endpoint.

**Requirements:**

 * WordPress 5.5 or higher
 * PHP 7.4 or higher
 * An [unseat.ai](https://unseat.ai) account (for the publishing platform; the SEO
   meta and llms.txt serving features work without one)

### External services

This plugin can optionally load **Google Analytics 4** (gtag.js) from Google. This
integration is **off by default** and is only activated after a site administrator
sets a GA4 Measurement ID (format `G-XXXXXXXX`) through the plugin’s REST endpoint(`
POST /wp-json/seo-machine/v1/ga4`). No external service is contacted until that 
ID is configured.

What it is used for: when a Measurement ID is configured, the plugin enqueues Google’s
analytics script from `https://www.googletagmanager.com/gtag/js` so the site owner
can measure website traffic.

What data is sent and when: while a Measurement ID is configured, the script loads
on front-end page views for visitors who are not logged-in administrators (logged-
in administrators are excluded to avoid inflating analytics). On each such page 
view the visitor’s browser sends the standard Google Analytics data directly to 
Google — for example the page URL, referrer, IP address, and device/browser information.
No data is sent when no Measurement ID is configured, and the plugin itself does
not collect, store, or transmit any of this data to unseat.ai or to any other party.

This service is provided by Google. By configuring a GA4 Measurement ID you are 
choosing to use Google Analytics on your site; please review Google’s terms and 
privacy policy:

 * Google Analytics Terms of Service: https://marketingplatform.google.com/about/
   analytics/terms/us/
 * Google Privacy Policy: https://policies.google.com/privacy
 * How Google uses information from sites or apps that use its services: https://
   policies.google.com/technologies/partner-sites

No other external services are contacted by this plugin. All SEO meta, llms.txt,
IndexNow, robots.txt, sitemap, status, and cache-flush features operate entirely
on your own server.

## Installation

 1. Install directly from the WordPress plugin directory, or upload the plugin zip 
    via Plugins > Add New > Upload Plugin.
 2. Activate the plugin.
 3. Verify the connector is working by visiting `https://yoursite.com/wp-json/seo-machine/
    v1/status` (requires authentication).

The unseat.ai platform will automatically detect the connector during onboarding.

## FAQ

### Do I need Rank Math, Yoast, or SEOPress installed?

No. The connector registers SEO meta fields that work with or without an SEO plugin,
and emits its own meta/OpenGraph/Twitter Card tags when none is active.

### What is llms.txt?

llms.txt is a machine-readable index of your site content, similar to robots.txt
but designed for AI language models. It helps AI search engines discover and cite
your content.

### Does this plugin collect any data?

The plugin itself does not collect or store any visitor data — it adds REST API 
endpoints to your existing WordPress installation, and all of that data stays on
your server. The unseat.ai platform connects to these endpoints using your WordPress
application password.

The one exception is the **optional** Google Analytics 4 integration: if an administrator
configures a GA4 Measurement ID, the plugin loads Google’s analytics script so visitor
analytics data is sent from the visitor’s browser to Google. This is off until you
configure it. See the **External services** section above for exactly what is sent
and links to Google’s terms and privacy policy.

### Can I use this without an unseat.ai account?

Yes. The SEO meta field registration, llms.txt serving, and robots.txt allowlist
all work independently. The status endpoint is primarily useful for the unseat.ai
platform.

## Avis

Il n’y a aucun avis pour cette extension.

## Contributeurs/contributrices & développeurs/développeuses

« Unseat Connector » est un logiciel libre. Les personnes suivantes ont contribué
à cette extension.

Contributeurs

 *   [ Ken Lundin ](https://profiles.wordpress.org/kglundin/)
 *   [ Unseat AI ](https://profiles.wordpress.org/unseatai/)

[Traduisez « Unseat Connector » dans votre langue.](https://translate.wordpress.org/projects/wp-plugins/unseat-connector)

### Le développement vous intéresse ?

[Parcourir le code](https://plugins.trac.wordpress.org/browser/unseat-connector/),
consulter le [SVN dépôt](https://plugins.svn.wordpress.org/unseat-connector/), ou
s’inscrire au [journal de développement](https://plugins.trac.wordpress.org/log/unseat-connector/)
par [RSS](https://plugins.trac.wordpress.org/log/unseat-connector/?limit=100&mode=stop_on_copy&format=rss).

## Journal des modifications

#### 2.16.1

 * Fixed: on a site’s FRONT PAGE the founder’s entity links published nowhere. Rank
   Math anchors its per-page author byline at the page canonical, which on the front
   page is the home URL — so the byline looked like the site’s own identity node
   and, when the founder had written the homepage, carried a matching name. The 
   connector treated it as the site’s Person, merged the links onto it and skipped
   creating the real one; Rank Math then deleted that byline as unreferenced and
   the links went with it. On a site running Rank Math the front page now publishes
   the same identity node every other page already did.
 * Also fixed: where that byline sorted ahead of the real identity node, the Organization’s«
   founder » pointed at the byline — a reference to a node that was about to be 
   deleted. It now points at the identity node.
 * On a site with NO SEO plugin, where the connector rewrites the page head directly,
   it cannot add an identity node beside a byline it declines to use; on such a 
   site the front page publishes no person links rather than writing them onto the
   byline. No current site is affected — the one site on that path anchors its identity
   elsewhere and is unchanged.
 * No change on any other page type, and none to what is stored or to sites whose
   knowledge graph is a Person (that node is matched by its own anchor, unchanged).

#### 2.16.0

 * Entity data now also publishes on a site where Rank Math is INSTALLED but renders
   no schema graph of its own — a switched-off schema module, a theme that emits
   the identity nodes instead, or a page type Rank Math declines to describe. Previously
   the mere presence of Rank Math stood the head rewrite down in favour of a filter
   that never fired, so neither path published anything.
 * On any request where Rank Math does render its graph, output is unchanged: the
   filter path still owns that request, and the head rewrite still never runs alongside
   it.

#### 2.15.0

 * Company and founder entity data now publish on sites that do NOT run Rank Math—
   Yoast, SEOPress, AIOSEO, or no SEO plugin at all. The connector rewrites the 
   JSON-LD already in the page head rather than adding a competing block.
 * The site’s own person is now recognised by the founder’s name as well as by the`#
   person` anchor, so Yoast’s `<home>#/schema/Person/{{ID}}` is extended rather 
   than duplicated.
 * The company node is created when a page claims none, which needs the company 
   name — so this requires platform build 2.15.0 or newer on both sides. An older
   platform’s push is reported as unpublished rather than recorded as a success 
   it did not get.
 * Rank Math sites are unchanged: they keep the existing filter path and the head
   rewrite does not run on them.
 * The SEO-meta fallback comment now reports the real plugin version instead of 
   a hard-coded `v2.5.3`.

#### 2.14.0

 * The `seo-machine/v1/entity` endpoint now also stores the founder’s name and job
   title, and the `rank_math/json_ld` filter builds the site’s Person node from 
   them when — and only when — the site does not already have one.
 * This is the one case where the plugin creates a node rather than merging into
   one, and it is not a reversal of the rule above. Rank Math’s knowledge graph 
   is either an Organization or a Person, never both, so a company site emits no`#
   person` node at all and there is nothing to merge into and nothing to contradict.
   Where the site DOES emit its own Person node, this creates nothing and merges
   as before.
 * A name is required. `sameAs` links hung on a nameless node are an identity claim
   about nobody, so with no founder name the person links stay unpublished and the
   site is left exactly as it was.
 * The new node is anchored at `<home>#person`, carries `worksFor` pointing at the
   Organization node the page actually emits, and the Organization node gets the
   reciprocal `founder` edge — but only if it does not already declare one. A `founder`
   the site owner has set is the site owner’s claim, not this plugin’s to overwrite.
 * Requires platform build 2.14.0 or newer on both sides: the endpoint echoes the
   identity back, and a platform that does not receive it reports the push as unpublished
   rather than recording a success it did not get.

#### 2.13.0

 * New `seo-machine/v1/entity` endpoint (administrator-only) stores the two `sameAs`
   lists — the organization’s authority profiles and the founder’s — and a `rank_math/
   json_ld` filter merges them into the Organization and Person nodes Rank Math 
   ALREADY emits, on every page of the site.
 * It merges and it never creates. If Rank Math emits no knowledge-graph block, 
   this plugin emits nothing and the links stay unpublished. That is deliberate:
   a second Organization block claiming to describe the same company is an entity
   conflict, and search engines resolve a contradiction worse than they resolve 
   a gap. Silence is recoverable; a competing claim is not.
 * The nodes are identified by the `@id` suffix Rank Math anchors them at (`#organization`/`#
   person`). `@type` is only consulted for a node carrying no `@id` at all: it is
   whatever the site owner picked in the setup wizard, and the LocalBusiness subtype
   list is effectively unbounded, so matching on it alone would miss every site 
   that chose a specific one. An `@id` that does not match is a refusal, never a
   fall-through — a real page also carries `<post-url>#author` (a Person named after
   the WordPress login account) and `#schema-NNNNNN` duplicates of the Organization
   node, and writing into those would assert one identity from many conflicting 
   nodes.
 * Existing `sameAs` entries come first and keep their relative order; ours are 
   appended. De-duplication is case-insensitive and ignores a trailing slash, and
   it runs across the WHOLE list rather than only over what we add — otherwise appending`
   https://x.com/acme` beside an existing `https://X.com/acme/` would publish the
   same profile twice. Two consequences worth stating plainly rather than discovering
   later: an existing entry has surrounding whitespace trimmed off it, and if the
   site’s own list already carried the same URL twice under different spellings,
   the second copy is dropped. Nothing else about an existing entry is rewritten,
   and none are reordered.
 * Stored URLs are validated on the way in, not on the way out: anything that is
   not `http`/`https` is dropped, and the list is capped. A stored value is emitted
   into the `<head>` of every page, so the endpoint is the only place that check
   belongs.
 * The endpoint echoes back exactly what it stored, and the platform treats a mismatch
   as a failed push rather than a success. `ok: true` on its own does not prove 
   the links landed — a redirect can downgrade the POST to a GET that returns the
   site’s OLD list, and the sanitizer drops silently — and a marker written on that
   flag would freeze the site’s entity data permanently.
 * `seo-machine/v1/status` now reports how many entity links the site is holding,
   so the platform can confirm a push landed without re-sending it — and can tell«
   we never sent them » apart from « we sent them and there is no Rank Math block
   to merge them into ».

#### 2.12.0

 * The `seo-machine/v1/robots` diagnostic no longer speaks a single English paragraph
   chosen by a branch chain. It returns `findings` — a list, worst first, where 
   each entry carries the `evidence` it read and a `message` that may assert nothing
   outside it. Findings COMPOSE: none suppresses another, and `severity` sorts the
   list rather than steering control flow. `cache_suspected` and `reason` are gone.
 * This replaces the design, not a bug in it. Twelve review rounds each found a 
   fresh defect INSIDE the previous round’s fix, and all twelve were one class: 
   a claim that outran its reads. That was inevitable — the explanation was thirteen
   hand-written branches over eight mostly tri-state facts, a state space in the
   hundreds, where branch ORDER decided which sentence got spoken and every new 
   branch silently pre-empted an older one. A structure that cannot be reviewed 
   to correctness will keep producing correct-looking sentences about faults it 
   did not observe, however many rounds it survives.
 * A whole-site block is now read correctly when this plugin’s own output is merged
   into it. RFC 9309 merges records naming the same user-agent, and this plugin 
   injects `Allow: /?unseat_sitemap=` and `Allow: /wp-admin/admin-ajax.php` into
   the `*` group. A maintenance plugin emitting `User-agent: *` / `Disallow: /` 
   therefore lands in the SAME group as those Allows — and the predicate, which 
   bailed on any non-empty `Allow:`, concluded the site was not blocked and returned`
   connector_governs: true` for a site no crawler could enter. The plugin’s own 
   output was masking a whole-site ban from the plugin’s own detector. Only an `
   Allow:` of the ROOT re-opens a `Disallow: /`; `Allow: /blog` re-opens `/blog`
   and leaves the rest of the domain dead, per longest-match.
 * `serving_wp_output` replaces `cache_suspected`. It is a byte comparison of what
   the live URL returns against what WordPress generates. The old field was derived
   from the `# Injected by unseat-connector` marker, which this plugin appends AFTER
   whatever blob won the filter — so the marker can sit on output we did not produce,
   and any verdict drawn from it is fabricated evidence.
 * When the live `/robots.txt` does not return a 2xx, every served-side fact is 
   set to null at the source rather than guarded at each consumer. A read that failed
   is not a fact, and a diagnostic that has to remember to check `served_code` before
   each of nine other fields will eventually forget once.
 * No finding gives remediation that depends on a fact it did not read. A physical`
   robots.txt` on disk is reported as present and the operator is told to read it
   and compare it against `rendered` — not to delete it, because if WordPress’s 
   own output is currently wrong that file may be the only thing keeping the site
   crawlable. A crawler group that escapes `*` is named and the rules are to be 
   added INSIDE it — not to delete the group, because all this endpoint read is 
   that the group fails to block query-string URLs, never what else it carries.
 * `connector_governs` is now a projection of the findings and is false whenever
   any critical one fires. Two fields computed separately from the same reads is
   how this endpoint once shipped `connector_governs: true` in the same payload 
   as a sentence describing a de-indexed site.
 * A SEARCH ENGINE banned from the entire domain by name is now a critical finding.
   It used to produce NOTHING. The coverage pass exempts any named group that blocks
   its own bot — correct for `User-agent: AhrefsBot` / `Disallow: /`, which is banned
   harder than our rules and where « delete that group » would unblock a scraper
   somebody banned on purpose — but it applied the same reasoning to Googlebot, 
   where being banned IS the emergency and not the reason to stop looking. A file
   carrying `User-agent: Googlebot` / `Disallow: /` returned an empty `findings`
   list and `connector_governs: true`, on a site Google cannot crawl one URL of.
   It is reported and never remediated: this endpoint has read that the ban is there,
   not why it is there, and a staging box bans Googlebot on purpose.
 * `Disallow: **` is read as the whole-site block it is. Both recognisers match 
   a literal set of spellings and the collapse step returned an all-star path untouched,
   so `**` was in neither set: a site with every URL blocked was reported as one
   where « query-string URLs are NOT blocked » — the exact opposite of the truth—
   while `Disallow: *`, the same directive, fired two criticals. A banned Googlebot
   spelled that way got an escape lecture telling the operator to add query rules
   to the group that had already shut Google out of his domain. All-star paths now
   collapse to the one canonical spelling. (An EMPTY path is not an all-star path:
   a bare `Allow:` is an explicit no-op in RFC 9309, and collapsing it to `allow:*`
   would turn a line that re-opens nothing into one that re-opens everything.)
 * The whole-site `*` critical reads the paths its group re-opens, not just the 
   fact of the ban. `Disallow: /` with `Allow: /blog/` shuts every crawler out of
   everything EXCEPT `/blog/` — and the finding said the domain was « removed from
   search results » and to fix the root `Disallow:` « before anything else », which
   would have thrown the entire site open to crawlers somebody fenced into one subtree
   on purpose. Same defect as the named-bot ban below, one group over, and `*` is
   the commoner place to write a fence. The predicate behind it stays deliberately
   LOOSE — the strict one is what a competing plugin’s merged `Disallow: /` hid 
   behind the connector’s own injected `Allow:` lines — so what had to learn to 
   read the paths was the prose, not the test. On a connector site that list is 
   usually our own two `Allow:` lines, and naming them is exactly right: they are
   the only URLs a crawler can still reach.
 * A de-indexed WordPress site is no longer told its robots.txt is a deliberate 
   fence it should ignore. « Is this a fence or a shut-out? » was answered by asking
   whether the group re-opened ANY path — but WordPress core’s robots.txt default
   always carries `Allow: /wp-admin/admin-ajax.php`, and this plugin injects two`
   Allow:` lines of its own, so on a real site that answer was always YES. The honest
   branch was unreachable: a site with `Disallow: /` and every content URL blocked
   was reported as a fence around `admin-ajax.php`, told « if that fence is deliberate,
   you should ignore this », and expressly forbidden to delete the root `Disallow:`—
   the one repair. The same masking bug as the merged-`Allow:` defect above, one
   layer out: the boilerplate that once hid a ban from the PREDICATE went on to 
   hide it from the OPERATOR. A fence is now judged on the paths somebody CHOSE —
   boilerplate subtracted — for the `*` group and for named bots alike, so a hand-
   pasted `User-agent: Googlebot` / `Disallow: /` / `Allow: /wp-admin/admin-ajax.
   php` reads as the total ban it is. The boilerplate list is fixed rather than 
   derived from our own `rules()`, because `rules()` is filterable and a third-party
   filter that widened it would SUPPRESS a real fence; a test fails if our own directives
   ever drift out of it.
 * `Disallow: /*$` is read as the whole-site block it is. A `*` immediately before
   the end-anchor makes the anchor vacuous — `*` already absorbs everything up to
   the end — so `/*$` matches every URL on the domain, but the collapse step left
   any `$`-terminated path alone and both recognisers missed it, reporting a fully
   blocked site as one whose query-string URLs were not blocked. A `$` that does
   NOT follow a star is a real anchor and still survives untouched: `/$` is the 
   homepage and nothing else, and `/*.php$` is not a whole-site block.
 * A search engine fenced into a few paths is no longer reported as one shut out
   of the site. `Disallow: /` plus `Allow: /blog/` is not a ban: under longest-match
   that crawler reaches `/blog/` and nothing else. The whole-site-ban critical fired
   on it anyway — told the operator the crawler « will not crawl a single URL on
   this domain », which is false and checkable against his own GSC, and then told
   him to remove the whole-site `Disallow:`, which would have thrown the ENTIRE 
   site open to a bot he deliberately fenced into one subtree. A critical whose 
   remediation is the harm. The two cases are now two findings: a total ban (a root`
   Disallow:` and no `Allow:` at all) stays critical, and a fence is reported as
   a fence, names the paths it re-opens, and orders nothing.
 * Two sentences no longer claim to be exhaustive about evidence they never read.
   The whole-site `*` critical said « every crawler that obeys robots.txt is excluded»—
   false for any crawler with a group of its own, which is the entire bug this endpoint
   exists for. And the served-differs-from-generated major named a CDN cache and
   a physical file as « the two things that do this », so an operator behind an 
   nginx `location = /robots.txt` rewrite purged a cache that was not the cause 
   and hunted a file that was not there.
 * A site with « Discourage search engines » ticked is no longer told to hunt a 
   plugin that does not exist. Both injecting filters no-op when search-engine visibility
   is off, so the connector’s rules are absent BY DESIGN — and the report shipped
   two majors blaming a competing `robots_txt` callback for discarding them, ranking
   the one sentence that told the truth below both. The cause is now read from `
   blog_public` rather than assumed, and both findings declare it. This is not an
   edge case: it is every staging clone promoted to production with the checkbox
   still ticked.
 * A UTF-8 BOM no longer hides a whole-site block. `trim()` does not strip a BOM,
   so `﻿User-agent: *` failed to parse as a user-agent line and every rule in that
   group was dropped as belonging to no group. A `robots.txt` saved with a BOM by
   a Windows editor and carrying `Disallow: /` was reported as a file that merely
   fails to block query strings — the opposite of the truth, with the whole-site
   critical silent.
 * `serving_wp_output` compares content, not raw bytes. A host that rewrites line
   endings changes no directive, and the byte compare called that « the live URL
   is not WordPress’s output » and sent the operator after a CDN that was serving
   exactly what WordPress generated.
 * The normalize/collapse/merge pipeline lives in one function now that two readers
   need it. It had already drifted once: the whole-site reader skipped the trailing-
   star collapse, so `Disallow: /**` was seen by one reader and missed by the other
   in the same response.
 * A banned Googlebot no longer gets a critical and a major that contradict each
   other. Two readers held two different notions of « totally banned » — one had
   learned that boilerplate `Allow:` lines do not count, the other had not — so `
   User-agent: Googlebot` / `Disallow: /` / `Allow: /wp-admin/admin-ajax.php` shipped
   a critical saying Google « will not crawl a single page on this domain » NEXT
   TO a major saying that group « does not block query-string URLs » and to add 
   query rules inside it. The major is false — Google is already blocked from every
   URL, query-string or not — and following it made two findings disappear while
   Google stayed banned. Both readers now share one predicate, so the total-ban 
   exemption and the total-ban critical can never disagree about the same group 
   again.

#### 2.11.0

 * Fixed the Rank Math robots.txt check, which read an option name that has never
   existed. Rank Math stores its custom robots.txt in `rank-math-options-general`(
   hyphens); this plugin read `rank_math_options_general` (underscores), following
   the spelling of every other Rank Math name it touches — the post meta and `rank_math_modules`
   really are underscored. `get_option()` on an absent name returns false, so the
   check reported « no Rank Math override » on the one site that had one, and the
   release below silently did nothing. The name now lives in a single constant, 
   read and written from one place.
 * This was the whole of the revheat.com bug. Rank Math registers the `robots_txt`
   filter only when its stored content is non-empty, and then returns that content
   and discards everyone else’s. A stale blob pasted into its editor had been replacing
   this plugin’s rules for months, re-declaring a `User-agent: Googlebot` group 
   that held only `Disallow: /wp-admin/` — and robots.txt has no inheritance, so
   Googlebot obeyed that group, ignored `*`, and never saw the `Disallow: /*?` that
   would have kept it off ~60 junk query-string clones of the homepage.
 * The release route (`POST seo-machine/v1/robots` with `release_override=true`)
   now re-reads the option after clearing it and reports `released` from what the
   database says, not from what it tried to do. A write swallowed by a `pre_update_option_*`
   short-circuit or a read-only database previously reported success over an unchanged
   site.
 * `seo-machine/v1/robots` now observes the query-string rules directly (`query_rules_in_rendered`/`
   query_rules_in_served`) instead of inferring them from the `# Injected by unseat-
   connector` marker. The marker is emitted by the AI-allowlist filter alone; the`
   Disallow: /*?` rule comes from a different filter that carries no marker and 
   bails silently on plain permalinks or a first group with no `User-agent:` line.
   A site could therefore carry the marker, report `connector_governs: true`, and
   still be leaking every query-string URL to Googlebot.
 * That check is anchored to GROUPS, not to the document. robots.txt has no inheritance:
   a crawler with its own `User-agent:` group obeys that group and ignores `*` entirely.
   A file can carry `Disallow: /*?` under `User-agent: *` and also declare a `User-
   agent: Googlebot` group holding only `Disallow: /wp-admin/` — and Googlebot will
   crawl every query-string URL. That is precisely the file revheat.com served, 
   so « the rule appears somewhere in the document » is not a check, it is the bug
   wearing a tick. The new `query_rules_shadowed_by_served` names each crawler that
   has walked out of the group the rules are in and can still reach a query-string
   URL.
 * Directives are compared per RFC 9309 — names case-insensitive, whitespace around
   the colon insignificant, paths case-sensitive. Records naming the same user-agent
   are merged, as Google’s parser merges them. An exact string match read `disallow:/*?`
   as absent and told the operator « query-string URLs are NOT blocked » about a
   file that blocks them perfectly well, which is the wrong answer to give about
   the one input this endpoint exists to read: a robots.txt somebody else wrote.
 * A group is only an escape if a query-string URL is still REACHABLE through it—
   never merely because it does not repeat this plugin’s rules verbatim. A group
   carrying `Disallow: /*?` without the `Allow: /?unseat_sitemap=` exception is 
   stricter than `*`, not looser; so is one blocked from the whole site (`User-agent:
   AhrefsBot` / `Disallow: /`). Neither is reported. And where a group IS reported,
   the remediation is to add the query-string rules inside it — never to delete 
   it. This endpoint reads one thing about a group, that a query-string URL can 
   still be reached through it; it does not read what else the group carries. « 
   Delete it, `*` already covers them » is a coverage claim nothing here checked,
   and against a `User-agent: Googlebot` group holding `Disallow: /staging/` it 
   hands Googlebot the staging site.
 * Of the groups that genuinely do escape, only a crawler that actually INDEXES 
   flips the verdict. A bot that ignores `*` but files nothing into a search index
   is named in `query_rules_shadowed_by_*` as evidence and does not fail the site—
   a diagnostic that reddens a healthy install teaches operators to ignore it.
 * Every `reason` is written from the reads in the same response. The live URL’s
   HTTP status is now read, so a host that 404s `/robots.txt` is no longer told 
   to purge a CDN; a physical robots.txt on disk is named even when the verdict 
   is healthy, because a file the web server may be serving directly is a frozen
   copy that no future release can update; and no branch asserts what the live URL
   returns on a request where fetching it failed.
 * The sentence that endpoint speaks is now a pure function of the reads that produced
   it, so it can be tested without a WordPress install, a live HTTP fetch or a filter
   replay. It could not be before, and that is where every defect above was found—
   a diagnostic whose explanation is untestable will happily explain a fault it 
   did not observe.
 * The release route now verifies its BACKUP before it clears anything, and refuses
   to clear an override it could not back up. It re-read the option it cleared but
   not the option it saved — the write whose failure cannot be undone. A host or
   security plugin that filters option writes by name could swallow the backup while
   letting Rank Math’s own option through, and the route would report `released:
   true` over a customer’s hand-written robots.txt that no longer existed anywhere.`
   backup_option` is also named only when a backup provably exists; it used to be
   asserted on every call, including calls that wrote none.
 * The verdict now reads the live URL’s HTTP STATUS, not just its body. A non-2xx
   response still has a body, and that body can be a perfectly correct robots.txt—
   a plugin fataling on `shutdown` after the file has already flushed returns HTTP
   500 with the right content on the wire. Every content check passed and the verdict
   came back `connector_governs: true`, in the same payload as a `reason` reading«
   what came back is an error page, not the robots.txt crawlers read ». A response
   we could not make sense of is a read we did not make.
 * On a subdirectory install the endpoint no longer invents a fault. There, the 
   rules are scoped (`Disallow: /blog/*?`), and a `*` group carrying the site-wide`
   Disallow: /*?` blocks strictly more while our `Allow:` exception survives untouched—
   a correct site. It was told its `Allow: /?unseat_sitemap=` was missing, that 
   its fallback sitemap was unreachable, and was pointed at a URL that install does
   not use. The endpoint now names the rules it actually observed to be absent rather
   than guessing which one it was.
 * A group that blocks query-string URLs in a DIFFERENT SPELLING is no longer called
   an escape. `Disallow: /*?*` blocks exactly what `Disallow: /*?` blocks — a trailing`*`
   matches the empty string — and it is the commoner spelling in the wild. A `User-
   agent: Googlebot` group carrying it was reported as escaping, told the operator«
   query-string URLs are wide open » for a crawler that could not reach a single
   one, and returned `connector_governs: false` on a correct file forever. A trailing
   wildcard run is now collapsed before comparison. Only a trailing run: `Disallow:/*?*
   =*` needs a `=` and is genuinely narrower than our rule, so it is still reported.
 * The rules named in `query_rules_missing_from_star_rendered` are printed in the
   spelling this plugin emits, matching `query_rules`; they were being emitted lower-
   cased, so the two fields could not be diffed against each other. The field is
   null, not an empty list, when the filter replay threw and the `*` group was never
   read — an empty list reads as a clean bill of health on a request that looked
   at nothing.
 * The release route explains every failure it can have, not just one. A backup 
   that verified while the CLEAR write was rejected — a settings-lock plugin guarding
   Rank Math’s option specifically — returned `released: false` with nothing to 
   explain it, and the operator was left reading a `reason` about a competing robots.
   txt filter, which is true of the site but wrong about that call. `release_blocked`
   is now non-null in both failure states and says plainly which write was refused.
 * The backup verification now bypasses the object cache. `update_option()` seeds
   the cache with the value it just tried to store, so re-reading it handed back
   our own array out of memory and passed the check without the database ever being
   consulted — a verification that verified itself.
 * The Rank Math unhook matches a statically-registered callback too, not only an
   object instance — in all three callable spellings, including the `'RankMath\Foo::
   robots_txt'` string. It exists to survive a priority change without becoming 
   a silent no-op; testing only for an object left it one refactor away from exactly
   that.
 * A `User-agent: *` group that disallows the ENTIRE site (`Disallow: /`) is now
   read, named, and reported before anything else. Nothing in the endpoint had ever
   looked for it: the query-rule checks read the same file, found `Disallow: /*?`
   absent, and printed « Query-string URLs are NOT blocked » — the exact opposite
   of the truth — about a site where every URL is blocked to every crawler, without
   once mentioning the disallow that was removing the domain from search results.
   A staging robots.txt pasted into an SEO plugin’s editor is precisely how a live
   site arrives in that state, which makes it the single worst input this endpoint
   can be handed. It is now checked on both the rendered and the served copy, and
   it names its own cause: a stale edge copy, a competing callback, or a file on
   disk. It is measured straight off the body rather than off the query-rule pass,
   so a plain-permalink site — where this plugin emits no query rules and the coverage
   pass never ran — is no longer exempt from the check, which is how one could previously
   be handed a clean bill of health while de-indexed.
 * The equivalent-spelling collapse now applies to `Allow:` as well as `Disallow:`.`
   Allow: /?unseat_sitemap=*` is this plugin’s own rule — a trailing `*` matches
   the empty string — and it was being reported missing, which told a correct site
   that its `Allow:` exception was gone and its fallback sitemap unreachable. The
   same fabrication as the `Disallow: /*?*` case above, aimed at our own output 
   instead of a stranger’s.
 * `Disallow: *?` is recognised as the same rule as `Disallow: /*?` — a leading `*`
   matches the leading slash. A `User-agent: Googlebot` group carrying it was called
   an escape and the operator told query-string URLs were wide open for a crawler
   that could not reach one.
 * `release_blocked` no longer tells the operator to disregard `filters` when the
   clear write is refused. The override IS still in place and still winning in that
   case — that is why they called the route — so the sentence contradicted the `
   reason` shipping beside it in the same payload.
 * The whole-site-ban verdict no longer tells an operator to delete the file that
   is keeping their site in …

## Méta

 *  Version **2.16.1**
 *  Dernière mise à jour **il y a 2 mois**
 *  Installations actives **Moins de 10**
 *  Version de WordPress ** 5.5 ou plus **
 *  Testé jusqu’à **7.0.7**
 *  Version de PHP ** 7.4 ou plus **
 *  Langue
 * [English (US)](https://wordpress.org/plugins/unseat-connector/)
 * Étiquettes
 * [AI](https://fr.wordpress.org/plugins/tags/ai/)[llms.txt](https://fr.wordpress.org/plugins/tags/llms-txt/)
   [rest-api](https://fr.wordpress.org/plugins/tags/rest-api/)[schema](https://fr.wordpress.org/plugins/tags/schema/)
   [seo](https://fr.wordpress.org/plugins/tags/seo/)
 *  [Vue avancée](https://fr.wordpress.org/plugins/unseat-connector/advanced/)

## Évaluations

Aucun avis n’a encore été envoyé.

[Votre avis](https://wordpress.org/support/plugin/unseat-connector/reviews/#new-post)

[Tout voir](https://wordpress.org/support/plugin/unseat-connector/reviews/)

## Contributeurs

 *   [ Ken Lundin ](https://profiles.wordpress.org/kglundin/)
 *   [ Unseat AI ](https://profiles.wordpress.org/unseatai/)

## Support

Quelque chose à dire ? Besoin d’aide ?

 [Voir le forum de support](https://wordpress.org/support/plugin/unseat-connector/)