TempsTemps
  • Docs
  • Blog
  • Pricing
  • Enterprise
  • Security
  • Contact
Star—
TempsTemps

Open-source deployment platform with built-in error tracking, analytics, and monitoring. Runs on any VPS. No surprise bills, no data leaving your infrastructure.

  • Product
  • Features
  • Documentation
  • Changelog
  • Enterprise
  • Contact
  • Resources
  • Getting Started
  • Upgrade
  • GitHub
  • Reddit
  • Tools
  • VPS Security Scanner
  • PaaS Tax Calculator
  • Compare
  • vs Vercel
  • vs Netlify
  • vs Coolify
  • All Platforms
  • Deploy
  • Next.js
  • Node.js
  • Django
  • Laravel
  • Go
  • Rust
  • All Frameworks →
  • Legal & Compliance
  • Security & Trust
  • Data Ownership & Privacy
  • GDPR Compliance

© 2026 Temps. All rights reserved.

GitHubDocs
t
Temps

GA4 Funnels Are Broken and 40% of Users Block the Script — Build Your Own

GA4 Funnels Are Broken and 40% of Users Block the Script — Build Your Own

March 12, 2026 (4mo ago)

Temps Team

Written by Temps Team

Last updated March 12, 2026 (4mo ago)

Free guide

The Observability Stack Cheat Sheet

One page with the exact tools, configs, and integration points to replace Sentry + Plausible + FullStory with self-hosted alternatives.

  • Tool comparison matrix: 6 categories, 18 tools scored
  • Integration architecture diagram (what connects to what)
  • Privacy-first setup that needs zero consent banners
  • Copy-paste configs for the most common stacks

No spam. Unsubscribe anytime. Privacy policy

#analytics#conversion-funnels#google-analytics-alternative#privacy#first-party#conversion funnels without google analytics
Back to all posts

How to Build Conversion Funnels Without Google Analytics

GA4 made funnel tracking harder than it needs to be. Universal Analytics had a straightforward goal funnel — define steps, see drop-offs. GA4 replaced that with custom event configuration, manual exploration reports, and a 72-hour processing delay before results appear. Above 500K events, Google samples your data, and your funnel numbers become estimates rather than facts.

Most developers never configure GA4 funnels at all. Yet knowing exactly where users drop off between landing on your site and completing a purchase is arguably the most valuable metric you can track. A well-built funnel doesn't just show conversion rates — it shows which screen, which form field, which loading spinner is costing you revenue.

This guide covers what conversion funnels actually measure, how to design event schemas for funnel tracking, SQL patterns for computing step-by-step conversion, drop-off visualization, and how first-party analytics can replace GA4's funnel reports entirely.

TL;DR: Conversion funnels track where users drop off between key steps like signup and payment. GA4 funnels require custom event setup, have a 72-hour delay, and sample data above 500K events. You can build your own funnel system with PostgreSQL window functions, or use a platform like Temps that includes funnel analytics out of the box with overall_conversion_rate, per-step drop_off_rate, and average_completion_time_seconds — all retroactive, all unsampled. The average SaaS free-to-paid conversion rate is just 5-7%.


Funnel Analytics Platforms: Temps vs PostHog vs DIY PostgreSQL

CapabilityTempsPostHog (self-hosted)DIY PostgreSQL
Multi-step funnelsYes — built inYesYes (manual SQL)
Per-step drop-off rateYes — automaticYesManual query
Average time per stepYesYesManual query
Retroactive funnelsYesYesYes
Session replay integrationYes — same platformYes (separate plugin)No
Self-host RAM requirementLow (single binary + TimescaleDB)8GB+256MB (DB only)
PriceFree (self-host) / ~$6/mo CloudFree (self-host) / Free tier (Cloud)Infra cost only
Open-source licenseApache 2.0MITN/A
Single binaryYesNoNo

What Is a Conversion Funnel?

A conversion funnel maps the sequential steps users take from first touch to desired outcome. According to FirstPageSage, the average B2B SaaS website converts visitors to leads at just 2.2%. That means 97.8% of visitors leave without taking action — and a funnel tells you exactly where.

