How to do file downloading in a controller function?
Never send the response with $response-&gt;send(). Instead return the response:

use Symfony\Component\HttpFoundation\Response;

public function myController() {
    $response = new Response();
    // prepare $response
    return $response;
  }


BTW, this is a standard Symfony Controller. The fact that you also can return the main content in a render array is because Drupal has added an event subscriber, which converts the main content to a response with the themed html page.



This works for all responses, in your case for example for a binary file response:

Drupal\system\FileDownloadController::download():

  public function download(Request $request, $scheme = 'private') {
    $target = $request-&gt;query-&gt;get('file');
    // Merge remaining path arguments into relative file path.
    $uri = $scheme . '://' . $target;

    if (file_stream_wrapper_valid_scheme($scheme) &amp;&amp; file_exists($uri)) {
      // Let other modules provide headers and controls access to the file.
      $headers = $this-&gt;moduleHandler()-&gt;invokeAll('file_download', [$uri]);

      foreach ($headers as $result) {
        if ($result == -1) {
          throw new AccessDeniedHttpException();
        }
      }

      if (count($headers)) {
        // \Drupal\Core\EventSubscriber\FinishResponseSubscriber::onRespond()
        // sets response as not cacheable if the Cache-Control header is not
        // already modified. We pass in FALSE for non-private schemes for the
        // $public parameter to make sure we don't change the headers.
        return new BinaryFileResponse($uri, 200, $headers, $scheme !== 'private');
      }

      throw new AccessDeniedHttpException();
    }

    throw new NotFoundHttpException();
  }