diff --git a/typo3/sysext/backend/Classes/Backend/ToolbarItems/ShortcutToolbarItem.php b/typo3/sysext/backend/Classes/Backend/ToolbarItems/ShortcutToolbarItem.php
index c7f8b60062cf3280e184d8ab9b307fbbb69bad61..8a1d390574061d8341568f131d9c0c2ca92ca896 100644
--- a/typo3/sysext/backend/Classes/Backend/ToolbarItems/ShortcutToolbarItem.php
+++ b/typo3/sysext/backend/Classes/Backend/ToolbarItems/ShortcutToolbarItem.php
@@ -307,6 +307,7 @@ class ShortcutToolbarItem implements ToolbarItemInterface
 
     /**
      * Adds the correct token, if the url is an index.php script
+     * @todo: this needs love
      *
      * @param string $url
      * @return string
@@ -320,16 +321,20 @@ class ShortcutToolbarItem implements ToolbarItemInterface
         if (isset($parameters['returnUrl'])) {
             $parsedReturnUrl = parse_url($parameters['returnUrl']);
             parse_str($parsedReturnUrl['query'], $returnUrlParameters);
-            if (strpos($parsedReturnUrl['path'], 'index.php') !== false && isset($returnUrlParameters['M'])) {
-                $module = $returnUrlParameters['M'];
+            if (strpos($parsedReturnUrl['path'], 'index.php') !== false && !empty($returnUrlParameters['route'])) {
+                $module = $returnUrlParameters['route'];
                 $returnUrl = BackendUtility::getModuleUrl($module, $returnUrlParameters);
                 $parameters['returnUrl'] = $returnUrl;
                 $url = $parsedUrl['path'] . '?' . http_build_query($parameters, '', '&', PHP_QUERY_RFC3986);
             }
         }
+        if (isset($parameters['M']) && empty($parameters['route'])) {
+            $parameters['route'] = $parameters['M'];
+            unset($parameters['M']);
+        }
 
-        if (strpos($parsedUrl['path'], 'index.php') !== false && isset($parameters['M'])) {
-            $module = $parameters['M'];
+        if (strpos($parsedUrl['path'], 'index.php') !== false && isset($parameters['route'])) {
+            $module = $parameters['route'];
             $url = BackendUtility::getModuleUrl($module, $parameters);
         } elseif (strpos($parsedUrl['path'], 'index.php') !== false && isset($parameters['route'])) {
             $routePath = $parameters['route'];
diff --git a/typo3/sysext/backend/Classes/Controller/EditDocumentController.php b/typo3/sysext/backend/Classes/Controller/EditDocumentController.php
index 952a8f5bfab9914ba2c987e655c6de3dd5714847..7b2502c12ea50f7069fc01175eb436d4e78298c9 100644
--- a/typo3/sysext/backend/Classes/Controller/EditDocumentController.php
+++ b/typo3/sysext/backend/Classes/Controller/EditDocumentController.php
@@ -1287,13 +1287,13 @@ class EditDocumentController extends AbstractModule
                     $returnUrl = $this->retUrl;
                     if ($this->firstEl['table'] === 'pages') {
                         parse_str((string)parse_url($returnUrl, PHP_URL_QUERY), $queryParams);
-                        if (isset($queryParams['M'])
+                        if (isset($queryParams['route'])
                             && isset($queryParams['id'])
                             && (string)$this->firstEl['uid'] === (string)$queryParams['id']
                         ) {
                             // TODO: Use the page's pid instead of 0, this requires a clean API to manipulate the page
                             // tree from the outside to be able to mark the pid as active
-                            $returnUrl = BackendUtility::getModuleUrl($queryParams['M'], ['id' => 0]);
+                            $returnUrl = BackendUtility::getModuleUrl($queryParams['route'], ['id' => 0]);
                         }
                     }
                     $deleteButton = $buttonBar->makeLinkButton()
diff --git a/typo3/sysext/backend/Classes/Controller/PageLayoutController.php b/typo3/sysext/backend/Classes/Controller/PageLayoutController.php
index fdf5f593be8353b2eb80cdcf2d2ce9f9d741e83d..ddfff72aabc9b4cad917d3968727db033f55b488 100644
--- a/typo3/sysext/backend/Classes/Controller/PageLayoutController.php
+++ b/typo3/sysext/backend/Classes/Controller/PageLayoutController.php
@@ -975,7 +975,7 @@ class PageLayoutController
             ->setModuleName($this->moduleName)
             ->setGetVariables([
                 'id',
-                'M',
+                'route',
                 'edit_record',
                 'pointer',
                 'new_unique_uid',
diff --git a/typo3/sysext/backend/Classes/Http/Application.php b/typo3/sysext/backend/Classes/Http/Application.php
index 683a1b35f0ddfe09d9b57f4155fcc7282d165c68..fe59180c9f236e19b365cbf08667c4d92555970a 100644
--- a/typo3/sysext/backend/Classes/Http/Application.php
+++ b/typo3/sysext/backend/Classes/Http/Application.php
@@ -33,18 +33,12 @@ class Application implements ApplicationInterface
      */
     protected $entryPointLevel = 1;
 
-    /**
-     * @var \Psr\Http\Message\ServerRequestInterface
-     */
-    protected $request;
-
     /**
      * All available request handlers that can handle backend requests (non-CLI)
      * @var array
      */
     protected $availableRequestHandlers = [
         \TYPO3\CMS\Backend\Http\RequestHandler::class,
-        \TYPO3\CMS\Backend\Http\BackendModuleRequestHandler::class,
         \TYPO3\CMS\Backend\Http\AjaxRequestHandler::class
     ];
 
