Mart has become a complex platform since its humble beginnings. It today handles multi-domain installations enabling multiple diverse sites within a single Drupal site based on domain hosts. To enable multi-domain installations, Mart automatically issues redirects based on a set of rules to send users to correct site / domain for a specific page / node etc.

In a recent instance, we were trying to troubleshoot a scenario where the home page of a domain was being redirected to a specific node, i.e. https://example.com was being redirected to https://example.com/node/96. This was unexpected and the redirect should not have happened.

We checked and confirmed the redirect wasn’t coming from Mart or any of its sub-modules. Now the issue was figuring out who was issuing this redirect in the entire Drupal infrastructure we had. It could have been anything, from Drupal core or the modules shipped with core, the limited set of contrib modules we were using (namely token, pathauto, redirect, metatag and a handful more).

“Venerable” Drupal 7 had drupal_goto for redirecting providing a very convenient hook hook_drupal_goto_alter. The best thing about this hook was it was invoked from within the drupal_goto call, which meant you could very easily check / log the stack trace if needed from within your hook_drupal_goto_alter implementation making it a breeze to know who actually issued the redirect.

This change record however mentions using an event listener on kernal.response for altering a redirect:
https://www.drupal.org/node/2023537

The problem with this approach is its too late to know who issued the redirect as the stack trace of the time when the redirect was issued is lost. Yes, you can still alter the redirect; just can’t know who issued it in the first place.

After going back and forth, I ultimately opened up the RedirectResponse class from Symfony. It resides here inside your Drupal 8 / 9 installation:
~/vendor/symfony/http-foundation/RedirectResponse.php

And added the following inside the constructor:

var_dump(debug_stacktrace());

As ugly as it may seem, it got the job done. I immediately figured the redirect was coming from redirect module’s RouteNormalizerRequestSubscriber::onKernelRequestRedirect. And it was because of a caching issue. One of our modules had updated a domain related configuration and did not clear relevant cached data from within Drupal causing a redirect.

Like I said, ugly but functional. Do not try it on production, or accidently leave the code behind. Can mess up your entire Drupal site.

Meanwhile if any of you have any alternate approaches to know the stack trace at the time a redirect was issued in Drupal 8 / 9, I will be all ears. Creating a sub-class for Symfony’s RedirectResponse class and logging it in the constructor there and then injecting your sub-class would be an overkill imho for this simple debugging scenario. Plus you won’t be able to troubleshoot if some code issues a TrustedRedirectResponse (or SecuredRedirectResponse or a CacheableSecuredRedirectResponse etc) as the same extend RedirectResponse and your class won’t be involved for them. For unawares, TrustedRedirectResponse is used to redirect to external urls in Drupal.