Now I know what happened, the file is being created correctly, but the undesired BOM is added when I download it.
This is the problem, I just had to change this:
/* Bad code */
header('Content-disposition: attachment; filename='.$_GET['filename']);
header('Content-type: application/txt');
readfile($_GET['filename']);
to this (Download as binary file so it remains intact):
/* Good code */
header('Content-disposition: attachment; filename='.$_GET['filename']);
header('Content-type: application/txt');
header('Content-Transfer-Encoding: binary');
header('Content-Description: File Transfer');
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate');
ob_clean();
flush();
readfile('txt/'.$_GET['filename']);
(This was originally posted as an edit on the question, but @Daniel suggested posting an answer for clarification).