Platforms

How to add a cookie consent banner to a Ruby on Rails app

In Rails, paste the snippet in the head of app/views/layouts/application.html.erb, above javascript_importmap_tags or javascript_include_tag, so it runs before your JavaScript on every page. Mark tracking tags in the same layout with type="text/plain" and data-cq-category. Turbo Drive needs no extra code: the banner stays in place across visits.
Published 21 October 2026 · By the Cookiequiet team · Markdown

Steps

Copy the snippetIn the Cookiequiet dashboard, add the website and copy the snippet with your site key, data-consent-mode="1" and data-auto-block="1".
Paste it in the application layoutOpen app/views/layouts/application.html.erb and paste the snippet in the head, above javascript_importmap_tags or javascript_include_tag and any other script.
Mark your tagsPaste analytics and advertising tags below the snippet with type="text/plain" and data-cq-category set to analytics or marketing.
Send page views on Turbo visitsIf your analytics needs a page view per visit, send it from a turbo:load listener, only when the cq:consent event has granted analytics.
Allow the origin in your CSPIf config/initializers/content_security_policy.rb defines a policy, add https://cookiequiet.com to script_src and connect_src.
Add the Cookie preferences linkIn the footer partial, add a plain anchor with href="#cq-preferences", data-cq-show-preferences and data-turbo="false".
Test with Reject allOpen the app in a private window, click Reject all and check that only cq_consent and the session cookie are set. Accept all, reload, follow a few links and check the consent log.

Where does the snippet go in Rails?

Rails renders every view inside a layout, app/views/layouts/application.html.erb by default. Paste the snippet in its head, above the tags that load your JavaScript (javascript_importmap_tags with importmap-rails, javascript_include_tag with jsbundling-rails):

erb
<!DOCTYPE html>
<html>
<head>
<title><%= content_for(:title) || "My app" %></title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<script src="https://cookiequiet.com/cq.js" data-site="YOUR_SITE_KEY" data-consent-mode="1" data-auto-block="1" defer></script>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
</head>
<body>
<%= yield %>
</body>
</html>

The site key is public (it appears in every page), so writing it in the layout is fine. If other layouts exist, for example for mailers or an admin area, add the snippet only to those that visitors see.

Paste tracking tags under the snippet and change their opening tag:

html
<script type="text/plain" data-cq-category="marketing">
/* Meta Pixel base code, unchanged */
</script>

Cookiequiet runs marked tags when the category is granted, and at once on later visits where the stored choice includes it. The Meta Pixel guide shows the full base code, and the Google Analytics 4 guide the Google tag with its Consent Mode alternative. To print them only in production, wrap them in <% if Rails.env.production? %>. Scripts that your JavaScript or a library creates at runtime are held by the auto-blocker when they come from one of the about 80 trackers it knows.

What changes with Turbo Drive?

Turbo Drive, on by default in new Rails apps, turns link clicks into fetches. The Turbo handbook says it replaces the body with the new page's body and merges the heads, and that a script in the head runs only the first time it appears. Three consequences:

  • The snippet runs once per full load and the stored choice stays applied across visits.
  • Marked tags belong in the head. Cookiequiet activates them on load and after each choice, not on each Turbo visit.
  • The banner's container is appended to the body, which Turbo replaces. Cookiequiet puts it back into the new body, so a banner waiting for an answer, the preferences button and showPreferences() keep working without extra code.

For code that runs per page, such as page views, Turbo fires turbo:load after the first load and after every visit; send the page view there only when the cq:consent event Cookiequiet dispatches on window has granted analytics.

How do I allow Cookiequiet in the Content Security Policy?

Rails defines the policy in config/initializers/content_security_policy.rb. Add the origin to the two directives the banner needs:

ruby
Rails.application.configure do
config.content_security_policy do |policy|
policy.script_src :self, "https://cookiequiet.com"
policy.connect_src :self, "https://cookiequiet.com"
end
end

script-src loads cq.js; connect-src covers the configuration, the geo lookup and the consent records. If the policy uses nonces, give marked inline scripts nonce="<%= content_security_policy_nonce %>": Cookiequiet copies the nonce to the script it runs after consent, so the policy lets it through.

html
<a href="#cq-preferences" data-cq-show-preferences data-turbo="false">Cookie preferences</a>

Cookiequiet handles the click and opens the preferences centre; data-turbo="false" keeps Turbo from treating it as a visit.

How do I test it?

  1. Run bin/rails server or bin/dev, open the app in a private window and view the source: cq.js comes before your JavaScript.
  2. Click Reject all. The cookies are cq_consent and the session cookie Rails sets (named after the app, such as _myapp_session), which is strictly necessary.
  3. Clear the cookies, reload, and follow a link before choosing: the banner stays visible.
  4. Click Accept all, reload: the marked tags run.
  5. Check the consent log in the dashboard.

There is no gem to install. Cookiequiet is free for one website with geo-targeting; Starter is €3 per website per month with unlimited pageviews.

Questions

No. The banner is one script tag in the application layout, with no gem, no importmap pin and no npm package. Pinning it would also lose the data attributes it reads its settings from.
Yes. Turbo Drive replaces the body on each visit, and the banner's container lives in the body, so Cookiequiet puts it back into the new body. The banner stays until the visitor chooses, and the preferences button keeps working.
The session cookie keeps the visitor's session and, with it, the protection against cross-site request forgery, so it is strictly necessary in most readings of the ePrivacy rules. List it as necessary in the cookie table.
An API-only app renders no HTML pages, so the snippet goes into the front-end that does: follow the React or Vue guide for a single-page app served separately.

Put a quiet banner on your website today

Free for one website with every core feature, no card and no trial clock. Upgrade a website to Starter for $3 a month to match your brand with AI and drop the branding, or to Pro for $9 to design it with the AI agent.