Redirect module with 'Force clean and canonical URLs' actived. ERR_TOO_MANY_REDIRECTS accessing an uppercase url.

Created on 7 February 2022, over 2 years ago
Updated 18 April 2024, 2 months ago

Documentation location/URL

Problem/Motivation

If in the redirection module we activate the 'Force clean and canonical URLs' configuration, when we go to a url written in uppercase (the canonical url being in lowercase) we get an error of type 'ERR_TOO_MANY_REDIRECTS'

This happens because of the following:
In the database, the page cache is being saved with the cid identifier of the url in uppercase. So when we try to access the url in lower case, Drupal performs a query to the cache_page table looking for the cid that contains the url (in lower case), but the query is not case sensitive and returns the line it saved with the cid in capital letters.
It then tries to get the canonical url using the returned cid as an array key, but since the query shouldn't have returned anything because it shouldn't match the lowercase url with the uppercase url, it fails to get the array value.

This all happens in the getMultiple() function of the Drupal\Core\Cache\DatabaseBackend class.

Proposed resolution

I have found 3 possible solutions:

1- Call service 'page_cache_kill_switch' in Drupal\redirect\EventSubscriber\RouteNormalizerRequestSubscriber class to not save the url in the cache_page table.

if ($redirect_uri != $original_uri) {
$response = new TrustedRedirectResponse($redirect_uri, $this->config->get('default_status_code'));
$response->headers->set('X-Drupal-Route-Normalizer', 1);
//\Drupal::service('page_cache_kill_switch')->trigger();
$event->setResponse($response);
}

2- Use a query with case sensitive in the getMultiple() function of the Drupal\Core\Cache\DatabaseBackend class

3- Check that the key obtained from the query exists in the cid_mapping collection in the foreach of the items obtained from the query.

getMultiple() function of the Drupal\Core\Cache\DatabaseBackend class

foreach ($result as $item) {
// Map the cache ID back to the original.
if(!isset($cid_mapping[$item->cid])){
continue;
}
$item->cid = $cid_mapping[$item->cid];
$item = $this->prepareItem($item, $allow_invalid);
if ($item) {
$cache[$item->cid] = $item;
}
}

Remaining tasks

🐛 Bug report
Status

Closed: works as designed

Version

1.0

Component

Code

Created by

🇪🇸Spain mjpastor

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • 🇦🇺Australia sonnykt Melbourne, Australia

    @mjpastor You may want to check the structure of the cache_page table especially the cid column:

    $ drush sqlc
    > show create table cache_page;
    

    The cid column should be like this (on MariaDB):

    `cid` varchar(255) CHARACTER SET ascii COLLATE ascii_bin NOT NULL DEFAULT '' COMMENT 'Primary Key: Unique cache ID.',
    

    If your cid column looks like the below, you can drop your cache_* tables then drush cr will correctly recreate them and the issue will be gone:

      `cid` varchar(255) NOT NULL DEFAULT '' COMMENT 'Primary Key: Unique cache ID.',
    
  • Issue was unassigned.
  • 🇮🇹Italy apaderno Brescia, 🇮🇹
  • Status changed to Closed: works as designed 2 months ago
  • 🇹🇹Trinidad and Tobago xamount

    I can confirm that @sonnykt 's solution has resolved this issue for me.

    In our case, the cache tables were missing COLLATE ascii_bin. Without this, there is case insensitivity which resulted in a redirect loop as described in the ticket description.

    As using the standard Drupal cache table definitions solves this issue I am going to mark this as "Closed works as designed".

Production build 0.69.0 2024