Built-in Drupal pages and pages created by contrib modules have static paths that are hard-coded. An example of this would be /user where you can log in, or /node, which shows a list of recent nodes. Often times, we care about what the URL in the browser’s address bar looks like, and wish to change these. This post is a summary of three options to achieve this.
Creating an alias
Drupal has support for path aliases, which allows us to specify alternative paths for pages. This is the simplest solution and works in most cases. Some aspects of this solution that may be undesirable in certain cases are the following:
- Aliases leave the old path intact, so we end up with two paths serving the same page. This may have negative affects on page statistics and SEO.
- Aliases don't work with dynamic paths. They don’t allow wildcards so one alias can only rename one single path. You cannot set up, for instance, node/[something] to redirect to myblog/[something] to cover all the node pages.
Using hook_url_inbound_alter and hook_url_outbound_alter
The second solution requires some coding, but also offers a lot more flexibility at the same time. When Drupal handles requests, it sends the incoming path to hook_url_inbound_alter before it starts to interpret the path. This enables modules to customize what content is served to a certain path. For example, if we wished to show our nodes at the path myblog/[node_id] instead of the default node/[node_id], we could do the following:
/** * Implements hook_url_inbound_alter(). */ function mymodule_url_inbound_alter(&$path, $original_path, $path_language) { if (preg_match('@^myblog/([0-9]+)$@', $path, $matches)) { $path = 'node/' . $matches[1]; } }
This will cause our nodes to show up at myblog/[node_id], but links pointing to node/[node_id] will take the visitor to the old path. The solution to this is hook_url_outbound_alter. This hook is called when Drupal generates links, and allows us to modify the path in them. An implementation to replace all node/[node_id] links with myblog/[node_id] looks as follows:
/** * Implements hook_url_outbound_alter(). */ function mymodule_url_outbound_alter(&$path, &$options, $original_path) { if (preg_match('@^node/([0-9]+)$@', $path, $matches)) { $path = 'myblog/' . $matches[1]; // Drupal's alias may override our path. // Set the 'alias' option to prevent it. $options['alias'] = TRUE; } }
To summarize, this method:
- Works with dynamic paths, that is, it can be applied to paths that have wildcards.
- Leaves the old path intact, causing duplicate pages.
- Safe to use in all scenarios with low risk of side effects. If both inbound and outbound URLs are altered properly, this method should not cause any broken links.
Using hook_menu_alter
This is the most drastic solution to achieve the goal. Using hook_menu_alter, custom modules can modify menu items that have been registered by other modules. The following is an example of how one can use hook_menu_alterto modify a path:
/** * Implements hook_menu_alter(). */ function mymodule_menu_alter(&$items) { $items['myfeed.xml'] = $items['rss.xml']; unset($items['rss.xml']); }
In general, you should only do this if you know what you are doing, because it may have unexpected side effects. Most importantly, links to the old URL will be broken after you modify the path this way. In addition, if the menu item being modified has subtabs, those tabs will not appear at the new path unless their menu item is also changed. Leave this as a last resort for situations where you absolutely need to get rid of the old path, say for privacy or similar concerns.
When modifying the path with hook_menu_alter:
- Wildcards are retained, so it works for dynamic paths.
- The old path is completely renamed, such that the page in question is only accessible via the new path.
- There is a considerable risk of causing broken links and missing tabs, so be careful.