The simplest funnel has four steps:

  1. Visit — user lands on your site
  2. Signup — user creates an account
  3. Activation — user completes a key action (first deployment, first project, first integration)
  4. Payment — user converts to a paid plan

Each step has a drop-off rate. If 1,000 people visit, 100 sign up, 40 activate, and 8 pay — your visit-to-signup rate is 10%, signup-to-activation is 40%, and activation-to-payment is 20%. The overall funnel conversion is 0.8%.

Why Drop-Off Matters More Than Conversion Rate

Overall conversion rate is a vanity metric. It tells you the end result but hides where the problem lives. If your visit-to-signup rate is 15% but signup-to-activation is 12%, you don't have a traffic problem. You have an onboarding problem.

Drop-off analysis pinpoints the exact step costing you money. Fix the worst drop-off first — that's where marginal effort produces the largest revenue gain. A 5% improvement at your worst step compounds through every step below it.

What Real Funnel Numbers Look Like

Benchmarks vary by industry, but here's what healthy SaaS funnels typically show:

StepTypical RateGood Rate
Visit → Signup2-5%8-12%
Signup → Activation20-40%50-70%
Activation → Paid15-30%40-60%
Visit → Paid (overall)0.5-2%3-5%

These ranges are compiled from publicly shared benchmarks by Lenny Rachitsky, OpenView Partners, and ProfitWell across 2023-2024 SaaS industry analyses.


Why Are GA4 Funnels So Painful?

GA4's funnel exploration reports require manual event configuration, can't run retroactively on historical data, and sample aggressively once you exceed 500K events. A Databox survey found that 67% of marketers find GA4 harder to use than Universal Analytics. Funnel setup is one of the biggest pain points.

Event Configuration Is a Full-Time Job

In Universal Analytics, goals were simple — define a URL pattern or event category, and you're done. GA4 replaced this with a fully event-driven model. Every funnel step needs a custom event. Every custom event needs parameters. Every parameter needs registration in the GA4 admin panel before it shows up in reports.

Want a checkout funnel? You need to fire begin_checkout, add_shipping_info, add_payment_info, and purchase events — each with specific required parameters like currency, value, and items arrays. Miss one parameter and that step silently breaks.

Data Sampling Destroys Accuracy

GA4 applies thresholding and sampling to exploration reports when your dataset exceeds roughly 500K events. Google shows a small green, yellow, or red shield icon in the corner of the report to indicate data quality — but most users never notice it.

What does sampling mean in practice? Google takes a subset of your data and extrapolates. For a funnel where precision matters — where 3% vs 5% conversion is the difference between a viable business and a failed one — sampling makes the numbers unreliable. You're making decisions based on guesses, not data.

The 72-Hour Delay

GA4 exploration reports can take up to 72 hours to process data. Standard reports are faster (24-48 hours), but funnel explorations sit in the "exploration" category with lower processing priority.

Push a change to fix a checkout drop-off and you won't know if it worked until three days later. That feedback loop is too slow for iterative product work.

No Retroactive Funnels

In GA4, if you didn't configure the events before users triggered them, the data doesn't exist. You can't go back and create a funnel for events you weren't tracking. Universal Analytics had the same limitation, but it hurt less because event setup was simpler.

This forces you to predict which funnels you'll need before you have the data to know which ones matter. It's backwards. You should define a funnel after you notice a problem, not before.


Free guide

The Observability Stack Cheat Sheet

One page with the exact tools, configs, and integration points to replace Sentry + Plausible + FullStory with self-hosted alternatives.

  • Tool comparison matrix: 6 categories, 18 tools scored
  • Integration architecture diagram (what connects to what)
  • Privacy-first setup that needs zero consent banners
  • Copy-paste configs for the most common stacks

No spam. Unsubscribe anytime. Privacy policy

What Data Model Do Conversion Funnels Need?

Building your own funnel system starts with one table: an events table with a user identifier, event name, timestamp, and optional properties. According to the Stack Overflow Developer Survey, PostgreSQL is used by 49% of professional developers, making it the most practical choice for a funnel backend.

