Add cache context to altered links I am using hook_link_alter(&amp;$variables) to alter links depending on the user's role. I'm doing this for links in the main menu as well as for custom links shown on entities. This works good except caching.
If I change a user's role still the previous behaviour of the altered links is shown. If I clear all caches, it works fine again.

I thought of adding cache context user.roles. But hook_link_alter seems not to support to add caching meta data to $variables see https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Menu%21menu.api.php/function/hook_link_alter/8.5.x. (Or I'm doing it wrong.) 

What can I do to make caching of my altered links aware of the user's roles?

My code looks like in hook_link_alter(&amp;$variables)

$roles = \Drupal::currentUser()-&gt;getRoles();

if(!in_array('member_gold', $roles)) {

  $url = $variables['url'];

  if (($url-&gt;isRouted() &amp;&amp; $url-&gt;getRouteName() === 'view.my_content.page') OR ... ) {

    $url = Url::fromRoute('myModule.ajax_callback', ['op' =&gt; 'do_action']);
    $token = \Drupal::service('csrf_token')-&gt;get($url-&gt;getInternalPath());

    $variables['url'] = $url;
    $variables['options']['query']['token'] = $token;
    $variables['options']['attributes']['class'][] = 'use-ajax';

  }

}


Update:

The links that I alter are in a) the main menu, b) in pseudo fields and c) by other modules. I succeeded adding some cache context to a) and b) and skipped c). It seems to work, but I don't know why, it was more or less trial and error:

For the menu links:
    function mymodule_preprocess_menu(&amp;$variables) {

if(isset($variables['menu_name']) &amp;&amp; $variables['menu_name'] == 'main') {

  $variables['#cache']['contexts'][] = 'user.roles';
  $renderer = \Drupal::service('renderer');
  $renderer-&gt;addCacheableDependency($variables, \Drupal\user\Entity\User::load(\Drupal::currentUser()-&gt;id()));
}


In my pseudo fields:

$build['action_my_action'] = [
    ...
    '#cache' =&gt; [
      'contexts' =&gt; ['user.roles'],
    ],
  ];

  $renderer = \Drupal::service('renderer');
  $renderer-&gt;addCacheableDependency($build, \Drupal\user\Entity\User::load(\Drupal::currentUser()-&gt;id()));


I don't understand why this works. I initially only tried adding the cache context, but this did not work. Only if I add the dependency to the current user. However I don't know whether this is correct, as I don't want to vary by user but by role.

Would be great if someone can explain me if this is right and why it works?
Since you can't have the role sensitivity reliably in the links themselves, I'd tackle this differently by using an intermediary routing function.

So if you have a link you want to point to mypath/role_1, mypath/role_2 etc, you might point it to role_route/somekey defined in mymodule.routing.yml like so:

mymodule.role_route:
 path: 'role_route/{key}'
 defaults: 
  _controller: '\Drupal\mymodule\Controller\RoleRouteController::route' 
 requirements: 
   _permission: 'access role paths' 


The route function of the controller might be something like:

function route($key) {
  $current_user = \Drupal::currentUser();
  $roles = $current_user-&gt;getRoles();
  // logic to process roles and key to get wanted route
  ...
  $role_route_name = ... ;
  return new RedirectResponse( Url::fromRoute($role_route_name);
}