@@ -81,12 +75,7 @@ class Application implements ApplicationInterface
      */
     public function run(callable $execute = null)
     {
-        $this->request = \TYPO3\CMS\Core\Http\ServerRequestFactory::fromGlobals();
-        if (isset($this->request->getQueryParams()['M'])) {
-            $this->request = $this->request->withAttribute('isModuleRequest', true);
-        }
-
-        $this->bootstrap->handleRequest($this->request);
+        $this->bootstrap->handleRequest(\TYPO3\CMS\Core\Http\ServerRequestFactory::fromGlobals());
 
         if ($execute !== null) {
             call_user_func($execute);
diff --git a/typo3/sysext/backend/Classes/Http/BackendModuleRequestHandler.php b/typo3/sysext/backend/Classes/Http/BackendModuleRequestHandler.php
deleted file mode 100644
index 38c5d073db07bca46e52094d1bb5514aa8db01d1..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Classes/Http/BackendModuleRequestHandler.php
+++ /dev/null
@@ -1,222 +0,0 @@
-<?php
-namespace TYPO3\CMS\Backend\Http;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-use Psr\Http\Message\ServerRequestInterface;
-use TYPO3\CMS\Backend\Utility\BackendUtility;
-use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
-use TYPO3\CMS\Core\Core\Bootstrap;
-use TYPO3\CMS\Core\Exception;
-use TYPO3\CMS\Core\FormProtection\BackendFormProtection;
-use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
-use TYPO3\CMS\Core\Http\Dispatcher;
-use TYPO3\CMS\Core\Http\RequestHandlerInterface;
-use TYPO3\CMS\Core\Http\Response;
-use TYPO3\CMS\Core\Type\Bitmask\Permission;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Core\Utility\MathUtility;
-
-/**
- * Handles the request for backend modules and wizards
- * Juggles with $GLOBALS['TBE_MODULES']
- */
-class BackendModuleRequestHandler implements RequestHandlerInterface
-{
-    /**
-     * @var Bootstrap
-     */
-    protected $bootstrap;
-
-    /**
-     * @var array
-     */
-    protected $moduleRegistry = [];
-
-    /**
-     * @var BackendUserAuthentication
-     */
-    protected $backendUserAuthentication;
-
-    /**
-     * Instance of the current Http Request
-     * @var ServerRequestInterface
-     */
-    protected $request;
-
-    /**
-     * Constructor handing over the bootstrap and the original request
-     *
-     * @param Bootstrap $bootstrap
-     */
-    public function __construct(Bootstrap $bootstrap)
-    {
-        $this->bootstrap = $bootstrap;
-    }
-
-    /**
-     * Handles the request, evaluating the configuration and executes the module accordingly
-     *
-     * @param ServerRequestInterface $request
-     * @return NULL|\Psr\Http\Message\ResponseInterface
-     * @throws Exception
-     */
-    public function handleRequest(ServerRequestInterface $request)
-    {
-        $this->request = $request;
-        $this->boot();
-
-        $this->moduleRegistry = $GLOBALS['TBE_MODULES'];
-
-        if (!$this->isValidModuleRequest()) {
-            throw new Exception('The CSRF protection token for the requested module is missing or invalid', 1417988921);
-        }
-
-        $this->backendUserAuthentication = $GLOBALS['BE_USER'];
-
-        $moduleName = (string)$this->request->getQueryParams()['M'];
-        return $this->dispatchModule($moduleName);
-    }
-
-    /**
-     * Execute TYPO3 bootstrap
-     */
-    protected function boot()
-    {
-        $this->bootstrap->checkLockedBackendAndRedirectOrDie()
-            ->checkBackendIpOrDie()
-            ->checkSslBackendAndRedirectIfNeeded()
-            ->initializeBackendRouter()
-            ->loadExtTables()
-            ->initializeBackendUser()
-            ->initializeBackendAuthentication()
-            ->initializeLanguageObject()
-            ->initializeBackendTemplate()
-            ->endOutputBufferingAndCleanPreviousOutput()
-            ->initializeOutputCompression()
-            ->sendHttpHeaders();
-    }
-
-    /**
-     * This request handler can handle any backend request coming from index.php
-     *
-     * @param ServerRequestInterface $request
-     * @return bool
-     */
-    public function canHandleRequest(ServerRequestInterface $request)
-    {
-        return $request->getAttribute('isModuleRequest', false);
-    }
-
-    /**
-     * Checks if all parameters are met.
-     *
-     * @return bool
-     */
-    protected function isValidModuleRequest()
-    {
-        return $this->getFormProtection() instanceof BackendFormProtection
-            && $this->getFormProtection()->validateToken((string)$this->request->getQueryParams()['moduleToken'], 'moduleCall', (string)$this->request->getQueryParams()['M']);
-    }
-
-    /**
-     * Executes the modules configured via Extbase
-     *
-     * @param string $moduleName
-     * @return Response A PSR-7 response object
-     * @throws \RuntimeException
-     */
-    protected function dispatchModule($moduleName)
-    {
-        $moduleConfiguration = $this->getModuleConfiguration($moduleName);
-
-        $response = GeneralUtility::makeInstance(Response::class);
-
-        // Check permissions and exit if the user has no permission for entry
-        $this->backendUserAuthentication->modAccess($moduleConfiguration, true);
-        $id = isset($this->request->getQueryParams()['id']) ? $this->request->getQueryParams()['id'] : $this->request->getParsedBody()['id'];
-        if ($id && MathUtility::canBeInterpretedAsInteger($id)) {
-            $permClause = $this->backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
-            // Check page access
-            $access = is_array(BackendUtility::readPageAccess((int)$id, $permClause));
-            if (!$access) {
-                // Check if page has been deleted
-                $deleteField = $GLOBALS['TCA']['pages']['ctrl']['delete'];
-                $pageInfo = BackendUtility::getRecord('pages', (int)$id, $deleteField, $permClause ? ' AND ' . $permClause : '', false);
-                if (!$pageInfo[$deleteField]) {
-                    throw new \RuntimeException('You don\'t have access to this page', 1289917924);
-                }
-            }
-        }
-
-        // Use Core Dispatching
-        if (isset($moduleConfiguration['routeTarget'])) {
-            $dispatcher = GeneralUtility::makeInstance(Dispatcher::class);
-            $this->request = $this->request->withAttribute('target', $moduleConfiguration['routeTarget']);
-            $response = $dispatcher->dispatch($this->request, $response);
-        } else {
-            // extbase module
-            $configuration = [
-                'extensionName' => $moduleConfiguration['extensionName'],
-                'pluginName' => $moduleName
-            ];
-            if (isset($moduleConfiguration['vendorName'])) {
-                $configuration['vendorName'] = $moduleConfiguration['vendorName'];
-            }
-
-            // Run Extbase
-            $bootstrap = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Core\Bootstrap::class);
-            $content = $bootstrap->run('', $configuration);
-
-            $response->getBody()->write($content);
-        }
-
-        return $response;
-    }
-
-    /**
-     * Returns the module configuration which is provided during module registration
-     *
-     * @param string $moduleName
-     * @return array
-     * @throws \RuntimeException
-     */
-    protected function getModuleConfiguration($moduleName)
-    {
-        if (!isset($this->moduleRegistry['_configuration'][$moduleName])) {
-            throw new \RuntimeException('Module ' . $moduleName . ' is not configured.', 1289918325);
-        }
-        return $this->moduleRegistry['_configuration'][$moduleName];
-    }
-
-    /**
-     * Returns the priority - how eager the handler is to actually handle the request.
-     *
-     * @return int The priority of the request handler.
-     */
-    public function getPriority()
-    {
-        return 90;
-    }
-
-    /**
-     * Wrapper method for static form protection utility
-     *
-     * @return \TYPO3\CMS\Core\FormProtection\AbstractFormProtection
-     */
-    protected function getFormProtection()
-    {
-        return FormProtectionFactory::get();
-    }
-}
diff --git a/typo3/sysext/backend/Classes/Http/RequestHandler.php b/typo3/sysext/backend/Classes/Http/RequestHandler.php
index 084e891a6e200257fffe5bb60250d53bdad4d2d5..a24905f77b2f4c540cce8acb870aa1a62fe1f85c 100644
--- a/typo3/sysext/backend/Classes/Http/RequestHandler.php
+++ b/typo3/sysext/backend/Classes/Http/RequestHandler.php
@@ -59,14 +59,22 @@ class RequestHandler implements RequestHandlerInterface
      */
     public function handleRequest(ServerRequestInterface $request)
     {
+        // Check if a module URL is requested and deprecate this call
+        $moduleName = $request->getQueryParams()['M'] ?? $request->getParsedBody()['M'] ?? null;
         // Allow the login page to be displayed if routing is not used and on index.php
-        $pathToRoute = (string)$request->getQueryParams()['route'] ?: '/login';
+        $pathToRoute = $request->getQueryParams()['route'] ?? $request->getParsedBody()['route'] ?? $moduleName ?? '/login';
         $request = $request->withAttribute('routePath', $pathToRoute);
 
         // skip the BE user check on the login page
         // should be handled differently in the future by checking the Bootstrap directly
         $this->boot($pathToRoute === '/login');
 
+        if ($moduleName !== null) {
+            trigger_error('Calling the TYPO3 Backend with "M" GET parameter will be removed in TYPO3 v10,'
+                . ' the calling code calls this script with "&M=' . $moduleName . '" and needs to be adapted'
+                . ' to use the TYPO3 API.', E_USER_DEPRECATED);
+        }
+
         // Check if the router has the available route and dispatch.
         try {
             return $this->dispatch($request);
diff --git a/typo3/sysext/backend/Classes/Http/RouteDispatcher.php b/typo3/sysext/backend/Classes/Http/RouteDispatcher.php
index 957f85c785c7a09eed65abbafa46fdb7983942af..bfd9bc6339049680269c2cd116a5260c8daa2f2c 100644
--- a/typo3/sysext/backend/Classes/Http/RouteDispatcher.php
+++ b/typo3/sysext/backend/Classes/Http/RouteDispatcher.php
@@ -19,9 +19,12 @@ use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Routing\Exception\InvalidRequestTokenException;
 use TYPO3\CMS\Backend\Routing\Route;
 use TYPO3\CMS\Backend\Routing\Router;
+use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
 use TYPO3\CMS\Core\Http\Dispatcher;
 use TYPO3\CMS\Core\Http\DispatcherInterface;
+use TYPO3\CMS\Core\Http\Response;
+use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -45,10 +48,14 @@ class RouteDispatcher extends Dispatcher implements DispatcherInterface
         /** @var Route $route */
         $route = $router->matchRequest($request);
         $request = $request->withAttribute('route', $route);
+        $request = $request->withAttribute('target', $route->getOption('target'));
         if (!$this->isValidRequest($request)) {
             throw new InvalidRequestTokenException('Invalid request for route "' . $route->getPath() . '"', 1425389455);
         }
 
+        if ($route->getOption('module')) {
+            return $this->dispatchModule($request, $response);
+        }
         $targetIdentifier = $route->getOption('target');
         $target = $this->getCallableFromTarget($targetIdentifier);
         return call_user_func_array($target, [$request, $response]);
@@ -82,4 +89,84 @@ class RouteDispatcher extends Dispatcher implements DispatcherInterface
         $token = (string)(isset($request->getParsedBody()['token']) ? $request->getParsedBody()['token'] : $request->getQueryParams()['token']);
         return $this->getFormProtection()->validateToken($token, 'route', $route->getOption('_identifier'));
     }
+
+    /**
+     * Executes the modules configured via Extbase
+     *
+     * @param ServerRequestInterface $request
+     * @param ResponseInterface $response
+     * @return ResponseInterface A PSR-7 response object
+     * @throws \RuntimeException
+     */
+    protected function dispatchModule(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
+    {
+        $route = $request->getAttribute('route');
+        $moduleName = $route->getOption('moduleName');
+        $moduleConfiguration = $this->getModuleConfiguration($moduleName);
+
+        $backendUserAuthentication = $GLOBALS['BE_USER'];
+
+        // Check permissions and exit if the user has no permission for entry
+        // @todo please do not use "true" here, what a bad coding paradigm
+        $backendUserAuthentication->modAccess($moduleConfiguration, true);
+        $id = (int)$request->getQueryParams()['id'] ?? $request->getParsedBody()['id'];
+        if ($id) {
+            $permClause = $backendUserAuthentication->getPagePermsClause(Permission::PAGE_SHOW);
+            // Check page access
+            if (!is_array(BackendUtility::readPageAccess($id, $permClause))) {
+                // Check if page has been deleted
+                $deleteField = $GLOBALS['TCA']['pages']['ctrl']['delete'];
+                $pageInfo = BackendUtility::getRecord('pages', $id, $deleteField, $permClause ? ' AND ' . $permClause : '', false);
+                if (!$pageInfo[$deleteField]) {
+                    throw new \RuntimeException('You don\'t have access to this page', 1289917924);
+                }
+            }
+        }
+
+        // Use regular Dispatching
+        // @todo: unify with the code above
+        $targetIdentifier = $route->getOption('target');
+        if (!empty($targetIdentifier)) {
+            // @internal routeParameters are a helper construct for the install tool only.
+            // @todo: remove this, after sub-actions in install tool can be addressed directly
+            if (!empty($moduleConfiguration['routeParameters'])) {
+                $request = $request->withQueryParams(array_merge_recursive(
+                    $request->getQueryParams(),
+                    $moduleConfiguration['routeParameters']
+                ));
+            }
+            return parent::dispatch($request, $response);
+        }
+        // extbase module
+        $configuration = [
+                'extensionName' => $moduleConfiguration['extensionName'],
+                'pluginName' => $moduleName
+            ];
+        if (isset($moduleConfiguration['vendorName'])) {
+            $configuration['vendorName'] = $moduleConfiguration['vendorName'];
+        }
+
+        // Run Extbase
+        $bootstrap = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Core\Bootstrap::class);
+        $content = $bootstrap->run('', $configuration);
+
+        $response->getBody()->write($content);
+
+        return $response;
+    }
+
+    /**
+     * Returns the module configuration which is provided during module registration
+     *
+     * @param string $moduleName
+     * @return array
+     * @throws \RuntimeException
+     */
+    protected function getModuleConfiguration($moduleName)
+    {
+        if (!isset($GLOBALS['TBE_MODULES']['_configuration'][$moduleName])) {
+            throw new \RuntimeException('Module ' . $moduleName . ' is not configured.', 1289918325);
+        }
+        return $GLOBALS['TBE_MODULES']['_configuration'][$moduleName];
+    }
 }
diff --git a/typo3/sysext/backend/Classes/RecordList/AbstractRecordList.php b/typo3/sysext/backend/Classes/RecordList/AbstractRecordList.php
index 21d9a61b79197ba61ba51a3c402aea290dba9791..2bb2b17f33d0b8761d4dd15fa7f29f41652ea94e 100644
--- a/typo3/sysext/backend/Classes/RecordList/AbstractRecordList.php
+++ b/typo3/sysext/backend/Classes/RecordList/AbstractRecordList.php
@@ -15,9 +15,7 @@ namespace TYPO3\CMS\Backend\RecordList;
  */
 
 use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider;
-use TYPO3\CMS\Backend\Routing\Router;
 use TYPO3\CMS\Backend\Routing\UriBuilder;
-use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
@@ -215,12 +213,8 @@ abstract class AbstractRecordList
     protected function determineScriptUrl()
     {
         if ($routePath = GeneralUtility::_GP('route')) {
-            $router = GeneralUtility::makeInstance(Router::class);
-            $route = $router->match($routePath);
             $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
-            $this->thisScript = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'));
-        } elseif ($moduleName = GeneralUtility::_GP('M')) {
-            $this->thisScript = BackendUtility::getModuleUrl($moduleName);
+            $this->thisScript = (string)$uriBuilder->buildUriFromRoutePath($routePath);
         } else {
             $this->thisScript = GeneralUtility::getIndpEnv('SCRIPT_NAME');
         }
diff --git a/typo3/sysext/backend/Classes/Routing/UriBuilder.php b/typo3/sysext/backend/Classes/Routing/UriBuilder.php
index 93e5fadabb38a5672ff999e38de37f0ec0d426da..f2a9d313b8c1a57972bdbdff7f525068e4f2b913 100644
--- a/typo3/sysext/backend/Classes/Routing/UriBuilder.php
+++ b/typo3/sysext/backend/Classes/Routing/UriBuilder.php
@@ -54,6 +54,25 @@ class UriBuilder
         $this->routes = $router->getRoutes();
     }
 
+    /**
+     * Generates a URL or path for a specific route based on the given rout.
+     * Currently used to link to the current script, it is encouraged to use "buildUriFromRoute" if possible.
+     *
+     * If there is no route with the given name, the generator throws the RouteNotFoundException.
+     *
+     * @param string $pathInfo The path to the route
+     * @param array $parameters An array of parameters
+     * @param string $referenceType The type of reference to be generated (one of the constants)
+     * @return Uri The generated Uri
+     * @throws RouteNotFoundException If the named route doesn't exist
+     */
+    public function buildUriFromRoutePath($pathInfo, $parameters = [], $referenceType = self::ABSOLUTE_PATH)
+    {
+        $router = GeneralUtility::makeInstance(Router::class);
+        $route = $router->match($pathInfo);
+        return $this->buildUriFromRoute($route->getOption('_identifier'), $parameters, $referenceType);
+    }
+
     /**
      * Generates a URL or path for a specific route based on the given parameters.
      * When the route is configured with "access=public" then the token generation is left out.
@@ -106,8 +125,8 @@ class UriBuilder
     public function buildUriFromModule($moduleName, $parameters = [], $referenceType = self::ABSOLUTE_PATH)
     {
         $parameters = [
-            'M' => $moduleName,
-            'moduleToken' => FormProtectionFactory::get('backend')->generateToken('moduleCall', $moduleName)
+            'route' => $moduleName,
+            'token' => FormProtectionFactory::get('backend')->generateToken('route', $moduleName)
         ] + $parameters;
         return $this->buildUri($parameters, $referenceType);
     }
diff --git a/typo3/sysext/backend/Classes/Template/Components/Buttons/Action/ShortcutButton.php b/typo3/sysext/backend/Classes/Template/Components/Buttons/Action/ShortcutButton.php
index 14db2a1d29424eacc974fdb18bbd514d8037ad17..b3b815041ad59c8dae8d7e6862dcd8fe11868363 100644
--- a/typo3/sysext/backend/Classes/Template/Components/Buttons/Action/ShortcutButton.php
+++ b/typo3/sysext/backend/Classes/Template/Components/Buttons/Action/ShortcutButton.php
@@ -239,7 +239,7 @@ class ShortcutButton implements ButtonInterface, PositionInterface
 
         // Set default GET parameters
         if ($emptyGetVariables) {
-            $this->getVariables = ['id', 'M'];
+            $this->getVariables = ['id', 'route'];
         }
 
         // Automatically determine module name in Extbase context
diff --git a/typo3/sysext/backend/Classes/Template/DocumentTemplate.php b/typo3/sysext/backend/Classes/Template/DocumentTemplate.php
index 02365ea0deb4128a450984be95662f500d6f4977..446bf200151d18cf2625e62ec3cad1db34b69d8e 100644
--- a/typo3/sysext/backend/Classes/Template/DocumentTemplate.php
+++ b/typo3/sysext/backend/Classes/Template/DocumentTemplate.php
@@ -251,7 +251,7 @@ function jumpToUrl(URL) {
         $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
 
         // Setting default scriptID, trim forward slash from route
-        $this->scriptID = GeneralUtility::_GET('M') !== null ? GeneralUtility::_GET('M') : ltrim(GeneralUtility::_GET('route'), '/');
+        $this->scriptID = ltrim(GeneralUtility::_GET('route'), '/');
         $this->bodyTagId = preg_replace('/[^A-Za-z0-9-]/', '-', $this->scriptID);
         // Individual configuration per script? If so, make a recursive merge of the arrays:
         if (is_array($GLOBALS['TBE_STYLES']['scriptIDindex'][$this->scriptID])) {
diff --git a/typo3/sysext/backend/Classes/Template/ModuleTemplate.php b/typo3/sysext/backend/Classes/Template/ModuleTemplate.php
index f8b6622b1268b3e8befda5028fb81e42a6fe4cd3..aa55258e0fa6e145e38b2bf97b76be07d9a79da8 100644
--- a/typo3/sysext/backend/Classes/Template/ModuleTemplate.php
+++ b/typo3/sysext/backend/Classes/Template/ModuleTemplate.php
@@ -510,8 +510,9 @@ class ModuleTemplate
         // since this is used for icons.
         $moduleName = $modName === 'xMOD_alt_doc.php' ? 'record_edit' : $modName;
         // Add the module identifier automatically if typo3/index.php is used:
-        if (GeneralUtility::_GET('M') !== null) {
-            $storeUrl = '&M=' . $moduleName . $storeUrl;
+        // @todo: routing
+        if (GeneralUtility::_GET('route') !== null) {
+            $storeUrl = '&route=' . $moduleName . $storeUrl;
         }
         if ((int)$motherModName === 1) {
             $motherModule = 'top.currentModuleLoaded';
diff --git a/typo3/sysext/backend/Classes/Tree/View/AbstractTreeView.php b/typo3/sysext/backend/Classes/Tree/View/AbstractTreeView.php
index 2564e31bf185bd0cece9144e9939caa3d5f39c9c..13dab282fae1b8365f66b1052b037254462fad92 100644
--- a/typo3/sysext/backend/Classes/Tree/View/AbstractTreeView.php
+++ b/typo3/sysext/backend/Classes/Tree/View/AbstractTreeView.php
@@ -14,7 +14,6 @@ namespace TYPO3\CMS\Backend\Tree\View;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Backend\Routing\Router;
 use TYPO3\CMS\Backend\Routing\UriBuilder;
 use TYPO3\CMS\Backend\Tree\Pagetree\Commands;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
@@ -289,12 +288,8 @@ abstract class AbstractTreeView
     protected function determineScriptUrl()
     {
         if ($routePath = GeneralUtility::_GP('route')) {
-            $router = GeneralUtility::makeInstance(Router::class);
-            $route = $router->match($routePath);
             $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
-            $this->thisScript = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'));
-        } elseif ($moduleName = GeneralUtility::_GP('M')) {
-            $this->thisScript = BackendUtility::getModuleUrl($moduleName);
+            $this->thisScript = (string)$uriBuilder->buildUriFromRoutePath($routePath);
         } else {
             $this->thisScript = GeneralUtility::getIndpEnv('SCRIPT_NAME');
         }
diff --git a/typo3/sysext/backend/Classes/Utility/BackendUtility.php b/typo3/sysext/backend/Classes/Utility/BackendUtility.php
index 103a2feb361be0fde0a3ff1d8b6b2e272ac8b626..46af96a288d908e719dc534b626d313971082c03 100644
--- a/typo3/sysext/backend/Classes/Utility/BackendUtility.php
+++ b/typo3/sysext/backend/Classes/Utility/BackendUtility.php
@@ -2950,14 +2950,10 @@ class BackendUtility
             $script = basename(PATH_thisScript);
         }
 
-        if (GeneralUtility::_GP('route')) {
-            $router = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\Router::class);
-            $route = $router->match(GeneralUtility::_GP('route'));
+        if ($routePath = GeneralUtility::_GP('route')) {
             $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
-            $scriptUrl = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'));
+            $scriptUrl = (string)$uriBuilder->buildUriFromRoutePath($routePath, $mainParams);
             $scriptUrl .= $addParams;
-        } elseif ($script === 'index.php' && GeneralUtility::_GET('M')) {
-            $scriptUrl = self::getModuleUrl(GeneralUtility::_GET('M'), $mainParams) . $addParams;
         } else {
             $scriptUrl = $script . '?' . GeneralUtility::implodeArrayForUrl('', $mainParams) . $addParams;
         }
@@ -3169,8 +3165,7 @@ class BackendUtility
         try {
             $uri = $uriBuilder->buildUriFromRoute($moduleName, $urlParameters);
         } catch (\TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException $e) {
-            // no route registered, use the fallback logic to check for a module
-            $uri = $uriBuilder->buildUriFromModule($moduleName, $urlParameters);
+            $uri = $uriBuilder->buildUriFromRoutePath($moduleName, $urlParameters);
         }
         return (string)$uri;
     }
diff --git a/typo3/sysext/beuser/Classes/Controller/BackendUserActionController.php b/typo3/sysext/beuser/Classes/Controller/BackendUserActionController.php
index fe9bce3f6a9a965fee41849c6428f8c2f4d0fa14..61a1a70eb9750774b1e42b6de218574d05fdad66 100644
--- a/typo3/sysext/beuser/Classes/Controller/BackendUserActionController.php
+++ b/typo3/sysext/beuser/Classes/Controller/BackendUserActionController.php
@@ -125,7 +125,7 @@ class BackendUserActionController extends ActionController
         $extensionName = $currentRequest->getControllerExtensionName();
         if (count($getVars) === 0) {
             $modulePrefix = strtolower('tx_' . $extensionName . '_' . $moduleName);
-            $getVars = ['id', 'M', $modulePrefix];
+            $getVars = ['id', 'route', $modulePrefix];
         }
         $shortcutName = $this->getLanguageService()->sL('LLL:EXT:beuser/Resources/Private/Language/locallang.xml:backendUsers');
         if ($this->request->getControllerName() === 'BackendUser') {
diff --git a/typo3/sysext/beuser/Classes/Controller/PermissionController.php b/typo3/sysext/beuser/Classes/Controller/PermissionController.php
index 81155f031aca938ff34d153f4a05db7bbdeb7c81..072dd3bf481f6f4d7709c1fe2b436cce98ce9fdd 100644
--- a/typo3/sysext/beuser/Classes/Controller/PermissionController.php
+++ b/typo3/sysext/beuser/Classes/Controller/PermissionController.php
@@ -158,7 +158,7 @@ class PermissionController extends ActionController
         $extensionName = $currentRequest->getControllerExtensionName();
         if (empty($getVars)) {
             $modulePrefix = strtolower('tx_' . $extensionName . '_' . $moduleName);
-            $getVars = ['id', 'M', $modulePrefix];
+            $getVars = ['id', 'route', $modulePrefix];
         }
 
         if ($currentRequest->getControllerActionName() === 'edit') {
diff --git a/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php b/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php
index c0f3b752e46a1ffc5e03ef11aacfe97ffc75f012..292524f2a5501875a38b94708efe7f2db7844d67 100644
--- a/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php
+++ b/typo3/sysext/core/Classes/Utility/ExtensionManagementUtility.php
@@ -858,9 +858,8 @@ class ExtensionManagementUtility
         }
 
         // add additional configuration
+        $fullModuleSignature = $main . ($sub ? '_' . $sub : '');
         if (is_array($moduleConfiguration) && !empty($moduleConfiguration)) {
-            $fullModuleSignature = $main . ($sub ? '_' . $sub : '');
-
             if (!empty($moduleConfiguration['icon'])) {
                 $iconRegistry = GeneralUtility::makeInstance(IconRegistry::class);
                 $iconIdentifier = 'module-' . $fullModuleSignature;
@@ -876,6 +875,32 @@ class ExtensionManagementUtility
 
             $GLOBALS['TBE_MODULES']['_configuration'][$fullModuleSignature] = $moduleConfiguration;
         }
+
+        // Also register the module as regular route
+        // Build Route objects from the data
+        $name = $fullModuleSignature;
+        if (isset($moduleConfiguration['path'])) {
+            $path = $moduleConfiguration['path'];
+        } else {
+            $path = str_replace('_', '/', $name);
+        }
+        $path = '/' . trim($path, '/') . '/';
+
+        $options = [
+            'module' => true,
+            'moduleName' => $fullModuleSignature,
+            'access' => $moduleConfiguration['access'] ?: 'user,group'
+        ];
+        if ($moduleConfiguration['routeTarget']) {
+            $options['target'] = $moduleConfiguration['routeTarget'];
+        }
+
+        $router = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\Router::class);
+        $router->addRoute(
+            $name,
+            // @todo: see if we should do a "module route"
+            GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\Route::class, $path, $options)
+        );
     }
 
     /**
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-82406-RoutingBackendModulesRunThroughRegularDispatcher.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-82406-RoutingBackendModulesRunThroughRegularDispatcher.rst
new file mode 100644
index 0000000000000000000000000000000000000000..fd79b8e5a70883715c7ddd39b64cdce77a85017e
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Breaking-82406-RoutingBackendModulesRunThroughRegularDispatcher.rst
@@ -0,0 +1,49 @@
+.. include:: ../../Includes.txt
+
+==========================================================================
+Breaking: #82406 - Routing: Backend Modules run through regular dispatcher
+==========================================================================
+
+See :issue:`82406`
+
+Description
+===========
+
+Calling Backend modules was previously handled via a special `BackendModuleRequestHandler` which has
+been removed.
+
+When registering a Backend module, a route with the name of the module is automatically added to the
+Backend Router.
+
+When generating URLs for modules, the module is not added via the GET Parameter `&M=moduleName`
+anymore, but built like any other Backend Route (currently with the "route" and "token" parameters)
+
+All request handling functionality is now done by the regular Backend RequestHandler,
+which checks if the Route to be targeted is a module, and does extra module permission checks.
+
+
+Impact
+======
+
+Handling with the "&M" GET parameter in backend modules won't deliver the correct result anymore.
+
+Instantiating `BackendModuleRequestHandler` will result in a fatal PHP error.
+
+
+Affected Installations
+======================
+
+Installations with custom extensions including backend modules which work directly with the GET
+parameter "M".
+
+
+Migration
+=========
+
+If extensions use API methods like ``BackendUtility::getModuleUrl()`` are used, nothing needs to be
+modified.
+
+If a backend module is using the GET parameter "M" currently, the code needs to be adjusted to the
+GET "route" or use the UriBuilder directly.
+
+.. index:: Backend, PartiallyScanned
diff --git a/typo3/sysext/cshmanual/Classes/Controller/HelpController.php b/typo3/sysext/cshmanual/Classes/Controller/HelpController.php
index e623c99fde9db92c22328e9d9aab9162698b5391..06e25777560850bda48da1f7079ce46b749124bb 100644
--- a/typo3/sysext/cshmanual/Classes/Controller/HelpController.php
+++ b/typo3/sysext/cshmanual/Classes/Controller/HelpController.php
@@ -151,7 +151,7 @@ class HelpController extends ActionController
             $extensionName = $currentRequest->getControllerExtensionName();
             if (count($getVars) === 0) {
                 $modulePrefix = strtolower('tx_' . $extensionName . '_' . $moduleName);
-                $getVars = ['id', 'M', $modulePrefix];
+                $getVars = ['id', 'route', $modulePrefix];
             }
             $shortcutButton = $buttonBar->makeShortcutButton()
                 ->setModuleName($moduleName)
diff --git a/typo3/sysext/extbase/Classes/Mvc/Web/Routing/UriBuilder.php b/typo3/sysext/extbase/Classes/Mvc/Web/Routing/UriBuilder.php
index f27a50666ee216642a7549cb27bb44ddf2ca11ae..169644191b2e291a67914611917182bf2b6b2f5a 100644
--- a/typo3/sysext/extbase/Classes/Mvc/Web/Routing/UriBuilder.php
+++ b/typo3/sysext/extbase/Classes/Mvc/Web/Routing/UriBuilder.php
@@ -664,24 +664,32 @@ class UriBuilder
             }
         } else {
             $id = GeneralUtility::_GP('id');
-            $module = GeneralUtility::_GP('M');
+            $module = GeneralUtility::_GP('route');
             if ($id !== null) {
                 $arguments['id'] = $id;
             }
             if ($module !== null) {
-                $arguments['M'] = $module;
+                $arguments['route'] = $module;
             }
         }
         ArrayUtility::mergeRecursiveWithOverrule($arguments, $this->arguments);
         $arguments = $this->convertDomainObjectsToIdentityArrays($arguments);
         $this->lastArguments = $arguments;
-        $moduleName = $arguments['M'];
-        unset($arguments['M'], $arguments['moduleToken']);
+        $moduleName = $arguments['route'] ?? null;
+        unset($arguments['route'], $arguments['token']);
         $backendUriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
-        if ($this->request instanceof WebRequest && $this->createAbsoluteUri) {
-            $uri = (string)$backendUriBuilder->buildUriFromModule($moduleName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_URL);
+        if (!empty($moduleName)) {
+            if ($this->request instanceof WebRequest && $this->createAbsoluteUri) {
+                $uri = (string)$backendUriBuilder->buildUriFromRoutePath($moduleName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_URL);
+            } else {
+                $uri = (string)$backendUriBuilder->buildUriFromRoutePath($moduleName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_PATH);
+            }
         } else {
-            $uri = (string)$backendUriBuilder->buildUriFromModule($moduleName, $arguments);
+            if ($this->request instanceof WebRequest && $this->createAbsoluteUri) {
+                $uri = (string)$backendUriBuilder->buildUriFromModule($moduleName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_URL);
+            } else {
+                $uri = (string)$backendUriBuilder->buildUriFromModule($moduleName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_PATH);
+            }
         }
         if ($this->section !== '') {
             $uri .= '#' . $this->section;
diff --git a/typo3/sysext/extbase/Tests/Unit/Mvc/Web/Routing/UriBuilderTest.php b/typo3/sysext/extbase/Tests/Unit/Mvc/Web/Routing/UriBuilderTest.php
index a86b73b6d3cca85584fa91e9eb3e70e712850a42..063f27200e48cb37ec292fcc89722e43f52bcb35 100644
--- a/typo3/sysext/extbase/Tests/Unit/Mvc/Web/Routing/UriBuilderTest.php
+++ b/typo3/sysext/extbase/Tests/Unit/Mvc/Web/Routing/UriBuilderTest.php
@@ -13,6 +13,8 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Mvc\Web\Routing;
  *
  * The TYPO3 project - inspiring people to share!
  */
+use TYPO3\CMS\Backend\Routing\Route;
+use TYPO3\CMS\Backend\Routing\Router;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
@@ -76,6 +78,9 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
         $this->uriBuilder->_set('configurationManager', $this->mockConfigurationManager);
         $this->uriBuilder->_set('extensionService', $this->mockExtensionService);
         $this->uriBuilder->_set('environmentService', $this->createMock(EnvironmentService::class));
+        $router = GeneralUtility::makeInstance(Router::class);
+        $router->addRoute('module_key', new Route('/test/Path', []));
+        $router->addRoute('module_key2', new Route('/test/Path2', []));
         // Mocking backend user is required for backend URI generation as BackendUtility::getModuleUrl() is called
         $backendUserMock = $this->createMock(BackendUserAuthentication::class);
         $backendUserMock->expects($this->any())->method('check')->will($this->returnValue(true));
@@ -207,12 +212,12 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
      */
     public function buildBackendUriKeepsQueryParametersIfAddQueryStringIsSet()
     {
-        GeneralUtility::_GETset(['M' => 'moduleKey', 'id' => 'pageId', 'foo' => 'bar']);
+        GeneralUtility::_GETset(['route' => '/test/Path', 'id' => 'pageId', 'foo' => 'bar']);
         $_POST = [];
         $_POST['foo2'] = 'bar2';
         $this->uriBuilder->setAddQueryString(true);
         $this->uriBuilder->setAddQueryStringMethod('GET,POST');
-        $expectedResult = '/typo3/index.php?M=moduleKey&moduleToken=dummyToken&id=pageId&foo=bar&foo2=bar2';
+        $expectedResult = '/typo3/index.php?route=%2Ftest%2FPath&token=dummyToken&id=pageId&foo=bar&foo2=bar2';
         $actualResult = $this->uriBuilder->buildBackendUri();
         $this->assertEquals($expectedResult, $actualResult);
     }
@@ -222,12 +227,12 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
      */
     public function buildBackendUriKeepsQueryParametersIfAddQueryStringMethodIsNotSet()
     {
-        GeneralUtility::_GETset(['M' => 'moduleKey', 'id' => 'pageId', 'foo' => 'bar']);
+        GeneralUtility::_GETset(['route' => '/test/Path', 'id' => 'pageId', 'foo' => 'bar']);
         $_POST = [];
         $_POST['foo2'] = 'bar2';
         $this->uriBuilder->setAddQueryString(true);
         $this->uriBuilder->setAddQueryStringMethod(null);
-        $expectedResult = '/typo3/index.php?M=moduleKey&moduleToken=dummyToken&id=pageId&foo=bar';
+        $expectedResult = '/typo3/index.php?route=%2Ftest%2FPath&token=dummyToken&id=pageId&foo=bar';
         $actualResult = $this->uriBuilder->buildBackendUri();
         $this->assertEquals($expectedResult, $actualResult);
     }
@@ -240,7 +245,7 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
         return [
             'Arguments to be excluded in the beginning' => [
                 [
-                    'M' => 'moduleKey',
+                    'route' => '/test/Path',
                     'id' => 'pageId',
                     'foo' => 'bar'
                 ],
@@ -248,25 +253,25 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
                     'foo2' => 'bar2'
                 ],
                 [
-                    'M',
+                    'route',
                     'id'
                 ],
-                '/typo3/index.php?M=&moduleToken=dummyToken&foo=bar&foo2=bar2'
+                '/typo3/index.php?route=&token=dummyToken&foo=bar&foo2=bar2'
             ],
             'Arguments to be excluded in the end' => [
                 [
                     'foo' => 'bar',
                     'id' => 'pageId',
-                    'M' => 'moduleKey'
+                    'route' => '/test/Path'
                 ],
                 [
                     'foo2' => 'bar2'
                 ],
                 [
-                    'M',
+                    'route',
                     'id'
                 ],
-                '/typo3/index.php?M=&moduleToken=dummyToken&foo=bar&foo2=bar2'
+                '/typo3/index.php?route=&token=dummyToken&foo=bar&foo2=bar2'
             ],
             'Arguments in nested array to be excluded' => [
                 [
@@ -274,7 +279,7 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
                         'bar' => 'baz'
                     ],
                     'id' => 'pageId',
-                    'M' => 'moduleKey'
+                    'route' => '/test/Path'
                 ],
                 [
                     'foo2' => 'bar2'
@@ -283,7 +288,7 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
                     'id',
                     'tx_foo[bar]'
                 ],
-                '/typo3/index.php?M=moduleKey&moduleToken=dummyToken&foo2=bar2'
+                '/typo3/index.php?route=%2Ftest%2FPath&token=dummyToken&foo2=bar2'
             ],
             'Arguments in multidimensional array to be excluded' => [
                 [
@@ -293,7 +298,7 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
                         ]
                     ],
                     'id' => 'pageId',