The Events Table

Here's the minimum schema:

CREATE TABLE events (
  id          BIGSERIAL PRIMARY KEY,
  user_id     TEXT NOT NULL,
  event_name  TEXT NOT NULL,
  timestamp   TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  properties  JSONB DEFAULT '{}'
);

CREATE INDEX idx_events_user_time
  ON events (user_id, timestamp);
CREATE INDEX idx_events_name
  ON events (event_name);

That user_id can be an authenticated user ID, an anonymous session token, or a fingerprint hash — whatever identifier you use to track a single person across steps. The properties column holds arbitrary metadata: page URL, plan name, referral source, anything you might want to filter on later.

The Funnel Query Logic

A funnel query answers one question: how many users completed step N within a time window after completing step N-1? The SQL pattern uses CTEs to order events per user, then filters for sequential completion.

Here's the core query for a three-step funnel (visit, signup, payment):

WITH step_1 AS (
  SELECT DISTINCT ON (user_id)
    user_id, timestamp AS step_1_at
  FROM events
  WHERE event_name = 'page_view'
    AND timestamp >= NOW() - INTERVAL '30 days'
  ORDER BY user_id, timestamp
),
step_2 AS (
  SELECT DISTINCT ON (s1.user_id)
    s1.user_id, e.timestamp AS step_2_at
  FROM step_1 s1
  JOIN events e ON e.user_id = s1.user_id
    AND e.event_name = 'signup'
    AND e.timestamp > s1.step_1_at
    AND e.timestamp < s1.step_1_at + INTERVAL '7 days'
  ORDER BY s1.user_id, e.timestamp
),
step_3 AS (
  SELECT DISTINCT ON (s2.user_id)
    s2.user_id, e.timestamp AS step_3_at
  FROM step_2 s2
  JOIN events e ON e.user_id = s2.user_id
    AND e.event_name = 'payment'
    AND e.timestamp > s2.step_2_at
    AND e.timestamp < s2.step_2_at + INTERVAL '30 days'
  ORDER BY s2.user_id, e.timestamp
)
SELECT
  (SELECT COUNT(*) FROM step_1) AS step_1_count,
  (SELECT COUNT(*) FROM step_2) AS step_2_count,
  (SELECT COUNT(*) FROM step_3) AS step_3_count;

Each CTE filters for users who completed the previous step and then completed the current step within a defined time window. The time window prevents counting a user who signed up a year ago and paid today as a single funnel conversion.

Most funnel implementations skip time-window constraints between steps. Without them, your funnel data becomes meaningless at scale — a user who visited in January and paid in December inflates your conversion rate even though they represent a completely different decision journey.

Why Time Windows Matter

Without time constraints, you're measuring "did this user ever do both things?" rather than "did this user progress through a coherent journey?" A 7-day window between visit and signup is reasonable. A 30-day window between signup and payment fits most SaaS trial periods.

Adjust these windows based on your sales cycle. B2B products with longer evaluation periods might need 60-90 day windows. Consumer products with impulse-buy patterns might use 24-hour windows.


How Do You Build a DIY Funnel System?

A working funnel system needs three components: an event tracking endpoint, storage in PostgreSQL, and a query layer that computes step-by-step conversion. According to Stripe's checkout benchmarks, reducing checkout steps from 5 to 3 increases completion rates by 35% — proof that funnel analysis directly drives revenue decisions.

Step 1: Event Collection Endpoint

Here's a minimal Express.js endpoint that accepts events:

const express = require('express');
const { Pool } = require('pg');

const pool = new Pool({
  connectionString: process.env.DATABASE_URL
});

const app = express();
app.use(express.json());

app.post('/api/events', async (req, res) => {
  const { user_id, event_name, properties } = req.body;

  if (!user_id || !event_name) {
    return res.status(400).json({ error: 'Missing required fields' });
  }

  await pool.query(
    `INSERT INTO events (user_id, event_name, properties)
     VALUES ($1, $2, $3)`,
    [user_id, event_name, JSON.stringify(properties || {})]
  );

  res.status(201).json({ ok: true });
});

