Skip to content
Drupal

Migrate Redirect Map 1.0: Drupal Redirects for Every Old Drupal 7 URL

· 10 min read · 2,104 words
Shaker Abady

Written by

Shaker Abady

Founder & CEO, Gspikes

I own and run Gspikes, the agency I founded in 2018. Before going all-in, I led SEO at Chain Reaction — one of the region's largest digital agencies — and shipped production code at Orange and Forbes. Everything published here comes from projects I've personally built, migrated, or ranked.

LinkedIn

Last updated · October 2026

A Drupal 7 migration moves your content, but it doesn’t move your addresses. Every old URL that a visitor has bookmarked, another site links to, or Google still has in its index needs an answer on the new site. Usually that answer is a 301 redirect to the page’s new address. Getting those Drupal redirects right is the part of a migration that quietly decides whether search traffic survives the launch.

We built Migrate Redirect Map to do that job, and version 1.0.0, its first stable release, came out on 30 October 2026. This post explains what it does, how it decides what to redirect, and where it belongs in a Drupal 7 to Drupal 10 or 11 migration.

Migrate Redirect Map in one minute

  • What it does: builds the 301 redirects from legacy Drupal 7 URLs to the migrated content, and lists the URLs that would return 404 after launch.
  • What it reads: the migrate map tables of your node, taxonomy term, user and media migrations, plus the Drupal 7 url_alias and redirect tables.
  • What you get: every legacy URL sorted into one of seven statuses, a CSV of the whole map, and redirects created in batches from the admin UI or Drush.
  • Requirements: Drupal 10.3+ or 11 and the Redirect module (1.9 or later). Install with composer require drupal/migrate_redirect_map.
  • Security: 1.0.0 is covered by Drupal’s security advisory policy.

Why old URLs break in a Drupal 7 migration

There are still a lot of these migrations to come. In our State of Drupal 7 census, W3Techs data put Drupal 7 at 29.7% of all Drupal websites in August 2026, which we estimate at around 200,000 live sites. Each of them will need its old URLs handled when it moves.

Core’s migrations get you part of the way. The d7_url_alias migration copies the old aliases across, which keeps old URLs working as long as nothing else has changed. The Redirect module’s own Drupal 7 migration copies the redirects the old site already had. Neither covers the cases that cause most of the 404s after launch:

  • The aliases change. New Pathauto patterns on the new site produce different URLs for the same content, so the old alias no longer exists.
  • The IDs change. If entities don’t keep their Drupal 7 IDs, a system path such as node/12 now means a different page, or nothing.
  • Translations fold together. d7_node_complete merges a translated node into its source node, so the translation’s own node ID never appears on the new site.
  • Old redirects point at old places. A Drupal 7 redirect to node/45 is only useful if it is re-pointed at wherever node 45 ended up.

None of these show up as errors during the migration. They show up weeks later as lost rankings and broken backlinks, which is why our website migration SEO guide treats the redirect map as a launch blocker rather than a clean-up task.

What Migrate Redirect Map does

After your content migrations have run, the module works through the old site’s URLs in order:

  1. It reads the migrate maps of the node, taxonomy term, user and media migrations. Both naming styles work: core IDs such as d7_node_complete:article and the upgrade_d7_* IDs written by the Migrate Drupal UI.
  2. It reads the legacy url_alias table, so every alias a visitor, a backlink or a search engine might still use is accounted for.
  3. If the Drupal 7 site used the Redirect module, it reads the legacy redirect table too, including query strings, fragments and status codes, and re-points those redirects at the migrated content. Disabled legacy redirects are skipped.
  4. It looks up each migrated entity’s current alias on the new site, in the right language.
  5. It checks every candidate against the new site and gives it a status.
  6. It gives you the whole map as a CSV, and when you are ready, creates the Redirect entities in batches. Each redirect is validated before it is saved, so a collision is logged and skipped instead of stopping the run.

The seven answers for every legacy URL

