Menu hook callbacks not working for anonymous users My custom error handler module is somewhat based on the customerror module, but instead of returning markup, my menu callback function redirects based on the original path. It registers error paths as follows:

function my_customerror_menu() {
  $items = array();

  $items['my_customerror/%'] = array(
    'title callback' =&gt; FALSE,
    'access callback' =&gt; TRUE,
    'page callback' =&gt; 'my_customerror_page',
    'page arguments' =&gt; array(1),
    'type' =&gt; MENU_CALLBACK,
  );
  return $items;
}


'access callback' =&gt; TRUE should make it accessible to all users. I can use the path my_customerror/404 for example in the site settings and it is recognized as a valid path. This works fine in a local instance and on the Pantheon development  site, but once I pushed it to the Pantheon test environment, my_customerror/404 is not invoked on 404 - Drupal uses the default error response. Unless I am logged in, then it works in Test as well.

But the path my_customerror/404 works regardless when I type it in manually and does what it should. Somehow Drupal erroneously thinks that this path is not accessible to anonymous users.

What could be causing this?

Here is a simplified version of the callback function that shows what it does:

function my_customerror_page($code) {
  $dest = variable_get('my_customerror_' . $code . '_default', '');
  switch ($code) {
    [conditional stuff]
    default:
      header('Status: 301 Moved Permanently', false, 301);
      header("Location: " . $dest);
      break;
  }
  return "";
}


Don't ask why I use header() and not drupal_goto() ....

Just bringing something into the problem description that I already mentioned in a comment. When the Devel module is turned on and anonymous users are given permission to see developer output, it starts working! But this is not a solution, I obviously can't do this in the Live environment.
You're doing a switch(), but with only a default for the switch and no options, it means the switch is unnecessary. Drupal also provides the drupal_goto() function, which handles both redirects, as well as cancelling killing the script so the redirect can proceed. Your code can be simplified to:

function my_customerror_page($code) {
  $dest = variable_get('my_customerror_' . $code . '_default', '');
  drupal_goto($dest);
}


Or if you're really set on using header() to set headers for whatever reason, you just need to add drupal_exit() after your call to header().