app.listen(3001);

Keep this endpoint fast. Event tracking should never block the user experience. Insert and return immediately — do any enrichment (geolocation, device detection) asynchronously.

Step 2: Client-Side Event Firing

Track events from your frontend with simple fetch calls:

function trackEvent(eventName, properties = {}) {
  const userId = getUserId(); // from auth or anonymous cookie

  navigator.sendBeacon('/api/events', JSON.stringify({
    user_id: userId,
    event_name: eventName,
    properties
  }));
}

// Usage
trackEvent('page_view', { path: window.location.pathname });
trackEvent('signup', { method: 'github' });
trackEvent('onboarding_complete', { steps_skipped: 0 });
trackEvent('payment', { plan: 'pro', amount: 19 });

Using sendBeacon instead of fetch ensures the event fires even if the user navigates away. It's non-blocking and survives page transitions.

Step 3: Funnel Query Endpoint

Wrap the SQL from the previous section in an API endpoint:

app.post('/api/funnel', async (req, res) => {
  const { steps, window_days = 30 } = req.body;
  // steps: ['page_view', 'signup', 'payment']

  const ctes = steps.map((step, i) => {
    if (i === 0) {
      return `step_${i} AS (
        SELECT DISTINCT ON (user_id) user_id, timestamp AS t
        FROM events
        WHERE event_name = '${step}'
          AND timestamp >= NOW() - INTERVAL '${window_days} days'
        ORDER BY user_id, timestamp
      )`;
    }
    return `step_${i} AS (
      SELECT DISTINCT ON (s.user_id) s.user_id, e.timestamp AS t
      FROM step_${i - 1} s
      JOIN events e ON e.user_id = s.user_id
        AND e.event_name = '${step}'
        AND e.timestamp > s.t
        AND e.timestamp < s.t + INTERVAL '${window_days} days'
      ORDER BY s.user_id, e.timestamp
    )`;
  });

  const counts = steps.map((_, i) =>
    `(SELECT COUNT(*) FROM step_${i}) AS step_${i}_count`
  ).join(', ');

  const query = `WITH ${ctes.join(', ')} SELECT ${counts}`;

  const result = await pool.query(query);
  res.json(result.rows[0]);
});

Warning: This example concatenates user input into SQL for clarity. In production, use parameterized queries or a query builder to prevent SQL injection.

Step 4: Drop-Off Visualization

A funnel visualization doesn't need a charting library. Horizontal bars with CSS work fine to start:

<div class="funnel">
  <div class="step" style="width: 100%">
    <span>Page View: 10,000</span>
  </div>
  <div class="step" style="width: 12%">
    <span>Signup: 1,200 (12%)</span>
  </div>
  <div class="step" style="width: 4.8%">
    <span>Activation: 480 (40%)</span>
  </div>
  <div class="step" style="width: 1.4%">
    <span>Payment: 144 (30%)</span>
  </div>
</div>

We've found that showing the percentage relative to the previous step (not the first step) makes drop-off analysis more actionable. A "30% activation-to-payment" tells you more than a "1.4% overall conversion" when you're deciding what to fix next.


Free guide

The Observability Stack Cheat Sheet

One page with the exact tools, configs, and integration points to replace Sentry + Plausible + FullStory with self-hosted alternatives.

  • Tool comparison matrix: 6 categories, 18 tools scored
  • Integration architecture diagram (what connects to what)
  • Privacy-first setup that needs zero consent banners
  • Copy-paste configs for the most common stacks

No spam. Unsubscribe anytime. Privacy policy

How Do You Compare Cohorts Inside a Funnel?

Cohort comparison reveals whether changes actually improve conversion — or just shift the numbers around. According to Eppo's research, companies running A/B tests on their onboarding funnels see a median 12% lift in activation rates compared to those optimizing without experiments. Testing against cohorts is what separates opinion from evidence.

Splitting Funnels by Cohort

Add a cohort filter to your funnel query to compare groups. The most common splits are:

  • Time cohorts — users who signed up this week vs last week
  • Source cohorts — organic vs paid traffic
  • Feature cohorts — users who saw variant A vs variant B
  • Plan cohorts — free tier vs trial users

