Backend frameworks · 5 min setup

How to add analytics to a Ruby on Rails app

To add VisitTrack to Rails, put the script tag in the <head> of app/views/layouts/application.html.erb, rendered only in production. Turbo Drive visits are counted as pageviews automatically, and the tag loads once because Turbo keeps scripts in <head> across visits.

Updated

If your app sends a Content-Security-Policy (Rails generates config/initializers/content_security_policy.rb), allow the VisitTrack origin there. For conversions, record signups and payments on the server with the visitor id from the tracker's first-party _vt_vid cookie — Rails reads it with cookies[:_vt_vid].

Ruby on Rails at a glance

Where the tag goes
app/views/layouts/application.html.erb, <head>
Production only
<% if Rails.env.production? %>
Turbo
Drive visits tracked; tag loaded once
CSP
config/initializers/content_security_policy.rb
Server events
Net::HTTP.post to /api/track

How to install VisitTrack on Ruby on Rails

  1. Step 1: Store the ids in credentials or ENV

    The site id is public; the API key (VisitTrack Settings → API / MCP) is secret and stays on the server. ENV is used below — Rails credentials work the same.

  2. Step 2: Add the tag to the application layout

    Inside <head>. javascript_include_tag turns the data: hash into a data-site attribute.

    app/views/layouts/application.html.erb
    <head>
      <title><%= content_for(:title) || "My app" %></title>
      <meta name="viewport" content="width=device-width,initial-scale=1">
      <%= csrf_meta_tags %>
      <%= csp_meta_tag %>
      <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
      <%= javascript_importmap_tags %>
      <% if Rails.env.production? %>
        <%= javascript_include_tag "https://visitrack.app/tracker.js", defer: true, data: { site: ENV.fetch("VISITRACK_SITE_ID") } %>
      <% end %>
    </head>
  3. Step 3: Allow the origin in your CSP initializer

    Add it to your existing policy; keep whatever else you already allow.

    config/initializers/content_security_policy.rb
    Rails.application.configure do
      config.content_security_policy do |policy|
        policy.script_src  :self, "https://visitrack.app"
        policy.connect_src :self, "https://visitrack.app"
      end
    end
  4. Step 4: Deploy and browse

    Deploy, open the production site and click around. bin/dev on localhost renders no tag and would be ignored anyway.

How to check VisitTrack is working on Ruby on Rails

  1. View source in production: a <script> tag with src="https://visitrack.app/tracker.js" and your data-site is in the head.
  2. Click links between pages (Turbo Drive) — the VisitTrack live view shows you, and each page appears in the Pages tab.
  3. DevTools → Network: one POST to visitrack.app/api/collect per Turbo visit, with no full page reload.

Turbo Drive fetches the next page and updates the URL with history.pushState, which the tracker wraps — so every Drive visit is a pageview, and back/forward (popstate) too. Turbo merges the new <head> and doesn't re-run scripts that are already there, so the tracker starts once per full page load.

Turbo Frames don't change the URL unless they set data-turbo-action="advance"; when they do, that's a pageview too. Plain frame swaps aren't.

How to track custom events and goals in Ruby on Rails

Send signups from the server. With Devise, override the registrations controller and report the event once the user is saved:

Devise registrations + VisitTrack helper
# app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
  def create
    super do |user|
      VisitTrack.event(cookies[:_vt_vid], "signup", method: "email") if user.persisted?
    end
  end
end

# app/lib/visit_track.rb
require "net/http"

module VisitTrack
  def self.event(visitor_id, name, props = {})
    return if visitor_id.blank?
    Net::HTTP.post(
      URI("https://visitrack.app/api/track"),
      { type: "event", visitorId: visitor_id, name: name, props: props }.to_json,
      "Authorization" => "Bearer #{ENV.fetch("VISITRACK_API_KEY")}",
      "Content-Type" => "application/json"
    )
  rescue StandardError
    nil # analytics must never break signup
  end
end

