WP Manifestindependent plugin directory
manifest / analytics / bol-split-testing

BOL Split Testing

A/B split testing tool to randomly route visitors between the WordPress homepage and the payments app, tracking results.

by George Stephanis / Big Orange Lab · github.com/bigorangelab/bol-split-testing

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/bigorangelab/bol-split-testing/archive/refs/heads/trunk.zip

BOL Split Testing

BOL Split Testing

A single-file WordPress plugin that A/B tests the site homepage against an external payments app, splitting visitors by a configurable percentage and tracking display/redirect counts.

What it does

Visitors landing on the homepage are randomly assigned to one of two groups:

  • Group A (Displays) — stays on the WordPress homepage.
  • Group B (Redirects) — gets redirected (HTTP 307) to a configured payments app URL (e.g. https://payments.example.com).

The assignment is decided once per visitor and pinned for 7 days via the bol_landing_variant cookie, so a given visitor always sees the same variant for the duration of the test. Search engine crawlers and logged-in administrators are always excluded from the test and simply see the homepage.

Routing methods

The plugin supports two mutually exclusive routing strategies, selectable from the settings page:

  • Client-side (JS Hybrid) — recommended. An inline script in wp_head decides the variant in the browser and redirects via window.location.replace(). This works transparently with WP Engine's server-side page caching, since the cached PHP response never changes — only the browser branches.
  • Server-side (PHP). template_redirect decides and redirects server-side. This requires a WP Engine cache exclusion rule for / — without it, cached responses will serve stale redirect decisions to visitors.

Both methods share the same decision logic (bypass checks, cookie name/lifetime, percentage roll) implemented independently in PHP and JS.

Decision flow

Both routing methods walk through the same sequence of checks before ever touching the roll or the cookie. The PHP path (bol_server_side_redirect()) runs this on every request via template_redirect; the JS path (bol_client_side_script() + assets/js/frontend.js) runs the bypass checks in PHP at enqueue time (so bypassed visitors never even receive the script), then re-derives the rest in the browser against a page that may be cached indefinitely.

flowchart TD
    A[Request to front-end homepage] --> B{Is test active?<br/>bol_is_test_active}
    B -- No --> Z[Show normal homepage<br/>no cookie, no tracking]
    B -- Yes --> C{Bypass query param<br/>present? e.g. ?bypass_ab=1}
    C -- Yes --> Z
    C -- No --> D{Privileged role bypass?<br/>bol_should_bypass_for_role<br/>Editor+, or Contributor+ if enabled<br/>skipped entirely at 100%}
    D -- Yes --> Z
    D -- No --> E{Known search bot UA?<br/>bol_is_search_bot}
    E -- Yes --> Z
    E -- No --> F{bol_landing_variant<br/>cookie already set?}
    F -- "redirect" --> G[307 redirect to target URL<br/>no re-roll, no re-count]
    F -- "keep" --> H[Show homepage<br/>no re-roll, no re-count]
    F -- Not set --> I[Roll: random 0-99 < percentage?]
    I -- Yes --> J[Set cookie = redirect, 7 days<br/>increment bol_count_redirects<br/>307 redirect to target URL]
    I -- No --> K[Set cookie = keep, 7 days<br/>increment bol_count_displays<br/>show homepage]

    subgraph PHP["Server-side PHP method"]
        direction TB
        P1[Everything above runs inline<br/>on template_redirect, every request]
    end

    subgraph JS["JS Hybrid method"]
        direction TB
        J1[B–E run in PHP at wp_enqueue_scripts<br/>bypassed visitors never get the script]
        J2[bolFrontendData localized:<br/>percentage, target/track/redirect URLs, schedule]
        J3[frontend.js re-checks schedule<br/>if Conditionally Enabled, then F-K<br/>in the browser, against a cached page]
        J4[keep: fetch POST /track<br/>redirect: navigate to<br/>/redirect-and-track uncached REST route]
        J1 --> J2 --> J3 --> J4
    end

A few things the diagram compresses:

  • "Is test active?" folds in the tri-state bol_status option and, for "Conditionally Enabled," the full schedule check (date range, day-of-week, time-of-day window, evaluated in the site's timezone). For the JS Hybrid method this check is re-run in the browser on every pageview (see isScheduleActive() in assets/js/frontend.js), because the enqueued script itself lives on a page that may be served from cache — PHP can't be the one deciding "is now inside the window" once and bake that into the cached HTML.
  • Bypassed visitors are never counted — the display/redirect tallies only increment inside the roll step (I), so returning early at any of BE skips tracking entirely.
  • The cookie is the only state that persists a decision. There's no server-side lookup keyed by IP or user — once bol_landing_variant is set to redirect or keep, that decision short-circuits everything downstream of F for the next 7 days, in both PHP and JS.
  • PHP and JS intentionally duplicate this logic rather than sharing code, since one runs server-side per-request and the other runs client-side against a cached response — see CLAUDE.md for why, and keep both in sync when changing the rules.

Settings

Configurable from Tools → BOL Split Testing in wp-admin:

Option Description
Enable Split Test Tri-state: Disabled, Enabled, or Conditionally Enabled.
Schedule (Conditionally Enabled only) Start/end date-time, active days of the week, and an active time-of-day window (site timezone) — see below.
Redirect Target URL Destination URL for Group B.
Redirect Split Percentage Percent (0–100) of visitors redirected to the target.
Routing Method js_hybrid (default) or php.
Bypass Query Parameter Query key (default bypass_ab) that skips the test entirely, e.g. /?bypass_ab=1.
Exclude Logged-In Users Checkbox. Editors and above are always bypassed (never redirected/counted, shown an admin-bar link instead); enabling this extends that bypass down to Contributors and Authors. Ignored — everyone is redirected — when Redirect Split Percentage is 100.

The same page displays running totals of displays vs. redirects, with a button to reset the counters.

Conditional scheduling

When Enable Split Test is set to "Conditionally Enabled," the test only runs during a configured recurring window: an optional start/end date-time, a set of active days of the week, and an active time-of-day range (a start time later than the end time is treated as spanning midnight). All schedule times are evaluated in the site's configured timezone, which is restated on the settings page; the admin UI also flags if your browser's timezone differs from the site's, since schedule times are not adjusted for the viewer. A read-only weekly calendar preview on the settings page visualizes the configured active windows across all 7 days.

Because the recommended JS Hybrid routing method relies on a page-cached `