Problems with send_data, PDF files and ActionController vs ApplicationController

I’m having a problem with a controller that I’m writing to generate and return a PDF.

If I make a super basic HTML template like so:

  <%= form_with url:'/card', method: :get, local: true do |form| %>
    <%= form.textarea :id, size: "70x1" %>
    <%= form.submit "Get  PDF" %>
  <% end %>

That is rendered on my landing “root” page, with nothing in the controller.

Where my route file looks like this: get "/card", to: "card#get_pdf"

and my CardController:

class CardController < ActionController::Base
    def get_pdf
    ..........
    data = PdfHelper.generate(card_urls, "LETTER")
    send_data data, :type => 'application/pdf', :disposition => 'attachment', :filename => 'test.pdf'
  end
end

Everything works fine. I am able to click the button on the plain-html-only page and I am returned my generated PDF file.

I’m trying to add styling and JS to the main landing page. When I change my HomeController (with nothing in its methods) to

class HomeController < ApplicationController

Everything breaks - my button no longer downloads the PDF file anymore. I’m pretty stuck as to why. My application Controller is just the default

class ApplicationController < ActionController::Base
  # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has.
  allow_browser versions: :modern
  layout "application"
end

Please let me know if there is any further detail that could help debug this and thanks in advance. I’ve been fighting this for 2 days now and can’t figure out whats wrong.

What does happen when “everything breaks”? What do you see in the browser? What do you see in the development logs (or just the console where Rails is running, in most setups)?

At a guess, it may be something to do with whatever is in app/views/layouts/application.html.erb, since ApplicationController specifies that layout where before I suppose you had none.

This is what I see in the dev console when the request runs

Started GET "/card?id=&commit=Get++PDF" for 127.0.0.1 at 2025-03-19 15:04:34 +0000
Processing by CardController#get_pdf as HTML
  Parameters: {"id"=>"", "commit"=>"Get  PDF"}
generating PDF!
  Rendering text template
  Rendered text template (Duration: 3.2ms | GC: 0.0ms)
Sent data test.pdf (14.8ms)
Completed 200 OK in 1155ms (Views: 14.2ms | ActiveRecord: 0.0ms (0 queries, 0 cached) | GC: 1.0ms)

The web browser just seems to reload the same page, without any file being downloaded or popping up.

In Firefox dev tools I can see the server responded with HTTP 304, content-disposition attachment; filename=“test.pdf”; filename*=UTF-8’'test.pdf

And that the request headers had “Accept: text/html, application/xhtml+xml”

My application.html.erb looks like this


<!DOCTYPE html>
<html>
  <head>
    <title><%= content_for(:title) || "Arkhamprint" %></title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="mobile-web-app-capable" content="yes">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= yield :head %>

    <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
    <%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>

    <link rel="icon" href="/icon.png" type="image/png">
    <link rel="icon" href="/icon.svg" type="image/svg+xml">
    <link rel="apple-touch-icon" href="/icon.png">

    <%# Includes all stylesheet files in app/assets/stylesheets %>
    <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag    "application", :media => "all" %>
    <%= javascript_importmap_tags %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>

The application.html.erb includes javascript_importmap_tags, which is loading turbo, and Turbo Drive is intercepting the form submission. But it’s expecting to see an HTML document in response, not a PDF.

Adding data: { turbo: false } to the form_with in your template will disable Turbo Drive for that form and it will be handled normally by the browser.

Details here: https://turbo.hotwired.dev/handbook/drive#disabling-turbo-drive-on-specific-links-or-forms