Disallow node discovery by ID

Recently I wondered if viewing a node via its internal path could be disallowed, making it accessible using a path alias only. With hook_menu_alter(), a router item's page callback can be changed to our own function that will check if the requested path is internal or not.

<?php
function hidenid_menu_alter(&$items) {
 
$items['node']['page callback'] = 'hidenid_check';
 
$items['node/%node']['page callback'] = 'hidenid_check';
 
$items['node/%node/view']['page callback'] = 'hidenid_check';
}

function

hidenid_check($node = NULL, $cid = NULL) {
 
$path = $_REQUEST['q'];
 
  if (
strpos($path, 'node') === 0) {
   
drupal_not_found();
    return;
  }
  return
node_page_view($node, $cid);
}
?>

You might be asking what's the point of this? Good question! I wondered how such a thing could be accomplished after dreaming up a schedule service built on Drupal. I didn't want to use a node access control, just restrict visibility with obfuscated URLs. Without such a solution a curious visitor could still view posts at node/NID. The above solution is a proof-of-concept, I haven't deployed beyond a prototype so there are likely to be implications I don't know about.

We can't use the PHP superglobal $_GET because Drupal overwrites 'q' in drupal_init_path(). In the above code example the $node argument defaults as NULL in hidenid_check() for the case the requested path is 'node', because no page arguments are set for that callback.

Of course, for practicality there must be an alias or the node cannot be viewed. That's where the core path module and Pathauto helps, perhaps altering the node form to require the alias field as well. And you could always allow user 1 or certain roles to view it via the internal path.