Coexistence with Other Link Plugins
Royal Links is designed to run alongside Pretty Links, ThirstyAffiliates, BetterLinks, or Simple URLs while you migrate. You do not need to deactivate the other plugin first. This page covers the coexistence notice you’ll see on Royal Links admin pages, how URL-prefix conflicts are detected and blocked, what happens when import slugs collide with existing Royal Links, and how to extend the built-in detection registry for plugins we don’t yet recognize.
Running Pretty Links (or ThirstyAffiliates / BetterLinks / Simple URLs) alongside Royal Links is a supported state, not a warning. The two coexist cleanly during your transition. Royal Links only handles redirect slugs you create in Royal Links; anything else falls through to whichever plugin owns that slug. Prefix collisions are detected and blocked at settings-save time. Slug collisions during import are logged with a one-click retry that appends a -1 suffix.
The migration-mode notice
When Royal Links detects one of the recognized link plugins active on your site, it renders a supportive notice at the top of every Royal Links admin page. The notice looks like this:
Royal Links sees N live redirects on this site. Import them into Royal Links, or keep both plugins running side-by-side during the transition — the two coexist cleanly. Continue migration →
The count reflects the live entries in the other plugin’s own database or custom post type. The Continue migration → button jumps straight to the Import/Export tab with the correct source pre-selected.
What the notice does not do
- It does not deactivate the other plugin. Deactivation is your call after you’ve verified the migration is complete.
- It does not modify any existing links in either plugin.
- It does not add any hooks to the other plugin’s redirect path. Both plugins keep serving their own slugs exactly as before.
Dismissing the notice
Click the × in the top-right corner of the notice to dismiss it. Dismissal is per-user and per-competitor:
- If you’re logged in as User A and you dismiss the Pretty Links notice, User B (another admin) still sees it.
- If you dismiss the notice for Pretty Links but ThirstyAffiliates is also active, the ThirstyAffiliates notice keeps rendering separately.
- If you deactivate the other plugin and later reactivate it, the notice returns even for a user who previously dismissed it — the situation changed, so the notice earns a fresh chance to be useful.
Recognized plugins
The built-in registry recognizes six competitor plugins out of the box. If yours isn’t here, jump to Extending the registry.
| Plugin | Plugin basename | Migration wired? |
|---|---|---|
| Pretty Links | pretty-link/pretty-link.php | Yes |
| Pretty Links Pro | pretty-links-pro/pretty-link-pro.php | Yes (uses the Pretty Links migrator) |
| ThirstyAffiliates | thirstyaffiliates/thirstyaffiliates.php | Yes |
| BetterLinks | betterlinks/better-links.php | Yes |
| BetterLinks Pro | betterlinks-pro/betterlinks-pro.php | Yes (uses the BetterLinks migrator) |
| Simple URLs | simple-urls/simple-urls.php | Notice only (no migrator yet) |
How to migrate
The safe pattern for migrating from another link plugin to Royal Links is to run both plugins in parallel while you verify each set of imported links, then deactivate the source plugin once you’re confident.
Install and activate Royal Links
Search for “Royal Links” in your WordPress Plugins screen, click Install, then Activate. The other plugin stays active.
Go to Import/Export
In wp-admin, navigate to Links → Import/Export under the Royal Links menu (or click Continue migration → on the notice at the top of any Royal Links admin page).
Migrate one source at a time
Under “Migrate from another plugin,” you’ll see a card for each recognized source with a link count. Click Migrate. The importer runs in batches of 500 by default (adjust with the royal_links_import_limit filter). If the source has more than 500 links, click Migrate again to continue.
Review the imported links
Under Links → All Links, verify that a sample of imports look right: target URL preserved, redirect type preserved, nofollow / sponsored flags preserved, categories mapped. If anything is off, the source plugin is still active and untouched — nothing was lost.
Test one or two migrated URLs
Open a Royal Links slug in a new tab. It should redirect to the destination. The corresponding entry in the source plugin still exists too — both slugs resolve to the same destination during the parallel period, so no reader-facing link goes dead while you migrate.
Deactivate the source plugin when ready
When you’re confident the Royal Links copy is complete and correct, deactivate the source plugin from Plugins → Installed Plugins. Delete the plugin only after you’ve confirmed all its rewrite rules have flushed (visit any Royal Links slug to prompt WP to rebuild rewrites).
Royal Links only handles slugs that exist as royal_link posts. Every other slug on your prefix falls through to the next template_redirect handler, which lets the source plugin keep serving its own URLs. You can migrate one link at a time and none of the older ones break in the interim.
URL prefix conflicts
Every link-management plugin owns a URL prefix on your site (Royal Links defaults to /go/). If two active plugins try to own the same prefix, their rewrite rules fight and one plugin’s links stop resolving. Royal Links blocks this at the Settings save moment, before the collision reaches the reader.
What triggers a block
When you save a new Royal Links prefix under Links → Settings, the plugin checks the incoming value against three things:
- An active competitor with the same runtime prefix. If Pretty Links is active and its current prefix is empty (root), and you try to set Royal Links to empty, that’s a real conflict. Save is blocked. You’ll see a red admin notice: “The prefix you chose collides with the URL prefix currently used by Pretty Links, which is active on this site. Save was blocked so both plugins can keep coexisting cleanly. Try
goinstead.” - An existing custom post type’s rewrite slug. If any other plugin has registered a public CPT with a rewrite slug that matches your incoming prefix, save is blocked. WordPress would silently overwrite one rewrite with the other otherwise.
- A known default prefix of an inactive plugin— this is a warning, not a block. If you set Royal Links to
recommends(ThirstyAffiliates’ default) while ThirstyAffiliates is not installed, the save proceeds but you’ll see a yellow heads-up: “Heads up: the prefix you chose matches the default URL prefix used by ThirstyAffiliates. That plugin is not currently active, so the change was saved — but if you install it later you will hit a collision.”
Fixing a block
Pick a different prefix. The block notice includes a suggestion (the requested prefix with -links appended, or go if you tried to save an empty prefix). Or pick anything that’s not the other plugin’s active prefix.
Bypassing the check
Two escape hatches exist for the rare case where the collision is intentional (e.g. you’re about to deactivate the other plugin in the same session and want to pre-configure the same prefix):
// wp-config.php — disables the check globally define( 'ROYAL_LINKS_SKIP_COMPAT_PREFIX_CHECK', true );
Or, for conditional bypass:
// In a mu-plugin or your theme's functions.php add_filter( 'royal_links_skip_prefix_collision_check', '__return_true' );
Both bypass paths disable the check completely until you remove them. If you keep the bypass in place and then install a plugin that shares your prefix later, Royal Links won’t catch it and one of the two plugins’ redirects will silently stop working.
Slug conflicts during import
When you migrate from another link plugin, each source link is inserted as a royal_link post with the source’s slug. If a Royal Link with that exact slug already exists, Royal Links skips the insert rather than clobbering the existing post — because the existing post may already have click counts, per-link overrides, or A/B test data attached that you don’t want to lose.
The completion notice
After a migration run, the Import/Export page shows a summary like:
15 links migrated from Pretty Links. 3 skipped due to slug conflicts. 1 skipped for another reason (missing URL, etc.).
Below that, if any slug conflicts occurred, a details table renders inline:
| Slug | Title | Destination URL |
|---|---|---|
amazon-product | Amazon Product | https://amazon.com/dp/… |
shareasale-offer | ShareASale Offer | https://shareasale.com/r/… |
direct-link | Direct Link | https://example.com/product |
The one-click retry
Under the table you’ll see a Retry all with -1, -2, … suffix button. Clicking it:
- Reads every conflict in the current run.
- For each, tries slug +
-1, then-2, up to-50, stopping at the first free slug. - Calls the standard link-create pipeline with that free slug and the full args from the original source row (target URL, redirect type, nofollow / sponsored flags, categories, etc.).
- Redirects back with a summary: “Retried N conflict(s) with a suffix; M could not be retried.”
The retry is non-destructive. The pre-existing Royal Link with the base slug is never modified. The new imported links come in as separate posts with the suffix.
Buffer lifetime
The conflict details persist for 1 hour after a migration run, per user. Running a new migration replaces the buffer with the new run’s conflicts. Clicking Retry clears the buffer immediately after processing (successful or not).
Slug conflicts usually mean you’ve already imported this source plugin’s links once before (and are re-running the migration by mistake), OR you created a Royal Link manually with the same slug the source plugin uses. In the first case, ignore the conflicts — the links are already in Royal Links. In the second case, the retry-with-suffix path lets you keep both versions and reconcile manually.
How coexistence works when a reader clicks a link
When a reader hits a URL under your Royal Links prefix (say /go/apple), WordPress dispatches the request to Royal Links first. Royal Links checks whether a royal_link post with slug apple exists.
- If it exists: Royal Links resolves the redirect and returns 301/302/307 with the destination URL. Normal path.
- If it doesn’t exist: Royal Links bails silently (returns without setting a 404), which lets subsequent
template_redirecthandlers — including Pretty Links, ThirstyAffiliates, BetterLinks, etc. — try to handle the request. If none of them own the slug either, WordPress’s normal 404 handling fires as it would on any other missing URL.
The upshot: during a partial migration, half your links can live in Royal Links and half in the source plugin, sharing the same prefix, without any reader-facing URL going dead. As you migrate each link, resolution just moves from one plugin to the other.
Restoring pre-2.0.10 behaviour
If you want the pre-2.0.10 behaviour (force a 404 immediately on a Royal Links slug miss), a filter is available:
// Force 404 on cache miss instead of falling through add_filter( 'royal_links_missing_slug_fallthrough', '__return_false' );
Only useful if you’ve deliberately deactivated every other link plugin and want stricter 404 semantics.
Extending the detection registry
The built-in registry recognizes six plugins. If you’re running a link-management plugin the registry doesn’t know about (a private in-house tool, a niche third-party plugin, an older abandoned plugin), you can add it via a filter. Once added, that plugin will be included in prefix-collision detection and can trigger a migration notice if you also wire up a migrator on the WordPress side (out of scope for this filter — the filter only handles detection).
The royal_links_compat_plugins filter
add_filter( 'royal_links_compat_plugins', function ( $plugins ) {
$plugins['rebrandly-toolbar'] = array(
'name' => 'Rebrandly Toolbar',
'basename' => 'rebrandly-toolbar/rebrandly.php',
'migration_key' => null, // no migrator wired
'default_prefix' => '', // Rebrandly is edge-side, no local prefix
'prefix_option' => null, // no WP option to read the runtime prefix from
);
return $plugins;
} );Registry entry fields:
- Array key (e.g.
rebrandly-toolbar) — a canonical slug for the plugin. Used for dismissal storage and as the internal identifier. name— display name shown in the migration notice.basename— the plugin file basename WordPress’sis_plugin_active()expects. Usuallyfolder-name/main-file.php.migration_key— matches one of the built-in migrator keys (prettylinks,thirstyaffiliates,betterlinks) if you want the CTA to trigger a migration, ornullfor notice-only.default_prefix— the plugin’s known default URL prefix. Used for the “you set a prefix that matches an inactive plugin’s default” warning. Empty string means the plugin defaults to root.prefix_option— the WP option name where the plugin stores its current runtime prefix, ornullif unknown. Used for the “you set a prefix that matches an active plugin’s current prefix” block.
The filter runs once per admin page load. Once added, your custom entry behaves identically to a built-in entry: detection notice, per-user dismissal, prefix collision detection, and (if you wired a matching migration_key) the migration CTA.
Filters and constants reference
| Name | Type | What it does |
|---|---|---|
royal_links_compat_plugins |
Filter | Add, modify, or remove entries in the detection registry. Runs once per admin page load. |
royal_links_skip_prefix_collision_check |
Filter | Return true from this filter to bypass the prefix-collision check on the next settings save. Runs inside the pre_update_option_royal_links_link_prefix filter chain. |
ROYAL_LINKS_SKIP_COMPAT_PREFIX_CHECK |
Constant | Define as true in wp-config.php to bypass the prefix-collision check globally. |
royal_links_missing_slug_fallthrough |
Filter | Default true. When Royal Links receives a request for a slug that doesn’t exist, this filter decides whether to bail silently (so other template_redirect handlers can try) or force a 404 immediately. Return false for pre-2.0.10 strict-404 behavior. |
royal_links_import_limit |
Filter | Default 500. Maximum number of links to import per Migrate button click. Raise or lower to tune batch size for very large source datastores. |