Measure Measure
Sign In Start Free Trial
← Blog
rails integration tutorial ruby

Privacy-First Analytics for Rails Apps in 2026

by Jules

Rails developers have plenty of analytics options — but most of them involve third-party JavaScript that bloats your bundle, requires consent banners, or sends user data to Google. If you want clean traffic data without the compliance overhead, here’s how to add privacy-first analytics to a Rails app in under 5 minutes.


The One-Line Setup

Add the Measure.events tracking script to your application layout:

<%# app/views/layouts/application.html.erb %>
<head>
  <title>My App</title>
  <%= csrf_meta_tags %>
  <%= csp_meta_tags %>

  <!-- Measure.events — no cookies, no consent required -->
  <script async src="https://lets.measure.events/api/script/YOUR_SITE_KEY"></script>
</head>

Replace YOUR_SITE_KEY with the key from your Measure.events dashboard. That’s the minimum setup — pageviews are now tracked automatically across your entire app.


Turbo and SPA Support

If your Rails app uses Turbo (Hotwire), single-page transitions don’t trigger a full page reload — which means the standard script won’t catch navigation events. Fix it with a Stimulus controller or a Turbo event listener:

// app/javascript/controllers/analytics_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  connect() {
    document.addEventListener("turbo:load", () => {
      if (typeof measure !== 'undefined') {
        measure("pageview")
      }
    })
  }
}

Or more directly in your application.js:

// app/javascript/application.js
document.addEventListener("turbo:load", () => {
  if (window.measure) window.measure("pageview")
})

Tracking Custom Events

Beyond pageviews, you’ll want to track meaningful user actions — signups, button clicks, feature usage:

<!-- Track a signup button click -->
<button data-action="click->analytics#trackSignup">
  Get Started
</button>
// In your Stimulus controller or vanilla JS
trackSignup() {
  measure("signup_click", { plan: "starter" })
}

Or inline for simpler cases:

<%= button_to "Subscribe", subscribe_path,
    data: { action: "click->analytics#track",
            "analytics-event-param": "subscribe_click" } %>

Server-Side Events via the API

For events you can’t track client-side — completed payments, background job completions, webhook events — use the Measure.events HTTP API directly from Ruby:

# lib/measure_events.rb
require 'net/http'
require 'json'

module MeasureEvents
  SITE_KEY = ENV.fetch('MEASURE_SITE_KEY')
  BASE_URL = 'https://lets.measure.events/api/v1'

  def self.track(event_name, properties = {})
    uri = URI("#{BASE_URL}/events")
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true

    request = Net::HTTP::Post.new(uri)
    request['Content-Type'] = 'application/json'
    request['Authorization'] = "Bearer #{ENV.fetch('MEASURE_API_KEY')}"
    request.body = { event: event_name, site_key: SITE_KEY, **properties }.to_json

    http.request(request)
  rescue => e
    Rails.logger.warn "MeasureEvents tracking failed: #{e.message}"
  end
end

Then call it from any model, controller, or job:

# After a successful Stripe webhook
class StripeWebhooksController < ApplicationController
  def create
    case event.type
    when 'checkout.session.completed'
      MeasureEvents.track('subscription_started', plan: params[:plan])
    when 'customer.subscription.deleted'
      MeasureEvents.track('subscription_cancelled')
    end
  end
end

Excluding Admin Traffic

You don’t want your own dashboard visits inflating your numbers. Exclude them cleanly:

<%# app/views/layouts/application.html.erb %>
<% unless current_user&.admin? %>
  <script async src="https://lets.measure.events/api/script/YOUR_SITE_KEY"></script>
<% end %>

Or if you prefer a data attribute approach:

<script async src="https://lets.measure.events/api/script/YOUR_SITE_KEY"
        data-exclude-paths="/admin,/sidekiq"></script>

The MCP Server: Query Your Traffic with AI

This is where Measure.events goes beyond a standard analytics tool.

Add the MCP server to your Cursor or Claude configuration:

{
  "mcpServers": {
    "measure-events": {
      "url": "https://lets.measure.events/mcp",
      "headers": {
        "Authorization": "Bearer your_api_key_here"
      }
    }
  }
}

Now you can ask your AI assistant directly:

“How many signups did we get this week?” “Which blog post is driving the most traffic to /pricing?” “Is /dashboard usage trending up since we shipped the new feature?”

No dashboard tab-switching. No building custom queries. Your Rails app’s traffic data is part of your AI workflow.


GDPR Compliance

Measure.events is fully cookieless — no document.cookie writes, no localStorage tracking, no fingerprinting. There’s nothing to consent to. You can remove your cookie consent banner entirely for analytics purposes (check your jurisdiction, but for most EU sites, cookieless analytics doesn’t require consent).

For Rails apps with a consent management setup, you can conditionally load the script:

<% if user_consented_to_analytics? %>
  <script async src="https://lets.measure.events/api/script/YOUR_SITE_KEY"></script>
<% end %>

Though with a truly cookieless setup, this conditional isn’t strictly necessary.


Get Started

  1. Sign up for a free 14-day trial
  2. Create a site and copy your site key
  3. Add the script tag to app/views/layouts/application.html.erb
  4. Deploy — you’re tracking

The whole setup takes about 5 minutes. The MCP server config takes another 2.

Ready to see accurate analytics?

No cookies. No consent banners. No personal data. $29/mo with a 14-day free trial.

Start free trial →