I am getting the following when trying to redirect to an external site from my controller function defined in my routing file:

return new TrustedRedirectResponse($my_ext_url);

LogicException: The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\Core\Routing\TrustedRedirectResponse. in Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext() (line 159 of core\lib\Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber.php).

This is clearly related to cache metadata - but how do i switch this off for a trusted redirect?

Edit: There is currently no constructor on the controller and therefore no injection going on. Routing looks like:

  path: 'test/{order_id}'
    _controller: 'Drupal\my_module\Controller\MyController::handle'
    _title: 'Test Tite'
    _permission: 'access content'
  • You need to share your code of that controller. See also drupal.org/node/2638686, you're welcome to help me convince Wim that we need change that :)
    – Berdir
    Commented Jan 17, 2016 at 20:16
  • appreciate post back. it will take me awhile to pick the bones out of that thread, but will take a look through. After seeing this drupal.org/node/2599416 am going try injecting in a helper with @url_generator.non_bubbling although without understanding at a lower level am picking a straws at mo. Also added a little more info on my controller - its literally bare bones, no construct, no injection at mo just hitting handle from routing.
    – Mathew
    Commented Jan 17, 2016 at 21:45
  • The route definition and whether you use injection or not is not really relevant. What's relevant the code in your actual method, what services/methods call and how you handle what you get back. I'll add a quick generic response. If that's not enough to resolve this, then I can be more specific if you share your code.
    – Berdir
    Commented Jan 17, 2016 at 22:07

3 Answers 3


You mentioned the URL generator, which is one of the two ways this happens.

In short, you need to do something like my patch in this issue: https://www.drupal.org/node/2630808

Pass TRUE to toString(), then you get a different object back that you need to add as a cacheable dependency. You can do that with such GeneratedUrl objects, entity and config objects and you should do that whenever your response depends on them.

  • 3
    My method is using services and config to generate a payment redirect for paypal adaptive pay. I went through line by line commenting out each to workout what was causing this error. I am generating route based, absolute urls to pass to paypal as part of the pay request for success and cancel. These were my issue, my helper function for these was doing : return $url->toString(); it is now $url->toString(TRUE)->getGeneratedUrl();. Error gone with no extra dependencies, nothing todo with redirect url. I do not understand what the difference between these two lines are and why the problem?
    – Mathew
    Commented Jan 20, 2016 at 11:32
  • 1
    It's because Drupal can no longer detect that you're doing something wrong and then doesn't prevent you from doing so.
    – Berdir
    Commented Jan 20, 2016 at 22:18
  • Thanks @Berdir that solved my problem ! Isn't it supposed to be a bug as related here ? drupal.stackexchange.com/questions/196520/…
    – pbonnefoi
    Commented Sep 12, 2016 at 15:13
  • Yes, it is a bug. But the difference is that here, it is your own code that has the bug. And for REST, you're just using the code like you would when clicking around in the UI and there's nothing that you can do from the outside to fix it. So the bug is in the module that provides that route (just like in this case).
    – Berdir
    Commented Sep 12, 2016 at 15:53
  • 3
    To make Matthew's comment abundantly clear, Url::toString() seems to count as a render apparently, so if you are using this in your controller anywhere and do not sent TRUE to gather the cacheable meta data you will hit this trap. Fixing my TrustedRedirectResponse url did not do the trick for me because I also had another url generation earlier in the controller. I had to adapt both. Commented Jul 16, 2017 at 15:05

Many people may tell you this is what NOT to do, but for exotic and incomprehensible reasons, TrustedRedirectResponse can cause your controller to throw the error about metadata leaking "somewhere". I checked all around my desk and on the floor, and I couldn't find any metadata :) This can happen even if you disable caching for your route in every conceivable way.

So I thought well I'll create my trusted response but wrapping it in RenderContext bubble wrap. No dice. Maybe my situation is exotic, and the controller I'm working in does use code that's not in my control.

So if you get sick of Drupal telling you you can't do something, or want to skip hours of debugging leaky cache metadata whatever errors. Just make your own dang response..Symfony is under the hood and it's much less bossy. I added this method to my controller, and return the response, and boo-yeah, I can redirect to external sites. I just looked into the setTargetUrl() method of the RedirectResponse object.

I know that I only need to redirect to a small, finite number of external urls, so I can sanitize and white list the url input, so I only run this code to build a redirect for urls I trust.

private function createExternalRedirectResponse($url) {
    $response = new Response('', 302, []);
    $response->targetUrl = $url;
    $response->setContent(sprintf('<!DOCTYPE html>
        <meta charset="UTF-8" />
        <meta http-equiv="refresh" content="0;url=%1$s" />

        <title>Redirecting to %1$s</title>
        Redirecting to <a href="%1$s">%1$s</a>.
</html>', htmlspecialchars($url, ENT_QUOTES, 'UTF-8')));
    $response->headers->set('Location', $url);
    return $response;

Since Drupal's TrustedRedirectResponse object extends CacheableSecuredRedirectResponse, I believe that is the source of the issue. This object is adding cache-related metadata to the response, which is what triggers the issue in this case.

I have a controller with actions that ONLY redirect - nothing is ever rendered. The simple solution I found is to just use Symfony's RedirectResponse directly and not use Drupal's TrustedRedirectResponse and then no cache metadata will be involved.

use Symfony\Component\HttpFoundation\RedirectResponse;

Hope this helps.

  • 2
    This results in the error: "Redirects to external URLs are not allowed by default, use \Drupal\Core\Routing\TrustedRedirectResponse for it." Commented Apr 28, 2023 at 17:18

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.