-                    'M' => 'moduleKey'
+                    'route' => '/test/Path'
                 ],
                 [
                     'foo2' => 'bar2'
@@ -302,7 +307,7 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
                     'id',
                     'tx_foo[bar][baz]'
                 ],
-                '/typo3/index.php?M=moduleKey&moduleToken=dummyToken&foo2=bar2'
+                '/typo3/index.php?route=%2Ftest%2FPath&token=dummyToken&foo2=bar2'
             ],
         ];
     }
@@ -331,8 +336,8 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
      */
     public function buildBackendUriKeepsModuleQueryParametersIfAddQueryStringIsNotSet()
     {
-        GeneralUtility::_GETset(['M' => 'moduleKey', 'id' => 'pageId', 'foo' => 'bar']);
-        $expectedResult = '/typo3/index.php?M=moduleKey&moduleToken=dummyToken&id=pageId';
+        GeneralUtility::_GETset(['route' => '/test/Path', 'id' => 'pageId', 'foo' => 'bar']);
+        $expectedResult = '/typo3/index.php?route=%2Ftest%2FPath&token=dummyToken&id=pageId';
         $actualResult = $this->uriBuilder->buildBackendUri();
         $this->assertEquals($expectedResult, $actualResult);
     }
@@ -342,9 +347,9 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
      */
     public function buildBackendUriMergesAndOverrulesQueryParametersWithArguments()
     {
-        GeneralUtility::_GETset(['M' => 'moduleKey', 'id' => 'pageId', 'foo' => 'bar']);
-        $this->uriBuilder->setArguments(['M' => 'overwrittenModuleKey', 'somePrefix' => ['bar' => 'baz']]);
-        $expectedResult = '/typo3/index.php?M=overwrittenModuleKey&moduleToken=dummyToken&id=pageId&somePrefix%5Bbar%5D=baz';
+        GeneralUtility::_GETset(['route' => '/test/Path', 'id' => 'pageId', 'foo' => 'bar']);
+        $this->uriBuilder->setArguments(['route' => '/test/Path2', 'somePrefix' => ['bar' => 'baz']]);
+        $expectedResult = '/typo3/index.php?route=%2Ftest%2FPath2&token=dummyToken&id=pageId&somePrefix%5Bbar%5D=baz';
         $actualResult = $this->uriBuilder->buildBackendUri();
         $this->assertEquals($expectedResult, $actualResult);
     }
