I'm building a CSV file and serving it up as a downloadable file. Pretty straightforward. Thing is, I get 18 leading empty rows in my csv file. Any ideas? I've stripped the code down to just about nothing - should I be looking for problems somewhere else?

 * Implementation of hook_menu
  $items['providers/dashboard/generate-csv/%/%'] = array(
   'title' => t('Export CSV Data'),
   'page callback' => 'generate_csv',
   'page arguments' => array(3, 4),
   'access arguments' => array('administer module_name content'),
   'type' => MENU_CALLBACK
  return $items;

 * MENU_CALLBACK function
function generate_csv($source_type, $include_unpublished = FALSE) {

  // start up headers
  $csv_name = $source_type . '-csv_' . date('Y-m-d') . '.csv';
  header('Content-Type: text/x-comma-separated-values');
  header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  header('Cache-Control: private', false); // required for certain browser
  header('Content-Disposition: attachment; filename="' . $csv_name . '"');

  // write csv data   
  $test_output = "test,test,test";
  print $test_output;

EDIT - Looks like it is an issue with the headers, not the content. If I write the contents of my output to a file, I do not have any extra space at the beginning. Does anyone know:

  • What's up with the headers that could be causing the extra spaces? Or..
  • How can I ditch any output and start over fresh with my headers?
  • 1
    If you're getting blank lines at the end of the CSV file it might be because you have blank lines at the end of you PHP file (after the ?>). Try putting drupal_exit() immediately after print $test_output;
    – Clive
    Apr 5, 2012 at 18:02
  • 3
    That is the reason why omitting the closing PHP tag is really a best practice with Drupal.
    – mpdonadio
    Apr 5, 2012 at 18:17
  • Nice idea. Unfortunately I'm working with Drupal 6 & drupal_exit() doesn't seem to be available (sorry I didn't mention that). Alternative? As an aside, wouldn't that add extra rows at the end of the file?
    – doub1ejack
    Apr 5, 2012 at 18:56

2 Answers 2


The comments for my question were pretty much the answer, but I'll elaborate here.

The root of the problem was one of the included files had an ending PHP tag. However, it wasn't in my code and I had no idea where else to look.

I ended up using something similar, to the following code.

watchdog("Module_name", "Check these files: !files", array("!files" => implode(get_included_files(), ', ')));

This gave me a list of all the files that Drupal had included in order to run my code. There were about 140 of them.

The best way to figure out which might have superfluous end tags would be to cycle through each file programmatically, as Luke Redpath suggests on http://www.designerstalk.com/forums/programming/8005-php-finding-occurences-string-file.html.

In my case the file permissions were giving me trouble, so I brute forced it until I found an unrelated (but custom) module that had a bunch of ?> tags.


Maybe this lines "hang" in an output buffer, that were started some time before. This way you can set headers without the good old "headers already sent"-error, but this content will be send to the browser when flushing the buffer anyway.

Try this one

print $csv_data;

Not the answer you're looking for? Browse other questions tagged or ask your own question.