I'm migrating a legacy blog from a home-grown CMS to Drupal 7 and I need to wrap my head around how I'm going to redirect URLS to a new aliased pattern.

The legacy site's URLs use query strings /?t=40&an=49187&anc=570&format=xml which then redirect to an aliased url like /blog/i-know-this-is-not-ideal. Most of the listing pages on the site use the query parameters to link to pages, though others use the aliased urls. The an parameter appears to refer to their version of a Node ID, which I'm able to parse and store in my migration.

The company who owns this platform was kind enough to sent me an Excel spreadsheet with aliases in one column and query strings in another — so I have something at least.

Maintaining SEO juice is critical for this client — and Google has parsed these query parameters in many cases.

Should I look towards writing a custom module for this, as the Redirect module appears not to provide support for query parameters? Or should I look into htaccess rewrite rules?

  • 1
    You can try using Rules, like on the answer here
    – No Sssweat
    Jun 6, 2017 at 21:43
  • 1
    @NoSssweat your comment does make a lot of sense, though to me this question reminds me about this answer also ... maybe this question is even a duplicate of it ... Jun 7, 2017 at 6:09
  • Performing low-level redirects like this is a sensitive task, you need to be very mindful of the overhead you're introducing to every single request made to your site. As such, using Rules, which is notoriously slow and bulky, should be avoided like the plague
    – Clive
    Jun 7, 2017 at 7:18

1 Answer 1


I think a small custom module will probably be the method with the least overhead. You could store the legacy ID in a field during the migration and access it from there if the appropriate query string is set.

Very rough examples...

Drupal 7:


function mymodule_init() {
    if (!empty($_GET['an'])) {
        $query = new \EntityFieldQuery();
        $result = $query->entityCondition('entity_type', 'node')
            ->fieldCondition('field_old_id', 'value', $_GET['an'])

        if (!empty($result['node'])) {
            $nids = array_keys($result['node']);
            $nid = reset($nids);
            drupal_goto(url("node/$nid"), [], 301);


Drupal 8:


    class: Drupal\mymodule\RedirectSubscriber
    arguments: ['@entity_type.manager', '@path.alias_manager']
      - { name: 'event_subscriber' }


namespace Drupal\mymodule;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Path\AliasManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class RedirectSubscriber implements EventSubscriberInterface

    protected $nodeStorage;

    protected $aliasManager;

    public function __construct(EntityTypeManagerInterface $entity_type_manager, AliasManagerInterface $alias_manager)
        $this->nodeStorage = $entity_type_manager->getStorage('node');
        $this->aliasManager = $alias_manager;

    public function Redirect(GetResponseEvent $event)
        $old_id = $event->getRequest()->get('an');

        if (!empty($old_id)) {
            $ids = $this->nodeStorage->getQuery()
                ->condition('field_old_id', intval($old_id))

            if (!empty($ids)) {
                $nid = reset($ids);
                $url = $this->aliasManager->getAliasByPath("/node/$nid");
                $event->setResponse(new RedirectResponse($url, 301));

    static function getSubscribedEvents()
        $events[KernelEvents::REQUEST][] = array('Redirect', 20);
        return $events;
  • 1
    You missed the Drupal 7 part, but don't delete your answer, useful for D8 devs.
    – No Sssweat
    Jun 6, 2017 at 22:41
  • 1
    @NoSssweat Ah yeah, I did miss that! I added a D7 example
    – Clive
    Jun 6, 2017 at 22:48
  • Thanks @Clive — this is what I was imagining. I think I may write a small module that references the export I have though since it's more explicitly mapped out for me. I really wish I had a way to create an alias when I import the data. Jun 7, 2017 at 14:24
  • Aliases are stored in the database so you do have that option, really - how hard it is to implement depends on what method you're using to import I guess. Keep in mind that won't catch direct requests with the legacy IDs in the URL, so the redirect will still be necessary
    – Clive
    Jun 7, 2017 at 14:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.