@@ -354,11 +359,11 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
      */
     public function buildBackendUriConvertsDomainObjectsAfterArgumentsHaveBeenMerged()
     {
-        GeneralUtility::_GETset(['M' => 'moduleKey']);
+        GeneralUtility::_GETset(['route' => '/test/Path']);
         $mockDomainObject = $this->getAccessibleMock(AbstractEntity::class, ['dummy']);
         $mockDomainObject->_set('uid', '123');
         $this->uriBuilder->setArguments(['somePrefix' => ['someDomainObject' => $mockDomainObject]]);
-        $expectedResult = '/typo3/index.php?M=moduleKey&moduleToken=dummyToken&somePrefix%5BsomeDomainObject%5D=123';
+        $expectedResult = '/typo3/index.php?route=%2Ftest%2FPath&token=dummyToken&somePrefix%5BsomeDomainObject%5D=123';
         $actualResult = $this->uriBuilder->buildBackendUri();
         $this->assertEquals($expectedResult, $actualResult);
     }
@@ -368,9 +373,9 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
      */
     public function buildBackendUriRespectsSection()
     {
-        GeneralUtility::_GETset(['M' => 'moduleKey']);
+        GeneralUtility::_GETset(['route' => '/test/Path']);
         $this->uriBuilder->setSection('someSection');
-        $expectedResult = '/typo3/index.php?M=moduleKey&moduleToken=dummyToken#someSection';
+        $expectedResult = '/typo3/index.php?route=%2Ftest%2FPath&token=dummyToken#someSection';
         $actualResult = $this->uriBuilder->buildBackendUri();
         $this->assertEquals($expectedResult, $actualResult);
     }