The SQL is the same funnel query with a WHERE clause on properties:

-- Compare funnel conversion for organic vs paid users
WITH step_1 AS (
  SELECT DISTINCT ON (user_id)
    user_id, timestamp AS step_1_at,
    properties->>'utm_source' AS source
  FROM events
  WHERE event_name = 'page_view'
    AND timestamp >= NOW() - INTERVAL '30 days'
  ORDER BY user_id, timestamp
),
-- ... remaining steps same as before ...
SELECT
  source,
  COUNT(*) FILTER (WHERE step >= 1) AS visited,
  COUNT(*) FILTER (WHERE step >= 2) AS signed_up,
  COUNT(*) FILTER (WHERE step >= 3) AS paid
FROM funnel_users
GROUP BY source;

Integrating A/B Tests with Funnels

If you're running A/B tests, store the variant assignment as an event property. When a user enters the experiment, fire an event like experiment_assigned with { experiment: 'onboarding_v2', variant: 'control' }. Then filter your funnel by that property.

This gives you something GA4 can't do natively — funnel conversion rates broken down by experiment variant, computed on raw unsampled data, available immediately. No waiting 72 hours. No data sampling. No separate experimentation tool.


What Open-Source Funnel Tools Exist?

PostHog, Plausible, and Umami are the three main open-source analytics platforms, but they differ dramatically in funnel support. PostHog's community data reported 70,000+ deployments worldwide, making it the most widely deployed open-source analytics tool — but also the heaviest.

PostHog: Full Funnels, Heavy Infrastructure

PostHog is the closest open-source equivalent to Mixpanel or Amplitude. It offers multi-step funnels, funnel breakdowns by property, conversion time analysis, and correlation analysis to identify what properties predict conversion.

The catch? Self-hosting PostHog requires PostgreSQL, ClickHouse, Redis, Kafka, and the PostHog application itself. The recommended minimum is 8GB RAM and 4 CPU cores. That's a real server — not a $6/month VPS.

PostHog Cloud offers a generous free tier (1 million events/month), but then you're sending data to a third party again.

Plausible: Goals, Not Funnels

Plausible tracks goals — page visits and custom events that you define as conversions. You can see how many users completed each goal, but you can't build multi-step sequential funnels. There's no "show me users who did A, then B, then C in order."

Plausible's strength is simplicity. It runs on Elixir and ClickHouse, needs about 1GB of RAM self-hosted, and gives you clean dashboards with zero configuration. But if you need funnel analysis, it's the wrong tool.

Umami: Clean Analytics, No Funnels

Umami is a lightweight, privacy-focused analytics tool built on Node.js and PostgreSQL (or MySQL). It tracks page views, referrers, devices, and custom events. Like Plausible, it doesn't support sequential funnel analysis.

Umami runs on minimal resources — 512MB RAM is plenty. But funnel queries would need to be written manually against the raw events table.

How They Compare

FeatureTempsPostHogPlausibleUmami
Multi-step funnelsYesYesNoNo
Funnel breakdownsYesYesNoNo
Conversion time analysisYes (average_completion_time_seconds)YesNoNo
Per-step drop-off rateYes (automatic)YesNoNo
Session replay built inYesYes (plugin)NoNo
Self-host RAM requirementLow (single binary + TimescaleDB)8GB+1GB512MB
Single-binary installYesNoNoNo
Custom event trackingYesYesYesYes
Retroactive funnelsYesYesN/AN/A
LicenseApache 2.0MITAGPLMIT

Why Does First-Party Tracking Produce Better Funnel Data?

First-party analytics capture 30-40% more events than third-party scripts because ad blockers don't filter them. A study by Plausible found that sites running both GA4 and first-party analytics simultaneously saw 26-40% higher page view counts in the first-party tool. That gap ripples through every funnel step.

Ad Blockers Create Invisible Holes in Your Funnel

According to Backlinko, 42% of internet users run ad blockers. Among developer and tech audiences, that number climbs higher. Every one of those users is invisible to Google Analytics.

