# 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.

Source: https://cookiequiet.com/platforms/ruby-on-rails
Published: 2026-10-21
Publisher: Cookiequiet

## Steps

1. **Copy the snippet** In the Cookiequiet dashboard, add the website and copy the snippet with your site key, data-consent-mode="1" and data-auto-block="1".
2. **Paste it in the application layout** Open 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.
3. **Mark your tags** Paste analytics and advertising tags below the snippet with type="text/plain" and data-cq-category set to analytics or marketing.
4. **Send page views on Turbo visits** If your analytics needs a page view per visit, send it from a turbo:load listener, only when the cq:consent event has granted analytics.
5. **Allow the origin in your CSP** If config/initializers/content_security_policy.rb defines a policy, add https://cookiequiet.com to script_src and connect_src.
6. **Add the Cookie preferences link** In the footer partial, add a plain anchor with href="#cq-preferences", data-cq-show-preferences and data-turbo="false".
7. **Test with Reject all** Open 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.

## How do I block my tags until consent?

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](/platforms/meta-pixel) shows the full base code, and the [Google Analytics 4 guide](/platforms/google-analytics-4) 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.

## How do I add the Cookie preferences link?

```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

### Is there a gem for Cookiequiet?

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.

### Does the banner stay when I click a link with Turbo?

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.

### Does the Rails session cookie need consent?

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.

### Where do I put the tag in an API-only Rails app?

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.

## Sources

- [Rails Guides: Layouts and rendering in Rails](https://guides.rubyonrails.org/layouts_and_rendering.html)
- [Turbo Handbook: Navigate with Turbo Drive](https://turbo.hotwired.dev/handbook/drive)
- [Turbo Handbook: Building your Turbo application (scripts and permanent elements)](https://turbo.hotwired.dev/handbook/building)
- [Turbo Reference: Events (turbo:load)](https://turbo.hotwired.dev/reference/events)
- [Rails Guides: Securing Rails applications (Content Security Policy)](https://guides.rubyonrails.org/security.html)
