When users log in, I want to redirect them to another page, when they have a certain role. In Drupal 7, I'd use drupal_goto() in hook_user_login() to do the redirect, and hook_module_implements_alter() to make sure that my module is last in the list, so that everything else gets a chance to run.

In Drupal 8, it seems that performing a redirect outside of a controller is not a supported use case.

On that page, gngn has the same problem, and manually generates a RedirectResponse and sends it.

The problem is that, as I see it, send() doesn't terminate the request and the page will have two HTML documents: a 'redirecting to...' page, followed by whatever page would have been there in the first place. Which could be a security issue.

johnvb notes this, and inserts a call to exit() after send().

Presumably something useful is being done in the $kernel->terminate() call in index.php, which is being bypassed by calling exit().

In Drupal 7, drupal_goto() called drupal_exit() to handle this problem.

drupal_goto() was removed says that modules needing to interrupt the request flow should throw an HTTP exception, but there are no 'redirect' exceptions.

As the user login also does a redirect, another approach would be to alter the redirection URL, as suggested on the drupal_goto() change record link above. However, that approach will result in my method being called on every single request, which for what I'm doing seems insanely wasteful.

So, how should I perform a redirect from hook_form_alter() in Drupal 8?

3 Answers 3

use Drupal\Core\Form\FormStateInterface;

 * Implements hook_form_FORM_ID_alter().
function [MODULENAME]_form_user_login_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // Alter login form and add own custom submit handler.
  $form['#submit'][] = '_[MODULENAME]_user_login_form_submit';

  // Not all forms pay attention to $form['#submit'][]. For these
  // try adding your submit hook to the #submit for the specific
  // button.  e.g.,
  $form['actions']['submit']['#submit'][] = '_[MODULENAME]_user_login_form_submit';

 * Custom submit handler for login form.
function _[MODULENAME]_user_login_form_submit($form, FormStateInterface $form_state) {
  // Set redirect to login form.

You have several ways to redirect user after login. As in the previous answers you can do it by implementing hook_form_FORM_ID_alter() and adding additional submit function, but you can still use hook_user_login function too:

To redirect to some route with parameters generating some url (using Symfony\Component\Validator\Constraints\Url class reference):

$form_state->setRedirectUrl(new Url('route_name', ['route' => 'parameters'])); 

To redirect to some route:

$form_state->setRedirect('route_name', ['route' => 'parameters']);

Also you can use Symfony\Component\HttpFoundation\RedirectResponse, for example if you want to redirect a user to some custom user page:

function your_module_user_login($account) {
    global $base_url;
    $redirect = new RedirectResponse($base_url .'/your-url/'.$account->uid->value);

  • Where would I get $form_state from if I'm using hook_user_login? Using RedirectResponse like this results in two responses being sent (the redirect masks it, but it's not good security practice)
    – Claw
    Commented Dec 13, 2016 at 22:23
  • Yes, i agree, RedirectResponse is not a good example in this context. It can be used in other redirect purposes not in form API.
    – Andrew Nim
    Commented Dec 14, 2016 at 10:02
  • hook_user_login() has never been though to be used to redirect users with drupal_goto() or RedirectResponse.
    – apaderno
    Commented Nov 3, 2019 at 16:06

The correct way to set a redirection after a form is submitted, in Drupal 8, is following these steps.

The alternative would be using the redirect.destination service. In this case, also with Drupal 8 it would be possible to use hook_user_login().

function mymodule_user_login(UserInterface $account) {

The documentation for RedirectDestination::set() says, though:

This method should be used really rarely, for example views uses it, in order to override all destination calls in all of its rendering.

As side note, the correct method to redirect users after they log in, in Drupal 7, is using code similar to the following one.

function mymodule_user_login(&$edit, $account) {
  $edit['redirect'] = $path_to_redirect_users_to;

It works because the &$edit parameter the hook receives is the $form_state parameter passed to the login form submission handler.

function user_login_submit($form, &$form_state) {
  global $user;
  $user = user_load($form_state['uid']);
  $form_state['redirect'] = 'user/' . $user->uid;

function user_login_finalize(&$edit = array()) {
  global $user;
  watchdog('user', 'Session opened for %name.', array(
    '%name' => $user->name,

  // Update the user table timestamp noting user has logged in.
  // This is also used to invalidate one-time login links.
  $user->login = REQUEST_TIME;
    'login' => $user->login,
    ->condition('uid', $user->uid)

  // Regenerate the session ID to prevent against session fixation attacks.
  // This is called before hook_user in case one of those functions fails
  // or incorrectly does a redirect which would leave the old session in place.
  user_module_invoke('login', $edit, $user);

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.