I want a node to be reached only by its alias but not its internal path. The reason behind this is that I will create a node type, allow anon users to create such nodes, but the alias will contain a random text, so that only the user with the actual url information can revisit this page. Example: www.example.com/node/9 -> only accessible by admin www.example.com/34h356hrgc35yh4geg -> only accessible to anon users with this information.

If an anon user tries www.example.com/node/9, they should see Forbidden error or Not Found.

  • 1
    Not in Drupal, but you can restrict it using Apache config. You can set up Apache so that some urls (in this case it would be those beginning /node ) require http authentication. This would be another layer on top of Drupal authentication, so it does add complexity but I don't see how else it could be achieved. See this question for details and good answers: stackoverflow.com/questions/23469351/…
    – naomi
    Aug 21, 2017 at 15:09
  • Thank you. The problem with this approach for me is every node type's internal path is blocked. I would want only one node type's urls. Aug 22, 2017 at 6:08
  • You would have to use aliases for all nodes.The pathauto module is great for that. However, 4k4's answer below looks better.
    – naomi
    Aug 22, 2017 at 12:04

1 Answer 1


You can do this, if speed is important, in a middleware, checking if the request has no session and then block right away or in a request event subscriber after authentication checking for the path. Or a bit slower after routing if you want to check that the matched route is the canonical entity route and use the node entity in your logic.

The easiest but slowest is in a page preprocess hook. You can even do this in a theme.

function mytheme_preprocess_page(&$variables) {
  if (\Drupal::currentUser()->isAnonymous()) {
    if (isset($variables['node'])) {
      $node = $variables['node'];
      if ($node instanceof \Drupal\node\NodeInterface) {
        if ($node->getType() == 'article') {
          // use PathInfo instead of RequestUri to ignore query strings
          $path = \Drupal::request()->getPathInfo();
          if (preg_match('#/node/\d+/*$#', $path)) {
            throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

The cached page is varied by path and user permission by default and so the not found exception.

This doesn't work if you have installed the Redirect module with the setting Global Redirect - Enforce clean and canonical URLs enabled. In this case you need a request event subscriber with a priority higher than 33.

  • I have the 'slowest' option working in a module using the code above. But this is appealing: "Or a bit slower after routing if you want to check that the matched route is the canonical entity route and use the node entity in your logic" ... what would code for that look like?
    – aharown07
    Dec 10, 2021 at 19:59
  • 1
    Throw the exception in a request event subscriber with a priority of 31. Similar to this but without the cookie and redirect stuff drupal.stackexchange.com/questions/274485/…
    – 4uk4
    Dec 10, 2021 at 20:44

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.