Title: U&amp;AI SEO Bridge
Author: ybriki
Published: <strong>September 8, 2026</strong>
Last modified: September 8, 2026

---

Search plugins

![](https://s.w.org/plugins/geopattern-icon/uai-app-bridge.svg)

# U&AI SEO Bridge

 By [ybriki](https://profiles.wordpress.org/ybriki/)

[Download](https://downloads.wordpress.org/plugin/uai-app-bridge.0.15.0.zip)

 * [Details](https://wordpress.org/plugins/uai-app-bridge/#description)
 * [Reviews](https://wordpress.org/plugins/uai-app-bridge/#reviews)
 * [Development](https://wordpress.org/plugins/uai-app-bridge/#developers)

 [Support](https://wordpress.org/support/plugin/uai-app-bridge/)

## Description

The U&AI content platform audits client sites and produces a list of SEO issues.
This plugin is how those fixes reach your site — and, just as importantly, how you
see and control them.

**It never renders anything over your own content.** If a fix cannot be made to 
the real value, it is not made: the platform raises it for a person instead. Concretely:

 * Headings are changed in the page’s own stored content (Elementor’s data, or the
   post content), so the edit is visible and reversible in your own editor. A page
   the plugin cannot edit raises a task rather than an override.
 * Image alt text is only ever added where alt is empty. Where an image is in the
   Media Library, its own alt text is what renders — clear it there and nothing 
   is injected.
 * `robots` directives are merged: the plugin may add `noindex`, and can never remove
   or override one you set.
 * Third-party JSON-LD is never deleted. Invalid schema is reported as an issue;
   what to do about it is your decision.
 * Redirect rules only fire on an address that genuinely returns 404. Republish 
   a page at a path that had a rule and visitors reach your page again.
 * Where your theme or another SEO plugin already prints a meta description, this
   plugin’s own tag stands down rather than removing theirs.

#### You can see and change everything it does

**On every post, page, category and tag** there is a “U&AI SEO” panel listing everything
the plugin renders for that item: search title, meta description, canonical, noindex/
nofollow, structured data, language alternates, pagination links, injected image
alt text, and whether the plugin is managing that item at all. Text fields are editable;
machine-generated additions have a “Remove it” checkbox.

**Anything you set by hand is yours permanently.** The field is marked as owner-
edited and no automated write can overwrite it afterwards — enforced inside the 
plugin at the metadata layer, so it holds for every write path, not only the ones
that ask nicely.

**Tools  U&AI SEO** covers the site-wide behaviour: every redirect rule with a delete
button, plus switches for image-alt injection, external-link `rel` attributes, HTTP
security headers and the analytics snippet. A switch you turn off stays off — no
automated request can turn it back on.

#### Working alongside another SEO plugin

When another SEO plugin owns the `<head>`, the bridge does not compete for it.

**With Yoast SEO, values are written into Yoast’s own fields**, so they appear in
Yoast’s sidebar where you already edit, and they stay there if you deactivate this
plugin. A value you type in Yoast always wins: the plugin only fills a field you
left empty, and it records what it wrote so it can update its own value later without
ever touching yours. The same applies to a per-post noindex you set in Yoast — a
value from the platform can never re-expose a page you hid.

With other SEO plugins the platform writes their fields over the WordPress REST 
API where they expose them, and reports the fix as needing manual attention where
they do not.

#### Core REST meta

The SEO title and meta description are also registered with `show_in_rest`, so they
can be written in the same `POST /wp-json/wp/v2/posts` call that creates a post:

    ```
    {"title": "...", "content": "...", "meta": {"_uandai_seo_enabled": true, "_uandai_seo_title": "...", "_uandai_meta_description": "..."}}
    ```

This is what lets U&AI publish a generated article — body, featured image and SEO
fields — in one request. The keys are protected meta, so writes require the `edit_post`
capability for the target post, and they are subject to the same owner-edit protection
as everything else. Canonical URL and robots stay bridge-only because their validation
depends on the target post.

Post types are every public REST-enabled type (always including `post` and `page`);
filter with `uandai_seo_bridge_rest_meta_post_types`.

#### Conflict behaviour

If the bridge detects an active SEO plugin (Yoast, Rank Math, AIOSEO, SEOPress, 
Squirrly, The SEO Framework, or any plugin whose folder name contains “seo”), `head_injection_safe`
becomes false in `/health` and the plugin stops emitting its own head tags, so there
are never duplicate `<title>`, `<meta>` or `<link rel="canonical">` tags.

To force the bridge to coexist with another plugin, hook `uandai_seo_bridge_allowed_seo_plugins`(
returns an allow-list of plugin slugs) or `uandai_seo_bridge_head_injection_safe`(
returns the final boolean).

#### Canonical escape hatch

By default the bridge rejects canonical URLs whose host differs from the post’s 
host. Pass `force_external_canonical=1` (in the request body or query string) to
permit cross-host canonical values when migrating between domains.

#### Endpoints

All endpoints live under `/wp-json/uandai-seo/v1/` and accept and return JSON. Authenticate
with the same WordPress Application Password the U&AI dashboard already uses. Site-
wide settings and `/health` require `manage_options`; per-object routes require `
edit_post` or `edit_term` on that specific object.

Per post and term:

 * `GET|POST /page-meta/{post_id}` — read or write a post’s SEO fields
 * `POST /page-meta/{post_id}/clear` — remove what the plugin manages (values you
   set by hand are preserved)
 * `GET|POST /term-meta/{taxonomy}/{term_id}` — the same for a category or tag
 * `POST /term-meta/{taxonomy}/{term_id}/clear`
 * `POST /image-alt/{post_id}` — fill empty image alt text

Site-wide:

 * `GET /health` — version, capabilities, detected SEO plugins, duplicate installs,
   and which features you have switched off
 * `POST /resolve-url` — map a crawled URL to a post or term
 * `GET|POST /redirects` — list or replace redirect rules
 * `GET /robots`, `POST /robots/configure` — robots.txt additions
 * `GET /security-headers`, `POST /security-headers/configure`
 * `GET /dom-normalize`, `POST /dom-normalize/configure` — the body passes (image
   alts, external-link rel)
 * `POST /sitemap/configure`
 * `POST /snippet-config` — analytics snippet configuration
 * `POST /purge-cache` — best-effort cache purge after a write

### External services

This plugin connects your site to U&AI (https://uandai.co), the platform that produces
the SEO fixes it writes. A U&AI account is required to use it: you create a WordPress
Application Password and enter it in the U&AI dashboard, and that is what authorises
everything below.

The plugin’s own PHP never makes an outbound request. Traffic between your site 
and U&AI happens in two ways.

**1. U&AI calls your site.** The platform authenticates against the `uandai-seo/
v1` REST namespace with the Application Password you supplied and writes the SEO
fields listed under “Endpoints”. Nothing leaves your server except the responses
to those calls.

**2. The analytics snippet runs in your visitors’ browsers.** Only if you enable
the U&AI analytics snippet for this site in the U&AI dashboard: the platform pushes
a script URL to `POST /uandai-seo/v1/snippet-config`, and the plugin then enqueues
that script on the front end. The script is served by your U&AI instance — by default`
https://app.uandai.co/static/platform/snippet.js` — reads its collection settings
from `https://app.uandai.co/api/platform/snippet-config/`, and reports to `https://
app.uandai.co/api/platform/events/`.

What the snippet sends, by the collection level configured on your U&AI account:

 * Always: page URL, path, title, canonical URL and meta description; the referrer;
   UTM parameters and advertising click identifiers; the derived traffic source;
   device type; and a first-party anonymous visitor id and session id generated 
   in the browser.
 * Interaction level: scroll depth, clicks and call-to-action clicks, and form-field
   interaction metadata — field name, label and type, typing timing, and whether
   text was pasted. Field _values_ are sent only for fields your account explicitly
   allow-lists.
 * Interaction level, when enabled and only for a sampled share of sessions: an 
   rrweb session recording of the page, with input masking applied.
 * Attribution level: the values of third-party analytics cookies already present
   in the visitor’s browser (by default `_ga` and `_fbp`).

The snippet is never loaded in wp-admin and never for a logged-in WordPress user,
so staff activity stays out of your visitor numbers.

Consent: whether the snippet waits for consent is a per-account setting in U&AI.
With it on, nothing but a page view is recorded until your consent tool calls `window.
__uandaiOnConsent()` — form interaction, session recording, third-party cookie values
and the GA4 identify call all stay off until then. With it off, collection starts
on page load. Set it to match the consent regime your site operates under.

To stop it: switch the snippet off for the site in the U&AI dashboard (the platform
then pushes `enabled: false`), or deactivate this plugin — deactivating removes 
the snippet and ends the platform’s ability to write to the site.

U&AI terms of use: https://uandai.co/terms
 U&AI privacy policy: https://uandai.
co/privacy

## Reviews

There are no reviews for this plugin.

## Contributors & Developers

“U&AI SEO Bridge” is open source software. The following people have contributed
to this plugin.

Contributors

 *   [ ybriki ](https://profiles.wordpress.org/ybriki/)

[Translate “U&AI SEO Bridge” into your language.](https://translate.wordpress.org/projects/wp-plugins/uai-app-bridge)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/uai-app-bridge/), check
out the [SVN repository](https://plugins.svn.wordpress.org/uai-app-bridge/), or 
subscribe to the [development log](https://plugins.trac.wordpress.org/log/uai-app-bridge/)
by [RSS](https://plugins.trac.wordpress.org/log/uai-app-bridge/?limit=100&mode=stop_on_copy&format=rss).

## Changelog

#### 0.15.0

 * **This plugin no longer renders anything over your own content.** Earlier versions
   replaced the first `<h1>` on a page and prepended their own, on every render,
   after your page builder had finished. Nothing here reacted to a human edit, so
   an owner who changed the heading in Elementor was silently overridden every time—
   one site could not edit a heading at all until they deactivated the plugin. Headings
   are now changed in the page’s own stored content (Elementor’s data or the post
   content), so the edit is visible and reversible in your own editor. A page this
   plugin cannot edit raises a task for a person instead of a render-time override.
 * **A “U&AI SEO” panel on every post, page, category and tag.** Everything this
   plugin renders for that item is now shown where you edit it: search title, meta
   description, canonical, noindex/nofollow, structured data, language alternates,
   pagination links, injected image alt text, and whether the plugin is managing
   that item at all. Text fields are editable; machine-generated additions have 
   a “Remove it” checkbox. Anything you set by hand is marked as yours, and no automated
   write can overwrite it afterwards — enforced inside the plugin, so an older backend
   cannot bypass it.
 * **Tools  U&AI SEO** lists the site-wide behaviour: redirect rules (with a delete
   button per rule) and switches for image-alt injection, external-link `rel` attributes,
   HTTP security headers and the analytics snippet. A switch you turn off stays 
   off — no automated request can turn it back on.
 * **With Yoast SEO active, values are written into Yoast’s own fields** instead
   of being rendered over them, so they appear in Yoast’s sidebar where you already
   edit. A value you type in Yoast always wins: this plugin only fills a field you
   left empty, and it records what it wrote so it can update its own value later
   without ever touching yours.
 * Third-party JSON-LD is no longer deleted. Earlier versions removed any `ld+json`
   block they judged invalid — schema your own plugins emitted, removed at render
   time with no trace. Invalid schema is still reported as an issue; deciding what
   to do about it is yours.
 * Redirect rules now only fire on an address that genuinely 404s. If you republish
   a page at a path that had a redirect rule, visitors reach your page again instead
   of being sent away from it.
 * On a page where your theme or another SEO plugin already prints a meta description,
   this plugin’s own tag stands down rather than deleting theirs.
 * `robots` directives are merged rather than replaced: this plugin may add `noindex`,
   and can never remove or override one you set.
 * Image alt text is only ever added where alt is empty, and where an image is in
   the Media Library its own alt text is what renders — clear it there and nothing
   is injected.
 * Fixes: duplicate `<title>` tags are now actually de-duplicated (the old routine
   removed nothing and logged PHP notices); injected alt text can no longer produce
   malformed `<img>` markup; the forced content-type header uses the site’s configured
   charset instead of assuming UTF-8; text inside `<script>`, `<template>`, `<textarea
   >` and HTML comments is left alone by all content passes.

#### 0.14.0

 * Archive URLs resolve on sites whose permalink structure starts with a fixed segment.
   0.12.2 matched a taxonomy’s own rewrite slug, but WordPress prepends the permalink
   FRONT to it: a site using `/article/%postname%/` has the front `/article/`, its
   category taxonomy still reports its slug as plain `category`, and the real archive
   lives at `/article/category/<term>`. Matching the slug alone found nothing on
   exactly the sites the fix was written for — one production site had every archive
   come back unresolved even after upgrading. The front-prefixed form is now tried
   first, the bare slug second, and the resolved term’s own permalink still has 
   to equal the requested path before it is returned, so neither form can send a
   fix to the wrong archive.
 * Date archives (`/2026/`, `/2026/04/`, `/2026/04/15/`, with or without a permalink
   front) now report `date_archive_unsupported` instead of a bare unresolved. WordPress
   builds them from a template, so there is no post or term for this plugin to write
   to and no re-scan will produce one. Saying so stops them being retried run after
   run — and stops meta descriptions being generated for pages that can never receive
   them.

#### 0.13.0

 * The analytics snippet is now added with `wp_enqueue_script()` instead of being
   printed straight into `<head>`. WordPress had no record that the tag existed,
   so caching, script-optimisation and consent plugins could reorder or defer everything
   around it without accounting for it. It still loads early in `<head>` and still
   carries `data-site-id` on the tag, which the snippet reads as it boots.
 * Read permissions now match what is being read. Site-wide settings — robots rules,
   security headers, DOM normalisation, snippet config — and `/health` require `
   manage_options`, as the matching write routes always did; previously any user
   who could draft a post could read them, and `/health` also lists the site’s active
   SEO plugins. Reading a single post’s or term’s SEO metadata now requires `edit_post`/`
   edit_term` on that specific object instead of a blanket `edit_posts`, which had
   allowed walking post ids to read the SEO fields of other people’s drafts. **If
   the Application Password configured in U&AI belongs to a non-administrator, `/
   health` now returns 403 and the bridge will not be used** — it belonged to an
   administrator already for any site where writes worked.
 * The DOM normalisation output buffer is now explicitly closed at `shutdown` rather
   than left for PHP to flush at the end of the request, and on WordPress 6.9+ the
   plugin joins core’s own template output buffer instead of opening a second one.
   Two buffers opened by two components can be closed in the wrong order, which 
   puts one component’s output inside the other’s.
 * Added an “External services” section documenting exactly what this plugin connects
   to: the U&AI REST calls into your site, and — when you enable it — what the analytics
   snippet sends from your visitors’ browsers, field by field, with the consent 
   switch and the terms and privacy policy links. Nothing about the behaviour changed;
   it was simply never written down.
 * Removed a developer-only test checklist from the plugin package.
 * Passes WordPress’s own Plugin Check with no errors and no warnings, and is tested
   against WordPress 7.1.

#### 0.12.2

 * `POST /resolve-url` now resolves archive URLs for ANY registered public taxonomy,
   at any depth in the path. It previously only recognised `category` or `tag` sitting
   at the very first path segment, so a site that gives its custom post type a permalink
   base — `/article/category/blog` — or uses a custom taxonomy — `/article/case-
   study-category/investing` — had every archive URL come back unresolved. Those
   URLs then fell through to a post lookup, which cannot resolve an archive because
   an archive is not a post. On one production site this single gap accounted for
   77% of a remediation run: titles, meta descriptions, H1s and canonicals all computed,
   then discarded for want of a term id.
 * Matching is against each taxonomy’s real rewrite base, longest first, and the
   resolved term’s own permalink must equal the requested path before it is returned—
   so two taxonomies sharing a term slug (`/article/case-study-category/strategy`
   and `/article/portfolio-category/strategy`) resolve to their own terms and a 
   fix cannot land on the wrong archive.
 * Author archives now report `reason: author_archive_unsupported` instead of a 
   bare unresolved. There is no post or term behind one for our meta to attach to,
   and saying so keeps genuinely fixable URLs from being buried alongside them.

#### 0.12.1

 * `POST /resolve-url` now resolves URLs it previously refused. `url_to_postid()`
   matches against the site’s canonical home URL and rewrite rules, so it returned“
   not found” for URLs that are plainly this site’s: a `www.` host where WordPress
   is configured without it (or the reverse), an `http://` URL on an https site,
   a trailing slash that disagrees with the permalink structure, and custom post
   types whose permalink base does not round-trip. Each of those made an applicable
   SEO fix unresolvable — the fix was right, we just could not name the post to 
   write to.
 * The fallbacks stay exact rather than fuzzy: the host is only ever swapped for
   this site’s own www/non-www twin, so a foreign URL that happens to share a path
   still does not resolve; and the custom-post-type slug lookup is limited to public
   REST-enabled types and refuses when two posts share the slug, because then the
   path does not identify either of them.

#### 0.12.0

 * Installing the plugin now installs the U&AI analytics snippet too. Previously
   someone had to paste a script tag into the theme by hand, so most sites never
   did it and reported no visitor data at all. The platform pushes the site id to`
   POST /uandai-seo/v1/snippet-config` and the plugin renders the loader in `<head
   >` — no theme editing, and no chance of pasting the wrong site id.
 * The snippet is not rendered in wp-admin or for logged-in users, so staff traffic
   stays out of the client’s page-view counts. What the snippet may collect is still
   decided server-side and fetched at runtime, so a site cannot opt itself into 
   heavier collection.
 * `/health` reports `snippet_installed` and the `snippet_install` capability.

#### 0.11.3

 * Every write the bridge exposes is now safe to retry. Send an `Idempotency-Key`
   header and a replay of that key returns the STORED response instead of performing
   the write a second time — so a request that times out AFTER WordPress applied
   it no longer forces a choice between duplicating the change and skipping it. 
   Applied at the REST dispatch layer, so it covers every write route, including
   ones added later.
 * `/health` gains a `capabilities` array describing what the bridge can DO (as 
   opposed to `supports`, which lists the meta fields it can write), so a backend
   can detect idempotency support without matching on version numbers.
 * Keys are kept for 7 days and swept by a daily cron event; a request still in 
   flight answers 409 with `Retry-After`, and reusing a key with a different body
   is rejected rather than served the wrong result.

#### 0.11.2

 * A second copy of the plugin is now inert instead of fatal. WordPress installs
   a zip as a NEW plugin when its folder differs from the installed one, and both
   copies then load — the second inherited the first’s paths, never declared its
   own classes, and fataled the moment it called one. First copy to load now wins;
   any other returns immediately.
 * `/health` reports the plugin’s actual install path and any duplicate copies, 
   so this is diagnosable without shell access.

#### 0.11.1

 * Fixes a fatal error on activation. 0.11.0 inspected the site while registering—
   it called the conflict detector and pulled in wp-admin/includes/plugin.php at`
   plugins_loaded`, far earlier than any of that code had run before. Registration
   now adds its filters and inspects nothing; every check happens inside the callbacks,
   which run at wp_head with the full API available. Yoast detection no longer needs
   an admin include at all.

#### 0.11.0

 * Yoast passthrough now covers robots directives and JSON-LD as well as title, 
   description and canonical.
 * Schema is MERGED into Yoast’s @graph rather than emitted as a second graph: a
   type Yoast does not output is appended, a type it does output keeps Yoast’s node
   and only gains properties Yoast is missing. Yoast’s values always win, so its
   internal @id references stay intact.
 * The passthrough no longer registers when the bridge itself is rendering the head(
   an allow-listed SEO plugin), which would have produced our values twice.
 * `/health` advertises `yoast_passthrough_robots` and `yoast_passthrough_schema`
   separately, so the platform never routes a fix to a version that cannot render
   it.

#### 0.10.0

 * Coexists with Yoast SEO instead of standing down: when Yoast is active the bridge
   feeds its stored SEO title, meta description and canonical through Yoast’s own`
   wpseo_title` / `wpseo_metadesc` / `wpseo_canonical` filters. Yoast still renders
   the head — it renders your values. Nothing to disable, no duplicate tags, and
   deactivating this plugin restores Yoast’s own values exactly.
 * Heading fixes (H1/first H2) no longer stand down for a resident SEO plugin. Those
   are body content; no SEO plugin writes them.
 * `/health` advertises `yoast_passthrough` so the platform can route meta fixes
   to sites where Yoast owns the head.

#### 0.9.0

 * SEO title and meta description are writable through the core REST API (`meta`
   on `/wp/v2/posts`), so a published article carries its SEO fields in the create
   call.
 * `/health` advertises `core_rest_meta` plus the registered meta keys.
 * Sitemap, pagination links and JSON-LD output hardened per WordPress Plugin Check;
   redirects now use `wp_safe_redirect()` with an allow-list built from the configured
   rules.

#### 0.1.0

 * Initial release: REST namespace, head renderer for title/meta/canonical/robots/
   JSON-LD, conflict detection.

## Meta

 *  Version **0.15.0**
 *  Last updated **23 hours ago**
 *  Active installations **Fewer than 10**
 *  WordPress version ** 5.6 or higher **
 *  Tested up to **7.1**
 *  PHP version ** 7.4 or higher **
 * Tags
 * [canonical](https://wordpress.org/plugins/tags/canonical/)[meta description](https://wordpress.org/plugins/tags/meta-description/)
   [robots](https://wordpress.org/plugins/tags/robots/)[schema](https://wordpress.org/plugins/tags/schema/)
   [seo](https://wordpress.org/plugins/tags/seo/)
 *  [Advanced View](https://wordpress.org/plugins/uai-app-bridge/advanced/)

## Ratings

No reviews have been submitted yet.

[Your review](https://wordpress.org/support/plugin/uai-app-bridge/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/uai-app-bridge/reviews/)

## Contributors

 *   [ ybriki ](https://profiles.wordpress.org/ybriki/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/uai-app-bridge/)