This tutorial applies to self-hosted WordPress sitesThis tutorial applies to self-hosted WordPress sites on wordpress.org. Sites hosted on wordpress.com do not provide PHP execution access and cannot run the LaunchDarkly PHP SDK. For those environments, use the JavaScript SDK with client-side flag evaluation only.
Prerequisites
To complete this tutorial, you should have:- A self-hosted WordPress installation with PHP 8.1 or higher
- Composer available in your WordPress environment
- A LaunchDarkly account with at least one project and environment configured
- Access to your WordPress theme files or a custom plugin
Concepts
Before you read this tutorial, you should understand the following:- Feature flags are the foundation of LaunchDarkly. A feature flag controls whether a code path, UI element, or experience is active for a given user or group of users. Flags can be boolean (on/off) or multivariate (returning strings, numbers, or JSON). Everything in LaunchDarkly, including experiments, is built on top of flags.
- Experimentation uses flags to run A/B and multivariate tests. An experiment assigns users to variants through a flag’s targeting rules, then measures the effect on defined metrics. Experimentation requires flag evaluation to be consistent per user and per session. The same user must always see the same variant for the duration of the test.
- Server-side SDKs evaluate flags on the server before the page is rendered and sent to the browser. This eliminates client-side performance concerns like cold start delays and flicker.
- Client-side SDKs evaluate flags in the browser after the page loads. They are simpler to set up but require additional care to avoid flicker in experimentation scenarios.
- Bootstrapping is a technique where a server-side SDK pre-evaluates all flags for a given user and injects the results into the HTML response. The client-side SDK then initializes instantly using those values, with no additional network request.
- Contexts are the objects LaunchDarkly uses to evaluate flags and record experiment data. A context has a
kind(usuallyuser) and akeythat uniquely identifies the entity being evaluated.
Choose your approach
There are two main approaches to integrating LaunchDarkly with WordPress. Your choice depends on your WordPress setup and requirements.| PHP SDK (server-side) | JS SDK only (client-side) | |
|---|---|---|
| Requires | PHP access, Composer | Script tag in |
| Flicker risk | None | Yes, without mitigation |
| Cold start delay | None | ~100–200ms on first load |
| Works with full-page caching | With hybrid pattern (see below) | Yes |
| Best for | Performance-sensitive pages, SEO-critical pages | Simple setups, WordPress.com |
Option 1: PHP SDK with bootstrapping (recommended)
This approach evaluates flags server-side in PHP and injects the results into the page before it reaches the browser. The JavaScript SDK initializes instantly from those pre-evaluated values.Step 1: Install the PHP SDK
From your WordPress root or theme directory, install the SDK using Composer:functions.php:
apcu PHP extension to be installed and enabled.
Step 2: Evaluate flags in PHP and inject bootstrap data
Add the following to your theme’sfunctions.php. This function evaluates all client-side flags for the current user and outputs them as an inline script that the JavaScript SDK can read on initialization.
PHP performanceBy default, the PHP SDK caches flag data in memory per process. This is appropriate for low-to-medium traffic environments. For higher-traffic sites, you can configure the SDK’s HTTP cache to use Memcached or Redis using the
cache option, or use the Relay Proxy in daemon mode which is LaunchDarkly’s recommended approach for high-throughput PHP environments.Step 3: Initialize the JavaScript SDK with bootstrap data
In your theme or page template, initialize the JavaScript SDK using the injected bootstrap values. The SDK will be immediately ready with no network fetch required. Register the SDK script alongside your other theme assets. We recommend self-hosting the SDK file rather than loading it from a third-party CDN like unpkg or jsDelivr, which we do not support for production use. To learn more, read the JavaScript SDK reference.Handling caching
Full-page caching (WP Rocket, W3 Total Cache, Nginx FastCGI, Cloudflare, and similar caching layers) stores a rendered HTML snapshot and serves it to all subsequent visitors. This means PHP does not re-execute per user, and the same evaluated flag values are served to everyone. This is not a LaunchDarkly-specific limitation. It affects any server-side A/B testing tool running on a cached WordPress page. There are two approaches to resolve this.Approach A: Hybrid cookie pattern (recommended)
This pattern keeps full-page caching enabled for the majority of traffic while ensuring each user receives a correctly bucketed, consistent variant. How it works:- New visitors (with no variant cookie) bypass the cache and hit PHP, which evaluates the flag and sets a per-user cookie
- The caching layer is configured to serve cached pages only when the variant cookie is present
- On subsequent visits, the cached page loads and the JavaScript SDK reads the variant from the cookie with no PHP execution, no network call, no flicker
Exposure events in the cookie patternThe PHP
variation() call that sets the cookie records the experiment exposure server-side, so subsequent cached page loads do not need to fire another exposure event from JavaScript. Use the bootstrapped variant to gate your UI, and use ldClient.track() to record conversion events on the same context key. Avoid passing the full allFlagsState() payload through the cookie, since that bootstrap form is intended to be regenerated per-request by the server and grows with your client-side flag count. To learn more, read Bootstrapping.Cache bypass configuration
Configure your caching layer to bypass cache when the variant cookie is absent, for example, new visitors who need a fresh PHP evaluation:| Caching layer | Configuration |
|---|---|
| WP Rocket | Settings → Advanced Rules → Never Cache Cookies → add ld_visitor_id |
| W3 Total Cache | Page Cache → Rejected Cookies → add ld_visitor_id |
| Nginx FastCGI | |
| Cloudflare | Cache Rules: bypass cache for requests that have no ld_variant_* cookie so PHP can assign a variant. Exact configuration depends on your Cloudflare plan. On Pro and above, use Cache Rules with an expression such as
not http.cookie contains “ld_variant_” and a Bypass cache action. On Free, you may need to use a Worker or Page Rules to achieve the same effect. |
Approach B: Exclude experiment pages from caching
It is also possible to disable caching entirely on pages where an experiment is running. This is the simplest configuration change but means those pages lose the performance benefit of full-page caching. We generally do not recommend this approach for SEO-critical or high-traffic landing pages where cache performance is a requirement. It may be appropriate for lower-traffic pages where cache performance is less important.Option 2: JavaScript SDK only (client-side)
If you are unable to modify PHP or are running on a platform where PHP execution is restricted, you can use the JavaScript SDK alone. Self-host the SDK file alongside your other JavaScript resources and load it from your theme’s<head> using wp_enqueue_script().
Mitigating flicker in client-side mode
When the SDK loads asynchronously, the page renders before flag values are available. Users may briefly see the control variant before the treatment is applied. This is called “flicker.” To prevent flicker, hide the element under test until the SDK is ready, then reveal it:async or defer improves page load metrics but makes flicker worse, because the page renders before the script executes. If you use async/defer, the CSS hide pattern above becomes essential. The PHP bootstrap approach in Option 1 eliminates this tradeoff entirely.
By default, the JavaScript SDK fetches flags from LaunchDarkly on every page load, so the ~100–200ms cold start applies to every initialization. To cache flag values across visits, pass bootstrap: 'localStorage' to LDClient.initialize(). With this enabled, after the first visit the SDK reads cached values from localStorage and emits the ready event immediately, while still fetching the latest values in the background. Note that this can introduce stale-value flicker if flags change between visits, and that first-time visitors still see the full cold start. The PHP bootstrap approach in Option 1 avoids both tradeoffs.
Feature flags in WordPress
Feature flags let you control which users see which experiences without code deployments. Common use cases in WordPress include:- Gradual rollouts: Roll out a new page template, content block, or UI component to a percentage of visitors before releasing to everyone
- Targeted experiences: Serve specific content to users based on attributes like country, logged-in status, or subscription tier
- Kill switches: Instantly disable a feature if problems arise, without a deployment
- Beta access: Expose new features only to specific users or groups
Example: show a new hero section to 20% of visitors
In your WordPress template, add the following:Experimentation in WordPress
Experimentation builds on feature flags to run controlled A/B tests and measure the effect of changes on defined metrics. The typical WordPress experimentation flow involves the landing page (WordPress), downstream conversion pages (often React or other front-end apps), and a metric like signup or purchase completion.Setting up a simple experiment
To set up an experiment:- Create a multivariate flag: In the LaunchDarkly UI, create a string flag with two variations, for example,
controlandtreatment. Enable client-side SDK access on the flag. - Create a metric: Create a custom event metric in LaunchDarkly. For example, a metric named “signup-completed” tied to an event key of
signup-completed. Set the metric definition to Occurrence (Percent) (did this event happen for this user at all?). - Create the experiment: Create an experiment, attach the flag, select the metric, and configure the traffic allocation. Set the targeting rule to evaluate the flag for all users or a specific segment, then start the experiment.
- Evaluate the flag on the WordPress landing page (LP):
- Track the conversion event from your downstream app: When the user completes the goal action (for example, signup), fire a
track()call from wherever that event occurs, whether that’s the WordPress page itself or a downstream React application:
track() event to the experiment exposure using the same context key. As long as the same userId or visitorId is used consistently across your WordPress LP and downstream apps, attribution will be correct.
Passing variant decisions to downstream apps
If your WordPress LP is the entry point but conversion happens in a React app on the same domain, you can persist the variant assignment in a cookie and read it in the downstream app without re-initializing LaunchDarkly:Anonymous visitor to authenticated user
When a user arrives anonymously and later signs up or logs in, you can link their pre-login experiment exposure to their authenticated identity using a multi-context identify call. This ensures experiment attribution is preserved across the anonymous and authenticated states. At the moment of login or signup:Anonymous vs tracked visitor contextsMarking the
visitor sub-context as anonymous: true is recommended for most A/B testing scenarios because it reduces your monthly active user (MAU) consumption and excludes pre-login traffic from the contexts list. If you instead want to individually target specific anonymous visitors, audit which variations each visitor saw, or preserve pre-login attributes as a durable record on the contexts list, omit anonymous: true so the visitor is tracked. To learn more, read Anonymous contexts.Note also that visitor is a custom context kind defined by your application. If you use this kind, use the same kind name consistently across your application and in any experiment’s randomization unit configuration. To learn more, read Contexts.Using the Relay Proxy in daemon mode
For WordPress sites with significant traffic, configure the PHP SDK to use the LaunchDarkly Relay Proxy in daemon mode. The Relay Proxy maintains a persistent connection to LaunchDarkly and stores flag data in Redis or DynamoDB. This means:- The PHP SDK reads flag values from a local store on every request, with no outbound HTTP call to LaunchDarkly
- Flag updates are received by the Relay Proxy and propagated to the store in real time
- Your WordPress server is isolated from any LaunchDarkly availability events
launchdarkly/server-sdk-redis-predis (or launchdarkly/server-sdk-redis-phpredis) Composer package, and the Relay Proxy must be configured to write to the same Redis instance. To learn more, read Using daemon mode and Storing data.
To learn more, read The Relay Proxy.
Conclusion
In this tutorial, you learned how to:- Install and configure the LaunchDarkly PHP SDK in a self-hosted WordPress environment
- Bootstrap the JavaScript SDK with server-evaluated flag values to eliminate cold start delays and flicker
- Handle full-page caching using a hybrid cookie pattern that preserves cache performance
- Use feature flags for controlled rollouts and targeted content in WordPress templates
- Set up A/B experiments across a WordPress LP and downstream React applications
- Link anonymous visitor identities to authenticated user identities using multi-context