Route Devise to the controller with devise_for :users, controllers: { registrations: "users/registrations" }. Move the call into a background job if you prefer, passing the cookie value as an argument. In views, data: { vt_goal: "pricing_cta" } on link_to renders data-vt-goal.

Revenue attribution on Ruby on Rails

Creating Stripe Checkout Sessions from a controller? Pass the visitor id as client_reference_id, then connect Stripe in VisitTrack's Settings → Revenue — payments are credited to the visitor's first source. See Stripe revenue attribution.

app/controllers/checkouts_controller.rb
session = Stripe::Checkout::Session.create({
  mode: "subscription",
  line_items: [{ price: params[:price_id], quantity: 1 }],
  success_url: "#{root_url}thanks?session_id={CHECKOUT_SESSION_ID}",
  cancel_url: pricing_url,
  client_reference_id: cookies[:_vt_vid]
})
redirect_to session.url, allow_other_host: true

Track AI crawlers in Rails

AI crawlers don't execute JavaScript, so report them from the server. An after_action with a short-lived thread keeps the response fast. Files in public/ (including the default public/robots.txt) are served without reaching your controllers, so route robots.txt through a controller if you want those hits. Background: AI crawler tracking.

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  after_action :report_crawl

  private

  def report_crawl
    Thread.new(request.path, request.user_agent, request.remote_ip) do |path, ua, ip|
      Net::HTTP.post(
        URI("https://visitrack.app/api/collect-bot"),
        { siteId: ENV.fetch("VISITRACK_SITE_ID"), path: path, userAgent: ua, ip: ip }.to_json,
        "Content-Type" => "application/json"
      )
    rescue StandardError
      nil
    end
  end
end

Script options you might need on Ruby on Rails

All optional — add them to the same tag. Full reference: script configuration.

AttributeWhat it does
data-cookielessStore nothing in the browser; the server derives a daily-rotating id. See cookieless mode.
data-exclude="/app/*"Never track these paths (comma-separated, * wildcard). Checked on every navigation.
data-allow-localTrack on localhost and *.local — for testing an install only.
data-debugLog every event the script sends, and why it skipped one, to the console.
data-auto="false"Turn off automatic events (downloads, contact clicks, form submits, rage clicks, JS errors, 404s).

Ruby on Rails troubleshooting

Pageviews doubled after Turbo visits

The tag is in the <body> (Turbo re-runs body scripts on each visit) or in a second layout. Keep one copy in <head>.

CSP error for visitrack.app

Add the origin to both script_src and connect_src in the initializer, and restart the server.

cookies[:_vt_vid] is nil

The site is in cookieless mode, the tracker didn't load, or the request came from another root domain. In cookieless mode, use your own user id as visitorId.

KeyError: VISITRACK_SITE_ID on boot

ENV.fetch raises when the variable is missing. Set it in production, or use ENV["VISITRACK_SITE_ID"] and skip the tag when it's blank.

Ruby on Rails analytics FAQ

Where do I add an analytics script in Rails?

In the <head> of app/views/layouts/application.html.erb, rendered only when Rails.env.production?. Every view that uses the layout then loads it once.

Does VisitTrack work with Turbo Drive?

Yes. Turbo Drive updates the URL with the History API, which the tracker follows, so each visit is a pageview. Scripts in <head> aren't re-run on Turbo visits, so nothing is double-counted.

How do I allow VisitTrack in the Rails Content-Security-Policy?

Add https://visitrack.app to policy.script_src and policy.connect_src in config/initializers/content_security_policy.rb.

How do I track Devise signups in VisitTrack?

Override Devise's registrations controller, and in the create block send a signup event to /api/track with the _vt_vid cookie as visitorId once the user is persisted.

Can VisitTrack attribute Stripe revenue in a Rails app?

Yes. Pass client_reference_id with the visitor id when creating the Checkout Session, and connect the Stripe webhook in Settings → Revenue.

Keep going

Add VisitTrack to your Ruby on Rails site

Cookie-free analytics with revenue attribution, live visitors, funnels and session replays. One script tag, every feature on every plan. 14 days free, no card required.

Not on Ruby on Rails? See all 29 integration guides.