MIGRATE REDIRECT MAP 1.0Every old URL gets one of seven answersLegacy paths come from the Drupal 7 url_alias and redirect tables and the migrate maps.Each one is checked against the new site before anything is written.Statuses as implemented in migrate_redirect_map 1.0.0 (RedirectMapBuilder). Example paths are illustrative. gspikes.com — free to reuse with attributionDRUPAL 7 URLS/blog/2019/spring-launchnode/12/about-ustaxonomy/term/7/events?page=2/fr/actualites/rapportuser/login/files/brochure.pdf/news/old-press-pageCheckedagainstthe new siteBECOMES A 301redirectThe URL changedA 301 to the migrated entity’s new aliasexternalOld redirect to another websiteCreated only if you tick the optionNOTHING TO DOidenticalSame URL on the new siteNothing to doexistsA redirect is already thereLeft alone, enabled or disabledA PERSON DECIDESconflictThe path is live on the new siteSkipped, so a real page is never hiddenduplicateTwo entries claim one URLFirst one wins, the other is listedunresolvedNo migrated entity matchesWould 404: map it by hand

The statuses are the useful part, because they turn a migration’s URL problem into a list you can work through. Two of them create redirects, two need nothing, and three need a person to look at them.

  • redirect: the URL changed, so a 301 to the migrated entity’s new alias will be created.
  • external: a legacy redirect to an absolute http or https URL on another site. These are listed but only recreated when you ask, through a checkbox in the form or --include-external in Drush. Targets using javascript:, data:, mailto: or a protocol-relative //host are never created.
  • identical: the URL is the same on the new site. Nothing to do.
  • exists: a redirect for that URL already exists, enabled or disabled, so it is left alone. That also makes the module safe to run again.
  • conflict: the old URL is live on the new site. It may be an alias that now belongs to other content, an entity that reuses the legacy ID, or a real route such as user/login. The module skips these, because Redirect answers a request before Drupal’s routing does, and a redirect would hide that page.
  • duplicate: another legacy entry already claims the same URL and language with a different target. This usually happens when two Drupal 7 languages both become language-neutral on the new site. The first one wins and the second is reported.
  • unresolved: no migrated entity matches, so this URL would return 404. Views paths, custom menu callbacks and file paths land here. They are listed, never guessed.

That last rule is deliberate. A tool that guesses a target for every unmatched URL produces redirects nobody checked, and a wrong redirect is harder to spot than a 404. The unresolved list is a to-do list for a person who knows the site.

When to run it

WHEN TO RUN ITRun it after the aliases exist, before launchThe module reads what the migrations left behind, so it is the last step before go-live.Dry run first: nothing is written until you clear the box (or run Drush without –dry-run).Order of steps from the migrate_redirect_map README (Usage). gspikes.com — free to reuse with attributionMIGRATE REDIRECT MAP1Migrate contentd7_node_complete, terms,users and media2Build new aliasesPathauto, or importd7_url_alias3Dry runCounts per status anda CSV of every URL4Fix the listunresolved, conflict andduplicate rows first5Create redirectsIn batches from the UI,or drush mrm:build6Launch, then checkcurl -I a few old URLs:expect 301 to the aliasdry run again until the list is cleanToo early? Before step 2,redirects point at node/123, not the alias

Run it after the content migrations and after the new aliases exist, either because Pathauto has generated them or because you imported them with drush migrate:import d7_url_alias. If you run it earlier, the redirects point at system paths such as node/123. Those still work, but they aren’t what you want in the map.

In practice it is the last step before launch. Do a dry run, work through the unresolved, conflict and duplicate rows, run the dry run again, and create the redirects only when the list looks right. After launch, check a few old URLs with curl -I and expect a 301 to the new alias. Our Drupal 7 to Drupal 11 migration checklist shows where this sits among the other launch tasks.

Running it from the admin UI