@@ -381,12 +386,12 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
     public function buildBackendUriCreatesAbsoluteUrisIfSpecified()
     {
         GeneralUtility::flushInternalRuntimeCaches();
-        GeneralUtility::_GETset(['M' => 'moduleKey']);
+        GeneralUtility::_GETset(['route' => '/test/Path']);
         $_SERVER['HTTP_HOST'] = 'baseuri';
         $_SERVER['SCRIPT_NAME'] = '/typo3/index.php';
         $this->mockRequest->expects($this->any())->method('getBaseUri')->will($this->returnValue('http://baseuri'));
         $this->uriBuilder->setCreateAbsoluteUri(true);
-        $expectedResult = 'http://baseuri/' . TYPO3_mainDir . 'index.php?M=moduleKey&moduleToken=dummyToken';
+        $expectedResult = 'http://baseuri/' . TYPO3_mainDir . 'index.php?route=%2Ftest%2FPath&token=dummyToken';
         $actualResult = $this->uriBuilder->buildBackendUri();
         $this->assertSame($expectedResult, $actualResult);
     }
@@ -419,7 +424,7 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
         ];
         $this->uriBuilder->setAddQueryString(true);
         $this->uriBuilder->setAddQueryStringMethod('POST,GET');
-        $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('/typo3/index.php?M=&moduleToken=dummyToken&key1=POST1&key2=GET2&key3[key31]=POST31&key3[key32]=GET32&key3[key33][key331]=GET331&key3[key33][key332]=POST332');
+        $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('/typo3/index.php?route=&token=dummyToken&key1=POST1&key2=GET2&key3[key31]=POST31&key3[key32]=GET32&key3[key33][key331]=GET331&key3[key33][key332]=POST332');
         $actualResult = $this->uriBuilder->buildBackendUri();
         $this->assertEquals($expectedResult, $actualResult);
     }
