User redirection is often required on a website, and Drupal-based websites are no different. For instance, after successful authentication, users are taken to their account profile page.

In Drupal 7, drupal_goto() is the API function for this. It accepts an optional internal $path argument to redirect to. If none is provided, it redirects to the same page. This was later discouraged and $path should always be provided. Two things to note. Firstly, external destinations are not supported. Secondly, drupal_alter allows a developer to have the final say through an implementation of hook_drupal_goto_alter().

Like a good number of well-known API functions, drupal_goto() has been removed in Drupal 8 due to the changes in architecture. How do we do redirection now?

Creating a redirection

Drupal 8 uses a number of components from Symfony. Redirection is handled in the HttpFoundation Component by the Symfony\Component\HttpFoundation\RedirectResponse class.

When you want to redirect a user, create and return a new RedirectResponse object with the following constructor arguments: $url, $status (optional), and $headers (optional).

URL

The URL to redirect to should be a string with the scheme or protocol, path, fragment, query string, or any other acceptable part of a Universal Resource Identifier (URI).

This may be from one of the following ways:

  • same page

    • If you have access to the Request object ($request) and you want to redirect to the same page, you can get the URL like this: \Drupal\Core\Url::createFromRequest($request);
  • homepage

    • The static service container has a url() method which generates a URL from the name of a route and other parameters. In Drupal, <front> is understood as the homepage and this method also accepts it as the route name. This will give you a well-structured URL to the homepage - \Drupal::url('<front>', [], ['absolute' => TRUE]);

    • Another option is to have $url as an empty string. RedirectResponse understands that as the homepage too.

  • route name

    • As pointed out earlier, if you know the name of the route (e.g. user.login) you can generate the URL in the following two ways:
      • $url = \Drupal\Core\Url::fromRoute('user.login')->setAbsolute()->toString();
      • $url = \Drupal::url('user.login', [], ['absolute' => TRUE]);
  • URL string

    • Either an absolute (http://www.example.com/) or relative URL (/the-page) is acceptable.

Status

This is optional and the default is 302 which means "Found". RFC 2616 has this to say about this status:

The requested resource resides temporarily under a different URI.
Since the redirection might be altered on occasion, the client SHOULD
continue to use the Request-URI for future requests.

See vendor/symfony/http-foundation/Response.php or List of HTTP status codes for further details on standard status codes.

Headers

Response headers may also be provided as an array. For example, when a request is received and it is detected that Drupal has not been installed yet, we are redirected to the installer. This is temporary so there is no need to cache it:

<?php

$headers = ['Cache-Control' => 'no-cache'];
$response = new RedirectResponse($request->getBasePath() . '/core/install.php', 302, $headers);

Usage

We have the following examples:


return new RedirectResponse(\Drupal::url('locale.translate_status', [], ['absolute' => TRUE]));
	
return new RedirectResponse(\Drupal::url('<front>', [], ['absolute' => TRUE]));
	
return new RedirectResponse(Url::fromRoute('system.modules_uninstall')->setAbsolute()->toString());

If you know that the destination is an external URL then you are encouraged to use TrustedRedirectResponse instead:


return new RedirectResponse(\Drupal::url('<front>', [], ['absolute' => TRUE]));
	return new TrustedRedirectResponse('http://www.bbc.com/');

There is another way to do redirection if you know the route name and you have extended ControllerBase class. You can call the redirect() method from your class:

return $this->redirect('user.login');

Even when your class does not have to extend the ControllerBase class, you may still do something similar, because the redirect() method is from the UrlGeneratorTrait which you can also use in your classes. For example:

<?php
	
use Drupal\Core\Routing\UrlGeneratorTrait;
	
class MyClass {
  use UrlGeneratorTrait;
		
  public function doSomething() {
    // ....
    return $this->redirect('user.login');
  }
}	

As the class name suggests, your route name will first be converted to a URL, and then a RedirectResponse is created with the generated URL. However, this trait has been deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. So, it is always advisable to generate a URL with \Drupal\Core\Url and do the redirection yourself.

Altering a redirection

Inside Drupal 8 Events introduced the use of the EventDispatcher component in Drupal 8. This is an example of its application in a contrived scenario:

  1. Subscribe to the KernelEvents::RESPONSE event
  2. Check if the response matches a specific RedirectResponse to http://www.example.com/user/login
  3. Change the target URL to http://www.example.com/user/new-login

Here is the EventSubscriber class:

<?php

	// src/custom_redirection/EventSubscriber/CustomRedirectionSubscriber.php
	      
	namespace Drupal\custom_redirection\EventSubscriber;
	
	use Symfony\Component\EventDispatcher\EventSubscriberInterface;
	use Symfony\Component\HttpFoundation\RedirectResponse;
	use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
	use Symfony\Component\HttpKernel\KernelEvents;
	
	class CustomRedirectionSubscriber implements EventSubscriberInterface {
	
	  public function alterRedirection(FilterResponseEvent $event) {
	    $response = $event->getResponse();
	    if ($response instanceOf RedirectResponse && $response->getTargetUrl() == 'http://www.example.com/user/login') {
	       $response->setTargetUrl('http://www.example.com/user/new-login');
	    } 
	  }
	
	  static function getSubscribedEvents() {
	    $events[KernelEvents::RESPONSE][] = ['alterRedirection'];
	    return $events;
	  }
	}

For Drupal to know about it, configure a service for the class above.

services:
  custom_redirection.subscriber:
    class: Drupal\ custom_redirection\EventSubscriber\ CustomRedirectionSubscriber
    tags:
      - { name: event_subscriber }

This requires more coding than just implementing hook_drupal_goto_alter(), but it is part of the new direction Drupal 8 has followed.