On this page
- The Golden Rule
- Moving a Single Page (Atomic)
- Moving Pages (Topic Restructure)
- The Right Order
- Setting Up Redirects
- After Restructure
- Merging Pages
- Manual Merges
- Deleting Pages
- Redirect Chains
- Wire Flattens Chains Automatically
- RULE-61: Redirect Chain Lint
- Prevention
- Multi-Language URL Changes
- Translating URL Slugs
- Monitoring After URL Changes
- Quick Reference
- Redirect Behavior Details
Every URL change is a risk. Google has indexed the old URL, backlinks point to it, and users may have bookmarked it. Wire handles redirects automatically for merges and differentiations, but topic restructures and manual moves require deliberate planning. This guide covers every scenario. For complete Wire documentation, see the Guides page.
The Golden Rule
Every old URL must resolve to the correct final destination in one hop. No chains (A to B to C), no dead ends (404), no soft redirects (everything pointing to the homepage).
Wire generates 301 redirects as both _redirects (Netlify/Cloudflare format) and HTML fallback pages with <meta http-equiv="refresh"> and <link rel="canonical">. All redirects live in .wire/redirects.yml.
Moving a Single Page (Atomic)
For a single rename or relocation, use wire.chief move. It is one command that performs all five side-effects of a move in the correct order:
python -m wire.chief move docs/services/brakes/index.md docs/services/brake-repair/index.md
python -m wire.chief migrate-gsc # rekey GSC history onto the new URL
What move does, atomically:
git mvthe file (preserves history; falls back to plain filesystem move if the site is not a git repo)- Writes the 301 entry to
.wire/redirects.ymlwithreason: chief move - Rewrites
](/old/)and](/old/#anchor)internal links acrossdocs/**/*.md - Prints the link-rewrite count and next-step hint
- Refuses to overwrite an existing target — run with
--forceonly when the target is a stub you want replaced
Run migrate-gsc right after so the GSC history keyed on the old URL is rekeyed onto the new URL — otherwise the next audit sees the moved page as a brand-new orphan.
Why this command exists. The manual flow (git mv, edit wire.yml, migrate-gsc, grep for links, verify) used to leave duplicate live pages when the user forgot a step. move is atomic — all side-effects or none.
For topic-wide restructures, keep reading — those still need the deliberate audit/deduplicate/migrate cycle below.
Moving Pages (Topic Restructure)
When you reorganize a flat site into topic directories, every page gets a new URL:
/alte-seite/ → /ki-technologie/alte-seite/
/andere-seite/ → /dokumente/andere-seite/
The Right Order
Deduplicate before restructuring. This is the most common mistake. Run the full audit and deduplicate cycle while pages are still flat:
python -m wire.chief data
python -m wire.chief audit
python -m wire.chief deduplicate
Why this order matters:
Fewer redirects. If you merge A into B first, then move B to its topic, you need one redirect (A to B's new location). If you move both first, then merge, you get a redirect chain: A's old URL to A's new URL to B's new URL. Wire flattens chains at build time, but avoiding them is cleaner.
Google Search Console (GSC) data stays valid. Wire's overlap detection uses GSC keyword data stored by (topic, slug) pairs. When you move a page to a new topic, Wire registers it as a new page with no search history. The old data becomes orphaned. Running deduplication first means Wire makes merge/differentiate decisions with full keyword data.
Cross-topic merges work but create cross-topic redirects. Wire allows merging pages across topics. The donor gets a redirect to the keeper's topic. But this means content moves between silos, which may not match your intended topic structure. Deduplicating first keeps the decision cleaner.
Setting Up Redirects
Add redirects to .wire/redirects.yml before moving files. Wire's add_redirect() function handles this, or you can edit the YAML directly:
- from: /alte-seite/
to: /ki-technologie/alte-seite/
reason: "topic restructure"
date: "2026-03-13"
For large restructures (500+ pages), generate the redirects programmatically. Every old URL needs exactly one entry pointing to the new location.
After moving files:
python -m wire.chief sanitize # Fix broken internal links
python -m wire.build # Verify build + generate redirects
After Restructure
Migrate your existing GSC data to the new URL structure, then re-register pages:
python -m wire.chief migrate-gsc # Rekey GSC data using .wire/redirects.yml
python -m wire.chief data # Re-register pages + fetch fresh data
migrate-gsc reads .wire/redirects.yml and updates Content rows in the GSC database to match the new topic/slug/path. All keyword and snapshot history is preserved. If the new page already has some data (from a recent fetch), the old data is merged in.
For new URLs where Google hasn't transferred signals yet, Wire's GSC fetch automatically tries the old URL (via redirects) as a fallback. This means the audit sees data immediately after a restructure, not after waiting 2-4 weeks for Google to re-index.
Merging Pages
Wire's deduplicate command handles merges automatically. When two pages cannibalize each other (overlap ratio >0.4, traffic skew >0.7), Wire:
- Selects the stronger page as keeper (higher composite score from impressions, position, clicks, keyword count)
- Absorbs the donor's unique content into the keeper
- Archives the donor (
index.mdbecomesindex.md.archived) - Creates a redirect from donor URL to keeper URL
- Registers the redirect in
.wire/redirects.yml
The merge guard prevents content loss: if the merged output is less than 80% of the keeper's original body, Wire writes to .preview only and does not archive the donor.
Manual Merges
If you know two pages should be merged but Wire's thresholds don't trigger it (not enough shared keywords yet, or pages are in different topics):
- Manually combine the content into the keeper page
- Archive the donor: rename
index.mdtoindex.md.archived - Add the redirect:
from wire.tools import add_redirect
add_redirect("/old-donor-path/", "/keeper-path/", reason="manual merge")
Or edit .wire/redirects.yml directly.
Deleting Pages
Wire does not have a delete command. Deletion is a manual decision with SEO consequences. Before deleting:
- Check GSC data. Does the page get any impressions? Even 10 impressions/month means Google values it. Consider merging instead.
- Check inbound links. Run
python -m wire.chief auditand look for pages that link to the one you want to delete. Update those links first. - Add a redirect. Point the deleted URL to the most relevant surviving page, not the homepage (that is a soft 404 in Google's eyes).
# .wire/redirects.yml
- from: /deleted-page/
to: /relevant-surviving-page/
reason: "page removed, content covered by surviving page"
date: "2026-03-13"
If no relevant page exists, let it 404. A clean 404 is better than a misleading redirect.
Redirect Chains
A redirect chain is when URL A redirects to URL B, which redirects to URL C. Chains happen when:
- A page is moved (restructure), then its new location is merged into another page
- A page is merged, then the keeper is later moved to a different topic
- Manual redirects are added without checking existing redirect targets
Wire Flattens Chains Automatically
At build time, generate_redirects() detects chains and flattens them. If .wire/redirects.yml contains:
- from: /a/
to: /b/
- from: /b/
to: /c/
Wire generates the output as:
/a/ /c/ 301
/b/ /c/ 301
The build log warns about every chain it flattens:
WARNING: Redirect chain flattened: /a/ → /b/ → /c/ (now /a/ → /c/)
RULE-61: Redirect Chain Lint
The build linter (RULE-61) also detects chains in the HTML output as a safety net. If a redirect HTML page points to another redirect HTML page, the linter flags it. This catches chains from manual HTML redirect pages that bypass .wire/redirects.yml.
Prevention
The best way to avoid chains is to follow the right order:
- Deduplicate (merge/differentiate) while pages are flat
- Restructure into topics
- Add redirects from old flat URLs to new topic URLs
If you must restructure first:
- Wire's chain flattening handles it at build time
- But the
.wire/redirects.ymlfile keeps the intermediate entries, making it harder to audit later
Multi-Language URL Changes
For multi-language sites, each language has its own URL space:
/de/alte-seite/ → /de/ki-technologie/alte-seite/
/en/old-page/ → /en/ai-technology/old-page/
Restructure each language independently. German topics do not need to match English topics. Add redirects per language. The hreflang system handles cross-language linking automatically if pages declare their alternates in frontmatter.
Translating URL Slugs
After migrate-lang or translate, pages in non-default languages keep the source language's slugs. German pages end up at /de/industries/insurance/ instead of /de/industries/versicherungen/. Google uses URL path tokens as a ranking signal, so German pages with English slugs lose ranking potential for German search queries.
Wire already has the tools to fix this. For each slug you want to translate:
Rename the directory:
mv docs/de/industries/insurance/ docs/de/industries/versicherungen/Add a redirect to
.wire/redirects.yml:- from: /de/industries/insurance/ to: /de/industries/versicherungen/ status: 301Run sanitize to rewrite all internal links pointing to the old slug:
python -m wire.chief sanitizeWire's sanitizer detects links that pass through known redirects and rewrites them to the final target automatically. No manual link editing needed.
Build to verify:
python -m wire.build
For bulk renames, repeat steps 1-2 for each page, then run sanitize once. The sanitizer processes all redirect-based link rewrites in a single pass.
Why not translate slugs automatically? Slug translation is a strategic SEO decision. Some brands keep English slugs intentionally (technical terms, brand consistency). Wire provides the tools but leaves the decision to you.
Monitoring After URL Changes
After any URL change, monitor these signals over 2-4 weeks:
- Build linter. RULE-33 (broken internal links) and RULE-61 (redirect chains) catch issues immediately at build time.
- GSC data. Run
python -m wire.chief dataweekly. Watch for the new URLs appearing in search results and the old URLs dropping off. - Audit. Run
python -m wire.chief auditto check for new orphan pages (pages with no inbound links after restructure) and new cannibalization pairs.
The transition period is 2-6 weeks depending on crawl frequency. During this time, audit results will show limited data for moved pages. This is expected. Do not make additional URL changes during this period; let Google settle.
Quick Reference
| Action | Command | Redirect? | Who creates it? |
|---|---|---|---|
| Merge (auto) | wire.chief deduplicate | Yes | Wire automatic |
| Merge (manual) | Edit files + add_redirect() | Yes | You |
| Move to topic | Move files + edit .wire/redirects.yml | Yes | You |
| Delete | Remove file + edit .wire/redirects.yml | Recommended | You |
| Rename slug | Move directory + edit .wire/redirects.yml + sanitize | Yes | You |
| Translate slugs | Rename dirs + redirects + sanitize (see above) | Yes | You |
| Change domain | Update site_url in wire.yml | Server-level | Your server config |
All redirects end up in .wire/redirects.yml and are generated as 301s at build time. Wire flattens chains automatically. The linter catches anything that slips through.
Redirect Behavior Details
- Deduplication: Both
.wire/redirects.ymlandwire.yml redirects:are merged. wire.yml wins on conflict. - GSC anchor stripping: GSC URLs with
#anchors are stripped to base path before redirect/page matching./path/#sectionmatches against/path/. comparesyntax:vendors/X vendors/Y(no "vs" between paths).
See the configuration guide for details on wire.yml settings and the audit walkthrough for monitoring URL changes in GSC data.