@@ -452,7 +457,7 @@ class UriBuilderTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
         ];
         $this->uriBuilder->setAddQueryString(true);
         $this->uriBuilder->setAddQueryStringMethod('GET,POST');
-        $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('/typo3/index.php?M=&moduleToken=dummyToken&key1=GET1&key2=POST2&key3[key31]=GET31&key3[key32]=POST32&key3[key33][key331]=POST331&key3[key33][key332]=GET332');
+        $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('/typo3/index.php?route=&token=dummyToken&key1=GET1&key2=POST2&key3[key31]=GET31&key3[key32]=POST32&key3[key33][key331]=POST331&key3[key33][key332]=GET332');
         $actualResult = $this->uriBuilder->buildBackendUri();
         $this->assertEquals($expectedResult, $actualResult);
     }
diff --git a/typo3/sysext/filelist/Classes/FileList.php b/typo3/sysext/filelist/Classes/FileList.php
index fe821d9879000af5e32ebbc04a79683fad822fb2..207bd6b3be774c8d70642b4d8c48aa55aec81333 100644
--- a/typo3/sysext/filelist/Classes/FileList.php
+++ b/typo3/sysext/filelist/Classes/FileList.php
@@ -16,7 +16,6 @@ namespace TYPO3\CMS\Filelist;
 
 use TYPO3\CMS\Backend\Clipboard\Clipboard;
 use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider;
-use TYPO3\CMS\Backend\Routing\Router;
 use TYPO3\CMS\Backend\Routing\UriBuilder;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Database\ConnectionPool;