That's not a rounding error. If 42% of your top-of-funnel visitors are missing, your step-1 count is wrong. Every downstream conversion rate is calculated against a deflated denominator. Your real signup rate might be 3% when GA4 reports 5% — because GA4 never counted the blocked visitors.

Cookie Consent Reduces Your Data Further

Under GDPR, you need explicit consent before loading GA4. Cookie consent banners reduce opt-in rates to 30-50% in most implementations. Stack that on top of the 42% ad blocker rate, and GA4 might be capturing less than a third of your actual traffic in some European markets.

Temps stores visitor and session identifiers in localStorage on your own domain — no third-party cookies, no cross-origin tracking. Since the analytics endpoint is served from your own domain via the basePath="/api/_temps" proxy, the requests are indistinguishable from normal first-party API calls and bypass ad-blocker heuristics that target known analytics domains and third-party cookie patterns.

We've seen projects where switching from GA4 to first-party tracking nearly doubled the recorded event volume — not because traffic grew, but because events that were previously blocked or rejected started appearing in the data.


How Does Temps Handle Conversion Funnels?

Temps includes funnel analytics as part of its built-in observability layer — the same infrastructure that handles web analytics, session replay, and error tracking. Since Temps already stores every page view and custom event in TimescaleDB, funnels are a query on data you're already collecting. No additional setup, no separate analytics stack, no extra cost.

Three quotable claims:

Retroactive funnels: Define a funnel today against events from three months ago. Temps stores raw events, not pre-aggregated data, so you can build any funnel against your entire history without reconfiguring anything.

Automatic drop-off flagging: The API returns drop_off_rate per step and overall_conversion_rate for the full funnel. The dashboard highlights the step with the highest drop-off so you don't have to scan the table manually.

Single-platform correlation: Funnels, session replay, and error tracking share the same event store. When you identify a drop-off step, you can pull session replays filtered to users who dropped off at that exact step — without exporting data to a second tool.

The reason most deployment platforms don't include analytics is historical — hosting and analytics evolved as separate product categories. But they share the same infrastructure: an HTTP endpoint, a database, and a server. Combining them eliminates an entire class of integration work that developers accept as normal.

Defining Funnel Steps

In the Temps dashboard, you define funnel steps by selecting from events your application already tracks. No code changes required. Pick the events, set the time window, and the funnel renders immediately.

Because Temps stores raw events rather than pre-aggregated data, funnels are retroactive. Define a new funnel today and see conversion data from last month. This is the opposite of GA4's approach, where events must be configured before they occur.

Sending Events from a React App

Install the SDK and wrap your app with TempsAnalyticsProvider. The useTrackEvent hook sends any named event to Temps:

import { TempsAnalyticsProvider, useTrackEvent } from '@temps-sdk/react-analytics';

// In your root layout
export default function Layout({ children }) {
  return (
    <TempsAnalyticsProvider
      domain="yourdomain.com"
      basePath="/api/_temps"
      enableSessionRecording={true}
    >
      {children}
    </TempsAnalyticsProvider>
  );
}

// In a component
function CheckoutButton() {
  const trackEvent = useTrackEvent();

  function handleClick() {
    trackEvent('begin_checkout', { plan: 'pro', source: 'pricing_page' });
  }

  return <button onClick={handleClick}>Start checkout</button>;
}

Each trackEvent call becomes a row in Temps' event store. Build a funnel with steps page_view → begin_checkout → payment_complete and Temps calculates the conversion rate, per-step drop-off rate, and average time to complete for each step.

Automatic Drop-Off Calculation

Temps calculates drop-off between each step and highlights the largest drop-off in the funnel. If your signup-to-activation rate is 18% while your activation-to-payment rate is 55%, Temps flags the signup-to-activation step as your bottleneck.

The API response includes step_conversions, where each step carries conversion_rate, drop_off_rate, and average_time_to_complete_seconds. The top-level overall_conversion_rate and average_completion_time_seconds give you the funnel's headline numbers.

Combining Funnels with Session Replay