The report page is at Configuration » Search and metadata » Migration redirect map (/admin/config/search/redirect/migrate-map). There is nothing to configure in advance and no settings are stored. Each run takes four options:

  • Legacy database key: the key of the Drupal 7 database in settings.php. It defaults to migrate, the key Migrate Drupal already uses.
  • Migrations: the migration IDs whose maps are read. Leave it empty to use every d7_node*, d7_taxonomy_term*, d7_user and d7_media* migration, including the upgrade_ variants.
  • Also recreate legacy redirects to external URLs: off by default.
  • Dry run: ticked by default. Nothing is written until you clear it and submit again.

The map is built in a batch, 200 paths per request, and redirects are created 100 per request, so the page doesn’t time out on large sites. The report shows the counts and puts the rows that need attention first. Download the full report (CSV) returns every row. The last report is kept for your account only until the next run.

The page needs its own permission, Build migration redirect maps. It is marked as restricted, because it reads a database chosen in settings.php and creates redirects in bulk, so give it to trusted administrators only.

Running it with Drush

For very large maps, Drush is faster. The command needs Drush 12.5 or 13:

drush migrate-redirect-map:build --dry-run --csv=/tmp/redirect-map.csv
drush migrate-redirect-map:build

Without --dry-run, the command shows the counts and asks before it creates anything. Add -y to skip the question in scripts. The other options are --source-key, --migrations, --include-external and --csv, and mrm:build works as a short alias.

Reading the CSV

The report has one row per legacy URL, with these columns: legacy_path, legacy_query, legacy_system_path, legacy_langcode, langcode, target, status, status_code and reason. It is meant to be opened in a spreadsheet and signed off before launch. Legacy aliases were typed by editors, so any cell that starts with =, +, -, @, a tab or a carriage return is prefixed with an apostrophe, and the spreadsheet won’t run it as a formula.

Status codes come from the legacy Redirect table where there is one, falling back to the Drupal 7 default, which is 301 unless it was changed. Codes that Redirect doesn’t support are replaced by 301, and the reason column says so.

What it doesn’t do

  • It maps nodes, taxonomy terms, users and media. Files, views and custom paths are reported, not mapped.
  • Drupal 7 aliases never carry query strings. Query strings on legacy redirects are kept on both the source and the target, and a query-specific redirect is created even when the bare path is live, because Redirect only matches that exact query.
  • If the old site had several aliases for one node, each becomes its own redirect to the new canonical alias.
  • It doesn’t touch the content or the old database. Until you create the redirects, everything it does is read-only.

Cleaning up the old database before you migrate makes the list shorter and easier to check. Our guide to cleaning a decade of Drupal database cruft covers that step.

Security coverage and the review

Before 1.0.0, the module went through drupal.org’s security advisory coverage application, where volunteer reviewers read the code by hand. Their review shaped the release. The conflict check, validation before saving, the opt-in for external targets, the spreadsheet-safe CSV, the restricted permission, the Drush confirmation and a kernel test covering every status all came out of it. Thanks to vishal.kadam, avpaderno, anil_bhutia and klausi for their time.

The application was approved on 25 October, and 1.0.0 is covered by the security advisory policy. If you find a security problem, report it privately through the Security Team, using the “Report a security vulnerability” link on the project page, not in the public issue queue. The 1.0.0 release notes list every change since the beta.

Try it, and tell us what breaks

composer require drupal/migrate_redirect_map
drush en migrate_redirect_map

Start with a dry run on a copy of your migrated site and read the unresolved rows. Bug reports and ideas are welcome in the issue queue. Reports from multilingual sites, very large sites and unusual Drupal 7 alias setups are the most useful right now.

Migrate Redirect Map is maintained by Gspikes. If you’re planning a Drupal 7 migration, our Drupal 7 end-of-life migration guide covers the options, and our Drupal migration team can take it on for you.

Field Notes

Get the next one by email

Research on Drupal, WordPress and search. Measured, not guessed. New pieces straight to your inbox.

Confirmed opt-in. One click to leave, any time.

Put senior Drupal engineers on it

Migrations, custom modules, integrations, and support with real SLAs — with a fixed-price quote from a senior engineer within 3 days.

Want this applied to your business?

A senior strategist reviews your situation and sends an honest plan within one business day.

Get a Free Quote