@@ -1489,12 +1488,8 @@ class FileList
     protected function determineScriptUrl()
     {
         if ($routePath = GeneralUtility::_GP('route')) {
-            $router = GeneralUtility::makeInstance(Router::class);
-            $route = $router->match($routePath);
             $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
-            $this->thisScript = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'));
-        } elseif ($moduleName = GeneralUtility::_GP('M')) {
-            $this->thisScript = BackendUtility::getModuleUrl($moduleName);
+            $this->thisScript = (string)$uriBuilder->buildUriFromRoutePath($routePath);
         } else {
             $this->thisScript = GeneralUtility::getIndpEnv('SCRIPT_NAME');
         }
diff --git a/typo3/sysext/fluid/Classes/ViewHelpers/Be/Buttons/ShortcutViewHelper.php b/typo3/sysext/fluid/Classes/ViewHelpers/Be/Buttons/ShortcutViewHelper.php
index 9e838b9c21c3cf290d9f8e85928afb9943128246..12ba44799249a458adff43658f584a8967293ce7 100644
--- a/typo3/sysext/fluid/Classes/ViewHelpers/Be/Buttons/ShortcutViewHelper.php
+++ b/typo3/sysext/fluid/Classes/ViewHelpers/Be/Buttons/ShortcutViewHelper.php
@@ -34,7 +34,7 @@ use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
  * </output>
  *
  * <code title="Explicitly set parameters to be stored in the shortcut">
- * <f:be.buttons.shortcut getVars="{0: 'M', 1: 'myOwnPrefix'}" setVars="{0: 'function'}" />
+ * <f:be.buttons.shortcut getVars="{0: 'route', 1: 'myOwnPrefix'}" setVars="{0: 'function'}" />
  * </code>
  * <output>
  * Shortcut button as known from the TYPO3 backend.
@@ -99,7 +99,7 @@ class ShortcutViewHelper extends AbstractBackendViewHelper
             $moduleName = $currentRequest->getPluginName();
             if (count($getVars) === 0) {
                 $modulePrefix = strtolower('tx_' . $extensionName . '_' . $moduleName);
-                $getVars = ['id', 'M', $modulePrefix];
+                $getVars = ['id', 'route', $modulePrefix];
             }
             $getList = implode(',', $getVars);
             $setList = implode(',', $setVars);
diff --git a/typo3/sysext/form/Classes/Controller/FormManagerController.php b/typo3/sysext/form/Classes/Controller/FormManagerController.php
index 353eb05a6b72ae0dc866d1e5d8fb61db8fbdb9f3..80b473f97689f6266540a7de5997a50bcec93ef6 100644
--- a/typo3/sysext/form/Classes/Controller/FormManagerController.php
+++ b/typo3/sysext/form/Classes/Controller/FormManagerController.php
@@ -427,7 +427,7 @@ class FormManagerController extends AbstractBackendController
             $extensionName = $currentRequest->getControllerExtensionName();
             if (count($getVars) === 0) {
                 $modulePrefix = strtolower('tx_' . $extensionName . '_' . $moduleName);
-                $getVars = ['id', 'M', $modulePrefix];
+                $getVars = ['id', 'route', $modulePrefix];
             }
 
             $shortcutButton = $buttonBar->makeShortcutButton()
diff --git a/typo3/sysext/info/Classes/Controller/InfoModuleController.php b/typo3/sysext/info/Classes/Controller/InfoModuleController.php
index 309fe694305d4d0668130c4d551fa089c46f0013..24426a349ed4a798510723983b6297d2dad89274 100644
--- a/typo3/sysext/info/Classes/Controller/InfoModuleController.php
+++ b/typo3/sysext/info/Classes/Controller/InfoModuleController.php
@@ -172,7 +172,7 @@ class InfoModuleController extends BaseScriptClass
             ->setModuleName($this->moduleName)
             ->setDisplayName($this->MOD_MENU['function'][$this->MOD_SETTINGS['function']])
             ->setGetVariables([
-                'M',
+                'route',
                 'id',
                 'edit_record',
                 'pointer',
diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ClassNameMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ClassNameMatcher.php
index ba0746a6be33f4ec46baa580a257fce2098c3801..3f2af70a4755288a7bd95b88ec265bf3b18f7330 100644
--- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ClassNameMatcher.php
+++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ClassNameMatcher.php
@@ -379,6 +379,11 @@ return [
             'Breaking-55298-DecoupledHistoryFunctionality.rst',
         ],
     ],
+    'TYPO3\CMS\Backend\Http\BackendModuleRequestHandler' => [
+        'restFiles' => [
+            'Breaking-82406-RoutingBackendModulesRunThroughRegularDispatcher.rst',
+        ],
+    ],
 
     // Removed interfaces
     'TYPO3\CMS\Backend\Form\DatabaseFileIconsHookInterface' => [
diff --git a/typo3/sysext/lowlevel/Classes/Utility/ArrayBrowser.php b/typo3/sysext/lowlevel/Classes/Utility/ArrayBrowser.php
index 01700abf698c4084951500ac82574243b6e227ac..a7051053c318fbe80b1c461f943663cc0efea37f 100644
--- a/typo3/sysext/lowlevel/Classes/Utility/ArrayBrowser.php
+++ b/typo3/sysext/lowlevel/Classes/Utility/ArrayBrowser.php
@@ -109,7 +109,7 @@ class ArrayBrowser
             $output .= '<li' . ($isResult ? ' class="active"' : '') . '>';
             if ($isArray && !$this->expAll) {
                 $goto = 'a' . substr(md5($depth), 0, 6);
-                $output .= '<a class="list-tree-control' . ($isExpanded ? ' list-tree-control-open' : ' list-tree-control-closed') . '" id="' . $goto . '" href="' . htmlspecialchars((BackendUtility::getModuleUrl(GeneralUtility::_GP('M')) . '&node[' . $depth . ']=' . ($isExpanded ? 0 : 1) . '#' . $goto)) . '"><i class="fa"></i></a> ';
+                $output .= '<a class="list-tree-control' . ($isExpanded ? ' list-tree-control-open' : ' list-tree-control-closed') . '" id="' . $goto . '" href="' . htmlspecialchars((BackendUtility::getModuleUrl(GeneralUtility::_GP('route')) . '&node[' . $depth . ']=' . ($isExpanded ? 0 : 1) . '#' . $goto)) . '"><i class="fa"></i></a> ';
             }
             $output .= '<span class="list-tree-group">';
             $output .= $this->wrapArrayKey($key, $depth, !$isArray ? $value : '');
@@ -149,7 +149,7 @@ class ArrayBrowser
                 . (!MathUtility::canBeInterpretedAsInteger($theValue) ? '\''
                 . addslashes($theValue) . '\'' : $theValue) . '; ';
             $label = '<a class="list-tree-label" href="'
-                . htmlspecialchars((BackendUtility::getModuleUrl(GeneralUtility::_GP('M'))
+                . htmlspecialchars((BackendUtility::getModuleUrl(GeneralUtility::_GP('route'))
                 . '&varname=' . urlencode($variableName)))
                 . '#varname">' . $label . '</a>';
         }
diff --git a/typo3/sysext/recordlist/Classes/Browser/AbstractElementBrowser.php b/typo3/sysext/recordlist/Classes/Browser/AbstractElementBrowser.php
index f1b277fd86a0fa8e555ad60ffd20824a9b2e2dc9..2e819dad6fd0e7a0f287a1c3f33b9161bd913ca1 100644
--- a/typo3/sysext/recordlist/Classes/Browser/AbstractElementBrowser.php
+++ b/typo3/sysext/recordlist/Classes/Browser/AbstractElementBrowser.php
@@ -14,10 +14,8 @@ namespace TYPO3\CMS\Recordlist\Browser;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Backend\Routing\Router;
 use TYPO3\CMS\Backend\Routing\UriBuilder;
 use TYPO3\CMS\Backend\Template\DocumentTemplate;
-use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Localization\LanguageService;
@@ -106,12 +104,8 @@ abstract class AbstractElementBrowser
     protected function determineScriptUrl()
     {
         if ($routePath = GeneralUtility::_GP('route')) {
-            $router = GeneralUtility::makeInstance(Router::class);
-            $route = $router->match($routePath);
             $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
-            $this->thisScript = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'));
-        } elseif ($moduleName = GeneralUtility::_GP('M')) {
-            $this->thisScript = BackendUtility::getModuleUrl($moduleName);
+            $this->thisScript = (string)$uriBuilder->buildUriFromRoutePath($routePath);
         } else {
             $this->thisScript = GeneralUtility::getIndpEnv('SCRIPT_NAME');
         }
diff --git a/typo3/sysext/recordlist/Classes/Controller/AbstractLinkBrowserController.php b/typo3/sysext/recordlist/Classes/Controller/AbstractLinkBrowserController.php
index c1dbc264a8dd6a1d7b93c05d92c7cdbaa0897bb7..fe40625bb78fd8422538f54430c655ad20824f45 100644
--- a/typo3/sysext/recordlist/Classes/Controller/AbstractLinkBrowserController.php
+++ b/typo3/sysext/recordlist/Classes/Controller/AbstractLinkBrowserController.php
@@ -16,7 +16,6 @@ namespace TYPO3\CMS\Recordlist\Controller;
 
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
-use TYPO3\CMS\Backend\Routing\Router;
 use TYPO3\CMS\Backend\Routing\UriBuilder;
 use TYPO3\CMS\Backend\Template\DocumentTemplate;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
@@ -209,12 +208,8 @@ abstract class AbstractLinkBrowserController
     protected function determineScriptUrl(ServerRequestInterface $request)
     {
         if ($routePath = $request->getQueryParams()['route']) {
-            $router = GeneralUtility::makeInstance(Router::class);
-            $route = $router->match($routePath);
             $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
-            $this->thisScript = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'));
-        } elseif ($moduleName = $request->getQueryParams()['M']) {
-            $this->thisScript = BackendUtility::getModuleUrl($moduleName);
+            $this->thisScript = (string)$uriBuilder->buildUriFromRoutePath($routePath);
         } else {
             $this->thisScript = GeneralUtility::getIndpEnv('SCRIPT_NAME');
         }
diff --git a/typo3/sysext/recordlist/Classes/RecordList/AbstractDatabaseRecordList.php b/typo3/sysext/recordlist/Classes/RecordList/AbstractDatabaseRecordList.php
index 1b3b1dde87cfbce88e7ba875afa686ae27d16d69..672ff22b063ba291e9b78e1cf78b526119862024 100644
--- a/typo3/sysext/recordlist/Classes/RecordList/AbstractDatabaseRecordList.php
+++ b/typo3/sysext/recordlist/Classes/RecordList/AbstractDatabaseRecordList.php
@@ -15,7 +15,6 @@ namespace TYPO3\CMS\Recordlist\RecordList;
  */
 
 use TYPO3\CMS\Backend\RecordList\AbstractRecordList;
-use TYPO3\CMS\Backend\Routing\Router;
 use TYPO3\CMS\Backend\Routing\UriBuilder;
 use TYPO3\CMS\Backend\Tree\View\PageTreeView;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
@@ -1189,12 +1188,8 @@ class AbstractDatabaseRecordList extends AbstractRecordList
         $urlParameters = array_merge_recursive($urlParameters, $this->overrideUrlParameters);
 
         if ($routePath = GeneralUtility::_GP('route')) {
-            $router = GeneralUtility::makeInstance(Router::class);
-            $route = $router->match($routePath);
             $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
-            $url = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'), $urlParameters);
-        } elseif ($moduleName = GeneralUtility::_GP('M')) {
-            $url = BackendUtility::getModuleUrl($moduleName, $urlParameters);
+            $url = (string)$uriBuilder->buildUriFromRoutePath($routePath, $urlParameters);
         } else {
             $url = GeneralUtility::getIndpEnv('SCRIPT_NAME') . '?' . ltrim(GeneralUtility::implodeArrayForUrl('', $urlParameters), '&');
         }
diff --git a/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php b/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php
index 4da1c9e42d077249a79ae15676d04f12b9c7e6cd..bd6d78f10e3c82cba9bf15066011f41e540ba27b 100644
--- a/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php
+++ b/typo3/sysext/recordlist/Classes/RecordList/DatabaseRecordList.php
@@ -17,7 +17,6 @@ namespace TYPO3\CMS\Recordlist\RecordList;
 use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider;
 use TYPO3\CMS\Backend\Module\BaseScriptClass;
 use TYPO3\CMS\Backend\RecordList\RecordListGetTableHookInterface;
-use TYPO3\CMS\Backend\Routing\Router;
 use TYPO3\CMS\Backend\Routing\UriBuilder;
 use TYPO3\CMS\Backend\Template\Components\ButtonBar;
 use TYPO3\CMS\Backend\Template\DocumentTemplate;
@@ -869,7 +868,7 @@ class DatabaseRecordList
                     ->setModuleName('web_list')
                     ->setGetVariables([
                         'id',
-                        'M',
+                        'route',
                         'imagemode',
                         'pointer',
                         'table',
@@ -3580,12 +3579,8 @@ class DatabaseRecordList
         $urlParameters = array_merge_recursive($urlParameters, $this->overrideUrlParameters);
 
         if ($routePath = GeneralUtility::_GP('route')) {
-            $router = GeneralUtility::makeInstance(Router::class);
-            $route = $router->match($routePath);
             $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
-            $url = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'), $urlParameters);
-        } elseif ($moduleName = GeneralUtility::_GP('M')) {
-            $url = BackendUtility::getModuleUrl($moduleName, $urlParameters);
+            $url = (string)$uriBuilder->buildUriFromRoutePath($routePath, $urlParameters);
         } else {
             $url = GeneralUtility::getIndpEnv('SCRIPT_NAME') . '?' . ltrim(
                     GeneralUtility::implodeArrayForUrl('', $urlParameters),
@@ -4152,12 +4147,8 @@ class DatabaseRecordList
     protected function determineScriptUrl()
     {
         if ($routePath = GeneralUtility::_GP('route')) {
-            $router = GeneralUtility::makeInstance(Router::class);
-            $route = $router->match($routePath);
             $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
-            $this->thisScript = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'));
-        } elseif ($moduleName = GeneralUtility::_GP('M')) {
-            $this->thisScript = BackendUtility::getModuleUrl($moduleName);
+            $this->thisScript = (string)$uriBuilder->buildUriFromRoutePath($routePath);
         } else {
             $this->thisScript = GeneralUtility::getIndpEnv('SCRIPT_NAME');
         }
diff --git a/typo3/sysext/recycler/Classes/Controller/RecyclerModuleController.php b/typo3/sysext/recycler/Classes/Controller/RecyclerModuleController.php
index f6e98fc21527ef74672c2d958961595f6a99dec8..a60914e29ece2a855112d4dee6bbd2de63ffe24e 100644
--- a/typo3/sysext/recycler/Classes/Controller/RecyclerModuleController.php
+++ b/typo3/sysext/recycler/Classes/Controller/RecyclerModuleController.php
@@ -148,7 +148,7 @@ class RecyclerModuleController extends ActionController
         $extensionName = $currentRequest->getControllerExtensionName();
         if (count($getVars) === 0) {
             $modulePrefix = strtolower('tx_' . $extensionName . '_' . $moduleName);
-            $getVars = ['id', 'M', $modulePrefix];
+            $getVars = ['id', 'route', $modulePrefix];
         }
         $shortcutButton = $buttonBar->makeShortcutButton()
             ->setModuleName($moduleName)
diff --git a/typo3/sysext/reports/Classes/Controller/ReportController.php b/typo3/sysext/reports/Classes/Controller/ReportController.php
index 36f1cf864cb4b359496ea728d40a1b8441342a47..6a2054231b5bfe257e4a40eb6d6abfc738bb0f5b 100644
--- a/typo3/sysext/reports/Classes/Controller/ReportController.php
+++ b/typo3/sysext/reports/Classes/Controller/ReportController.php
@@ -172,7 +172,7 @@ class ReportController extends ActionController
         $setVars = $this->request->hasArgument('setVars') ? $this->request->getArgument('setVars') : [];
         if (count($getVars) === 0) {
             $modulePrefix = strtolower('tx_' . $this->request->getControllerExtensionName() . '_' . $moduleName);
-            $getVars = ['id', 'M', $modulePrefix];
+            $getVars = ['id', 'route', $modulePrefix];
         }
         $shortcutButton = $buttonBar->makeShortcutButton()
             ->setModuleName($moduleName)
diff --git a/typo3/sysext/tstemplate/Classes/Controller/TypoScriptTemplateModuleController.php b/typo3/sysext/tstemplate/Classes/Controller/TypoScriptTemplateModuleController.php
index 7b669e00204d669cb1027fa33429bd6e16ba3fb4..d3532ba7b1619b281fa431b94fcdf1ed1766d880 100644
--- a/typo3/sysext/tstemplate/Classes/Controller/TypoScriptTemplateModuleController.php
+++ b/typo3/sysext/tstemplate/Classes/Controller/TypoScriptTemplateModuleController.php
@@ -362,7 +362,7 @@ class TypoScriptTemplateModuleController extends BaseScriptClass
         // Shortcut
         $shortcutButton = $buttonBar->makeShortcutButton()
             ->setModuleName($this->MCONF['name'])
-            ->setGetVariables(['id', 'M']);
+            ->setGetVariables(['id', 'route']);
         $buttonBar->addButton($shortcutButton);
     }
 
diff --git a/typo3/sysext/viewpage/Classes/Controller/ViewModuleController.php b/typo3/sysext/viewpage/Classes/Controller/ViewModuleController.php
index 8c2725e1bc6a954897e4fe3907b2df80716c913d..de87dd2f468c29d483633a7d4fe2dba6e7e9db28 100644
--- a/typo3/sysext/viewpage/Classes/Controller/ViewModuleController.php
+++ b/typo3/sysext/viewpage/Classes/Controller/ViewModuleController.php
@@ -103,7 +103,7 @@ class ViewModuleController extends ActionController
         $extensionName = $currentRequest->getControllerExtensionName();
         if (count($getVars) === 0) {
             $modulePrefix = strtolower('tx_' . $extensionName . '_' . $moduleName);
-            $getVars = ['id', 'M', $modulePrefix];
+            $getVars = ['id', 'route', $modulePrefix];
         }
         $shortcutButton = $buttonBar->makeShortcutButton()
             ->setModuleName($moduleName)
diff --git a/typo3/sysext/workspaces/Classes/Controller/PreviewController.php b/typo3/sysext/workspaces/Classes/Controller/PreviewController.php
index 079b4a81625e5a75149f9062f898ec88a6c8d3e3..7945dcc6d0bbb1c9bb7568e0fa6e2c5cd2fa9a23 100644
--- a/typo3/sysext/workspaces/Classes/Controller/PreviewController.php
+++ b/typo3/sysext/workspaces/Classes/Controller/PreviewController.php
@@ -87,7 +87,7 @@ class PreviewController extends AbstractController
 
         // Remove the GET parameters related to the workspaces module and the page id
         unset($queryParameters['tx_workspaces_web_workspacesworkspaces']);
-        unset($queryParameters['M']);
+        unset($queryParameters['route']);
         unset($queryParameters['id']);
 
         // Assemble a query string from the retrieved parameters
diff --git a/typo3/sysext/workspaces/Classes/Controller/ReviewController.php b/typo3/sysext/workspaces/Classes/Controller/ReviewController.php
index 22279a17ad41ac67cd6897717bd90dffdc035783..6c21f5a0dd2aabe34dec17b0101cac42d7b285ce 100644
--- a/typo3/sysext/workspaces/Classes/Controller/ReviewController.php
+++ b/typo3/sysext/workspaces/Classes/Controller/ReviewController.php
@@ -50,7 +50,7 @@ class ReviewController extends AbstractController
         $extensionName = $currentRequest->getControllerExtensionName();
         if (count($getVars) === 0) {
             $modulePrefix = strtolower('tx_' . $extensionName . '_' . $moduleName);
-            $getVars = ['id', 'M', $modulePrefix];
+            $getVars = ['id', 'route', $modulePrefix];
         }
         $shortcutButton = $buttonBar->makeShortcutButton()
             ->setModuleName($moduleName)
diff --git a/typo3/sysext/workspaces/Classes/Service/WorkspaceService.php b/typo3/sysext/workspaces/Classes/Service/WorkspaceService.php
index f03a1c48d53945b61145e5916e8c985062985737..ba257964be14502a9cbdc469d480c35c3f40468c 100644
--- a/typo3/sysext/workspaces/Classes/Service/WorkspaceService.php
+++ b/typo3/sysext/workspaces/Classes/Service/WorkspaceService.php
@@ -863,10 +863,10 @@ class WorkspaceService implements SingletonInterface
         $uriBuilder = $this->getObjectManager()->get(\TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder::class);
         $redirect = 'index.php?redirect_url=';
         // @todo this should maybe be changed so that the extbase URI Builder can deal with module names directly
-        $originalM = GeneralUtility::_GET('M');
-        GeneralUtility::_GETset('web_WorkspacesWorkspaces', 'M');
+        $originalM = GeneralUtility::_GET('route');
+        GeneralUtility::_GETset('web_WorkspacesWorkspaces', 'route');
         $viewScript = $uriBuilder->uriFor('index', [], 'Preview', 'workspaces', 'web_workspacesworkspaces') . '&id=';
-        GeneralUtility::_GETset($originalM, 'M');
+        GeneralUtility::_GETset($originalM, 'route');
         if ($addDomain === true) {
             return BackendUtility::getViewDomain($uid) . $redirect . urlencode($viewScript) . $uid;
         }