Respostas:
Para o Drupal 6,
Deve ser o terceiro parâmetro
drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response_code = 302)
drupal_goto("products", NULL, $node->nid);
Para Drupal 7
drupal_goto($path = '', array $options = array(), $http_response_code = 302)
drupal_goto("products", array('fragment' => $node->nid));
No Drupal 6, o drupal_goto usa seu terceiro parâmetro para a fragmentação. Se você deseja criar a URL como produtos # 345, deve passar seu fragmento como terceiro argumento na função drupal_goto.
drupal_goto("products", NULL, $node->nid); // where $node->nid is the fragment.
No Drupal 7, você deve passar fragmento como par de valor-chave da matriz no segundo parâmetro da função drupal_goto.
drupal_goto('products', array('fragment' => $node->nid)) ; // where $node->nid is the fragment.
Ambos os itens acima produzirão uma URL semelhante aos produtos # 123, onde 123 é o valor da variável $ node-> nid.
drupal_goto perde #zzz se houver um? destination = foobar # zzz, como isso nunca é retornado por $ _SERVER, remova essa linha, pois nada além de quebrar coisas:
//$options['fragment'] = $destination['fragment']; // removed
is the same as
$options['fragment']='';