The deal is this: All anonymous users, regardless of what URL they access, should be redirected to the login form (unless of course they're on the login/registration/lost password form; then they should stay there). In their _phptemplate_variables function they have: if (!$user->uid) // no user? send to login section { $vars['template_file'] = 'page-login'; } So one would think that something like this at the top of page-login.tpl.php: if (strncmp($_GET['q'], 'user', 4) != 0) { drupal_goto('user/login'); } ...would do the trick. And one would be WRONG! ;) For, notice: node/1 (exists) == user/login (good!) user/register == user/register (good!) akjdhjkshdjkhsd (not found) == infinite loop (oh no!) admin/settings (403) == infinite loop (oh no!) On a 404/403, $_GET['q'] is actually empty. "Ok!" you think to yourself, "I'll test for $_GET['q'] being empty!" if (empty($_GET['q'] || strncmp($_GET['q'], 'user', 4) != 0) { drupal_goto('user/login'); } WRONG again! :P Because $_GET['q'] is also set to empty when it's in the process of redirecting. How about we use Ted's method for checking headers? $headers = drupal_set_header(); if (strncmp($_GET['q'], 'user', 4) != 0 || strpos($headers, '404') || strpos($headers, '403')) { drupal_goto('user/login'); } No again. So yeah. I'm officially stumped. :( Any ideas?