Here's where an integrated platform pays off. When you identify a drop-off — say, 60% of users abandon the onboarding step — you can watch session replays of users who dropped off at that exact step. You don't need to correlate IDs between Mixpanel and FullStory. It's the same system.

Did users rage-click on a broken button? Did they scroll past the CTA without seeing it? Did a JavaScript error crash the form? Session replay answers these questions in ways that funnel numbers alone never can.


Free guide

The Observability Stack Cheat Sheet

One page with the exact tools, configs, and integration points to replace Sentry + Plausible + FullStory with self-hosted alternatives.

  • Tool comparison matrix: 6 categories, 18 tools scored
  • Integration architecture diagram (what connects to what)
  • Privacy-first setup that needs zero consent banners
  • Copy-paste configs for the most common stacks

No spam. Unsubscribe anytime. Privacy policy

FAQ

How do you build conversion funnels without Google Analytics?

The three main approaches: (1) DIY with PostgreSQL — create an events table, fire events from your frontend, and query sequential completion with CTEs and time windows; (2) PostHog self-hosted — full funnel support but requires 8GB+ RAM; (3) Temps — funnel analytics built into the deployment platform, runs on ~512MB, Apache 2.0, free to self-host or ~$6/month on Temps Cloud.

Each approach avoids GA4's 72-hour delay and 500K-event sampling limit. First-party storage means you own 100% of your data and funnels run on actual event counts, not Google's statistical estimates.

How many funnel steps should you track?

Keep funnels between 3 and 7 steps. Fewer than 3 doesn't give you enough resolution to find bottlenecks. More than 7 creates noise — every step adds a conversion rate to monitor, and the numbers get too small to be statistically meaningful. Stripe's checkout research shows that 3-step checkouts outperform 5-step ones by 35%, which suggests simpler journeys convert better anyway.

Can you build funnels without user authentication?

Yes. Anonymous identifiers work — a random UUID stored in a first-party cookie or localStorage. The identifier just needs to persist across page views within a session. For cross-session funnels (visit today, sign up tomorrow), you need an identifier that survives browser restarts, which typically means a cookie with a 30-day expiry. Authenticated user IDs are more reliable, but anonymous tracking covers most funnel use cases.

What's a good conversion rate for SaaS funnels?

It depends on the step. Visit-to-signup rates of 2-5% are average; 8-12% is strong. Free-to-paid conversion rates average 5-7% for freemium models and 10-15% for free trial models, according to Lenny Rachitsky's analysis of 50+ SaaS companies. Don't compare your overall funnel rate to someone else's — compare each step to its benchmark independently.

Do funnels work for mobile apps?

Yes. The same event-based model applies. Fire events from your mobile app to the same collection endpoint and run the same funnel queries. The key difference is session handling — mobile apps have background/foreground states rather than page navigations, so your "session start" event might be an app-open event rather than a page view. The funnel query logic stays identical.

Is Temps funnel analytics free?

Yes. Temps is Apache 2.0 — self-host for free on your own server. Temps Cloud (managed hosting) runs at approximately $6/month (Hetzner infrastructure cost plus 30% margin), with no per-seat fees, no per-event fees, and no bandwidth billing. Funnel analytics, session replay, error tracking, and uptime monitoring are all included.


Stop Guessing Where Users Drop Off

Conversion funnels aren't complicated. They're a sequence of counts: how many users did step one, how many of those did step two, and so on. The hard part has always been the tooling — GA4 makes it needlessly complex, and self-hosted alternatives either lack the feature or require a heavy infrastructure stack.

You have three realistic options. Build it yourself with PostgreSQL and the SQL patterns from this guide — it works and you own every piece. Use PostHog if you want a full-featured open-source analytics suite and don't mind the 8GB+ RAM requirement. Or use a deployment platform like Temps that includes funnel analytics alongside web analytics, session replay, and error tracking without adding another service to your stack.

The metric that matters isn't your overall conversion rate. It's the drop-off at your worst funnel step. Find that step, watch the session replays, fix the problem, and measure again. That loop — funnel, replay, fix, re-measure — is how you systematically grow revenue.

curl -fsSL https://temps.sh/install.sh | bash