WooCommerce sessions are how WooCommerce keeps track of carts, notices, and customer state across page loads without relying on PHP sessions. Native PHP sessions are explicitly discouraged in WordPress plugin development because they cause caching issues, session lock contention, and storage scaling problems. WooCommerce solves this by storing session state in a custom database table (wp_woocommerce_sessions) plus a lightweight identifying cookie — giving you all the convenience of session storage without breaking page caching.
For developers building WooCommerce plugins, learning the WooCommerce session API unlocks a lot: you can store custom checkout fields before they’re saved to the order, persist quote-request data across multi-step forms, remember UI preferences for guest users, and integrate with marketing automation tools that need stable identifiers across the visit. The WooCommerce session API is built on the WC_Session abstract class with a concrete WC_Session_Handler implementation that owns the database table.
Quick verdict: use WC()->session->set() and get() for reading and writing; the wc_session_expiration filter to tune how long sessions live; and the wp_woocommerce_sessions table for direct queries when you need them. This guide covers the API, the database table, the cookies WooCommerce sets, expiration tuning, GDPR considerations, and a complete reusable session-manager class.
WooCommerce sessions: quick reference
If you are evaluating WooCommerce sessions for your next project, you are weighing real trade-offs between cost, complexity, ownership, and time-to-launch. The right WooCommerce sessions decision depends on a handful of variables — team capacity, scope clarity, and how much ongoing maintenance you can absorb. The summary below is the 60-second version; the rest of this guide unpacks the nuance.
- WooCommerce sessions pricing typically ranges based on scope clarity, integration count, and ongoing support requirements.
- WooCommerce sessions timelines vary from days (small scope) to months (enterprise scope) depending on complexity.
- The biggest variable in WooCommerce sessions is requirements clarity at the brief stage — vague briefs produce vague quotes.
- Vendor selection for WooCommerce sessions matters more than tool selection — the right team beats the right stack.
- WooCommerce sessions ROI is positive when scope is bounded, deliverables are specified, and success criteria are measurable.
For complementary perspectives on WooCommerce sessions, the WC_Session class reference and WC_Session_Handler class reference resources cover adjacent angles worth reviewing alongside this guide. They focus on the underlying technology and standards — this post focuses on the WooCommerce sessions decision specifically.
When you revisit your WooCommerce sessions approach in 12 to 24 months, three signals usually indicate a refresh is justified. First, the original brief no longer matches business reality — product, audience, or operational scope has shifted. Second, the underlying technology has moved forward enough that the WooCommerce sessions decision made under previous constraints would be different today. Third, ongoing maintenance overhead has crept up beyond what was forecast at launch. None of these are emergencies on their own; together they signal it is time to revisit fundamentals rather than patch around them.
Why WooCommerce uses its own session system
PHP’s built-in $_SESSION is technically available in WordPress but causes real problems at scale. WooCommerce sidesteps these problems by building its own session layer.
- Page cache compatibility — PHP sessions force
Cache-Control: no-cache, defeating full-page caching plugins. WC sessions identify visitors via a lightweight cookie, so cached HTML still works - No session lock contention — PHP file-based session locks block concurrent requests for the same session. WooCommerce uses database transactions instead, which scale better
- Shared hosting compatibility — many shared hosts disable or rate-limit PHP sessions; the WC database-backed approach has no such restrictions
- Centralized storage — all WC session data lives in one queryable table instead of per-PHP-process files, simplifying multi-server deployments
- Custom expiration logic — WC controls when sessions expire and how they’re cleaned up, decoupled from PHP’s session config
The wp_woocommerce_sessions database table
Every active WooCommerce session is one row in the wp_woocommerce_sessions table (prefix replaces wp_ on custom installs). Knowing the table structure is essential for debugging session issues and writing direct queries.
| Column | Type | Purpose |
|---|---|---|
session_id | BIGINT, auto-increment | Internal row ID |
session_key | VARCHAR(32), unique | Customer ID — 32-char hash for guests, numeric user ID for logged-in customers |
session_value | LONGTEXT | Serialized PHP associative array of all session keys/values for this customer |
session_expiry | BIGINT | Unix timestamp when this session becomes eligible for cleanup |
Table size monitoring: On high-traffic sites, this table can grow into millions of rows. WooCommerce runs a cleanup of expired sessions on its
woocommerce_cleanup_sessionsdaily cron, but if the cron is delayed or the table grows faster than cleanup runs, query performance degrades. The “Performance considerations” section later in this guide covers monitoring + cleanup strategies.
Important: session_value is PHP-serialized, not JSON. Use maybe_unserialize() when reading directly. Never write directly to this table — always go through WC()->session->set() so WooCommerce’s caching layer stays consistent.
WooCommerce cookies — what gets set and when
WooCommerce sets several cookies to make the session system work. Understanding them is critical for cookie consent banners, GDPR compliance, and debugging cookie-related cart issues.
| Cookie name | Set when | Purpose |
|---|---|---|
wp_woocommerce_session_{hash} | Guest first adds to cart, or you explicitly call set_customer_session_cookie() | Stores the session_key that links the browser to a row in the sessions table |
woocommerce_cart_hash | Cart contents change | Hash of cart contents — used to detect cart changes without reading the full session |
woocommerce_items_in_cart | Cart has items | Simple flag (value: “1”) so caching layers can branch on cart-empty vs cart-populated |
tk_or, tk_lr, tk_r3d | Set by Jetpack/Akismet on some installs | NOT set by WC core; comes from related plugins if active |
All three primary WooCommerce cookies are functional cookies — they are required for the shop to work. In most GDPR-strict frameworks, functional cookies are exempt from the consent requirement, but you should still disclose them in your cookie policy.
Initiating a WooCommerce session for guest users
By default, WooCommerce only creates a session for guest visitors at the moment they add their first product to the cart. If you need to store custom data for a guest BEFORE they add to cart — for example, capturing a referrer, a saved quote, or a multi-step form state — you must initiate the session manually.
// Initiate the WooCommerce session manually for guest users.
// Without this, the session is only created the moment a guest adds
// something to the cart — too late if you need to set custom data
// earlier in the visit.
add_action( 'woocommerce_init', 'raj_init_guest_wc_session' );
function raj_init_guest_wc_session() {
if ( is_user_logged_in() || is_admin() ) {
return; // logged-in users always have a session; admin doesn't need one
}
if ( isset( WC()->session ) && ! WC()->session->has_session() ) {
WC()->session->set_customer_session_cookie( true );
}
}Drop this into your plugin or child theme. After it runs, every guest visit creates a session row immediately, and you can store data via WC()->session->set() from the first page load.
Verify the session was created: Open Chrome DevTools → Application → Cookies and find the cookie starting with
wp_woocommerce_session_. If it’s present, the session is active. If not, check that your callback isn’t early-returning incorrectly (e.g., theis_admin()check fires unexpectedly on the front-end).
Set, get, remove WooCommerce session values
The core WooCommerce session API is four methods on WC()->session: set(), get(), has_session(), and get_customer_id(). Together they cover 90% of daily session work.
// Set a value in the WooCommerce session
WC()->session->set( 'my_custom_key', array( 1, 2, 3 ) );
// Get a value back (returns null if not set)
$value = WC()->session->get( 'my_custom_key' );
// Get with a default fallback
$value = WC()->session->get( 'my_custom_key', array() );
// Remove a value (set it to null)
WC()->session->set( 'my_custom_key', null );
// Check whether a session exists for the current visitor
if ( WC()->session->has_session() ) {
// visitor has an active WooCommerce session
}
// Get the customer ID associated with this session
// (a 32-char hash for guests, or numeric user ID for logged-in customers)
$customer_id = WC()->session->get_customer_id();Update an existing array stored in the session
A common pattern: keep an array of items in the session and append to it as the visitor interacts. The cleanest approach is to read the existing array, merge new values, then write back.
// Pattern: append to an existing array stored in the session.
// First get the existing value, cast to array (handles null case),
// then push the new values and write back.
$existing = (array) WC()->session->get( 'my_session_array' );
$new = array( 4, 5, 6 );
// Use array_merge for cleaner result than array_push of an array
$updated = array_merge( $existing, $new );
WC()->session->set( 'my_session_array', $updated );array_merge is preferred over array_push when both sides are arrays — it produces a flat result instead of a nested array. Cast to (array) handles the case where the key was never set (returns null).
Change the WooCommerce session expiration time
Default expiration is 48 hours after creation. Adjust via two filters that work together:
wc_session_expiring — when WC marks the session as "about to expire"
Default: 47 hours (1 hour before full expiration). This filter controls the threshold at which WooCommerce starts treating the session as ending soon — used internally for cleanup scheduling.
// "Session about to expire" — fires the WC cleanup logic that marks
// the session as ending soon. Default: 47 hours after session creation.
// Customize when you've also bumped the full expiration via the
// wc_session_expiration filter (below).
add_filter( 'wc_session_expiring', 'raj_session_about_to_expire' );
function raj_session_about_to_expire( $session_time ) {
// Mark as about-to-expire 1 hour before the full expiration.
// If you've set wc_session_expiration to 100 hours, this should be 99 hours.
return 60 * 60 * 99; // 99 hours in seconds
}wc_session_expiration — full session lifetime
Default: 48 hours. Controls how long sessions persist before being eligible for cleanup. Extend for B2B sites with long-running carts, shorten for sites prioritizing data minimization.
// Full session expiration — default is 48 hours (172800 seconds).
// Extend for long-running carts (e.g., B2B sites where buyers spend days
// curating an order) or shorten for sites where short sessions are
// preferred for privacy / GDPR reasons.
add_filter( 'wc_session_expiration', 'raj_session_expires' );
function raj_session_expires( $session_time ) {
return 60 * 60 * 100; // 100 hours in seconds
}Always update wc_session_expiring when you change wc_session_expiration — keep the “about to expire” threshold 1-2 hours before the full expiration so cleanup logic stays consistent.
Querying the wp_woocommerce_sessions table directly
Sometimes you need to inspect or report on sessions outside the normal request flow — admin dashboards showing active visitor count, cron jobs auditing abandoned carts, or debugging tools. Direct queries work as long as you treat the table as read-only:
// Query the wp_woocommerce_sessions table directly.
// Useful for: debugging session data, building admin tools that
// inspect carts of guest users, or cron jobs that clean up expired
// sessions older than the default cleanup window.
global $wpdb;
// All non-expired sessions
$active = $wpdb->get_results( $wpdb->prepare(
"SELECT session_id, session_key, session_expiry
FROM {$wpdb->prefix}woocommerce_sessions
WHERE session_expiry > %d
ORDER BY session_expiry DESC",
time()
) );
// Read a specific session value (it's stored as serialized PHP)
$row = $wpdb->get_row( $wpdb->prepare(
"SELECT session_value FROM {$wpdb->prefix}woocommerce_sessions
WHERE session_key = %s",
$customer_id
) );
if ( $row ) {
$session_data = maybe_unserialize( $row->session_value );
// $session_data is now the associative array of all session keys/values
// for this customer (e.g., 'cart', 'applied_coupons', custom keys you set)
}
// Count expired sessions waiting for cleanup
$expired_count = $wpdb->get_var( $wpdb->prepare(
"SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_sessions
WHERE session_expiry < %d",
time()
) );Never write to wp_woocommerce_sessions directly: WooCommerce maintains a memory cache of session data that gets out of sync with the table if you write directly via $wpdb. Always go through
WC()->session->set()for writes. Direct reads are fine — the table is the source of truth on disk.
Create a reusable WooCommerce session manager class
Once you have more than 2-3 session reads/writes scattered across your plugin, wrap them in a class. This keeps the API consistent, makes the code testable, and gives you a single place to add cross-cutting concerns like logging or caching.
// Encapsulate WooCommerce session logic in a class.
// This keeps related session reads/writes together, makes the code
// testable, and avoids global helper functions sprinkled across files.
class Raj_WC_Session_Manager {
const SESSION_KEY = 'raj_custom_data';
public function __construct() {
add_action( 'woocommerce_init', array( $this, 'initiate_session' ) );
add_action( 'raj_clear_session', array( $this, 'clear_data' ), 10, 1 );
}
/**
* Initiate the WC session for guest visitors so we can store data
* before they add anything to the cart.
*/
public function initiate_session() {
if ( is_user_logged_in() || is_admin() ) {
return;
}
if ( isset( WC()->session ) && ! WC()->session->has_session() ) {
WC()->session->set_customer_session_cookie( true );
}
}
/**
* Append values to a session array. Creates the array if it does
* not exist yet.
*/
public function add_values( array $values ) {
$existing = (array) WC()->session->get( self::SESSION_KEY, array() );
$merged = array_merge( $existing, $values );
WC()->session->set( self::SESSION_KEY, $merged );
}
/**
* Read all stored values.
*/
public function get_values() {
return (array) WC()->session->get( self::SESSION_KEY, array() );
}
/**
* Clear the session data for this key.
*/
public function clear_data( $key = self::SESSION_KEY ) {
if ( isset( WC()->session ) ) {
WC()->session->set( $key, null );
}
}
}
new Raj_WC_Session_Manager();
// Usage from anywhere in your plugin code:
// ( new Raj_WC_Session_Manager() )->add_values( array( 'foo', 'bar' ) );
// do_action( 'raj_clear_session', 'raj_custom_data' );WooCommerce session performance considerations
At high volume, the wp_woocommerce_sessions table can grow rapidly — every guest who visits gets a row, and the default 48-hour expiration means rows persist long after the visitor has left. Three monitoring + cleanup strategies:
- Monitor table size — for high-traffic sites, set up alerts when the table exceeds ~500k rows or ~500MB. Slow checkout pages often trace back to bloated sessions table
- Index check — confirm the
session_keycolumn has a UNIQUE index (it does by default; verify after migrations). Without it, every session read does a full table scan - Aggressive cleanup — for sites where 48 hours of session storage isn’t needed, reduce
wc_session_expirationto 24 or even 12 hours. Fewer rows persist; faster queries - Belt-and-suspenders cron — supplement WC’s default cleanup with a custom cron that purges rows older than 30 days unconditionally
// Schedule a daily cleanup of expired WooCommerce sessions. // WooCommerce runs its own cleanup, but on high-traffic sites with // long-expiration sessions, the table can balloon. This belt-and-suspenders // cron ensures rows older than 30 days never linger. add_action( 'raj_cleanup_wc_sessions', 'raj_cleanup_wc_sessions_callback' ); if ( ! wp_next_scheduled( 'raj_cleanup_wc_sessions' ) ) { wp_schedule_event( time(), 'daily', 'raj_cleanup_wc_sessions' ); } function raj_cleanup_wc_sessions_callback() { global $wpdb; $cutoff = time() - ( 30 * DAY_IN_SECONDS ); $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_sessions WHERE session_expiry < %d", $cutoff ) ); }
GDPR and cookie consent considerations
WooCommerce session cookies and the underlying database storage have specific GDPR implications you should disclose:
- The wp_woocommerce_session_{hash} cookie stores a random session key, not personal data — but it links the browser to a database row that may contain personal data (shipping address, billing address, cart contents)
- Disclose in your cookie policy — list
wp_woocommerce_session_*,woocommerce_cart_hash, andwoocommerce_items_in_cartas “strictly necessary / functional” cookies - Right to erasure — when a customer requests deletion, their
wp_woocommerce_sessionsrow should be deleted alongside their orders/account. WooCommerce’s GDPR data eraser already handles this - Data minimization — store only what you need in the session. Avoid putting full PII in custom session keys; use the session for ephemeral state and the order/customer records for canonical data
- Consent timing — strictly necessary cookies don’t require explicit consent under GDPR, but EU users still expect disclosure. A cookie banner that mentions WooCommerce by name is the safest path
Debugging WooCommerce sessions
When something is wrong with a cart or a custom session-backed feature, the diagnostic sequence is:
- 1. Check the cookie is set — Chrome DevTools → Application → Cookies → look for
wp_woocommerce_session_ - 2. Check the session row exists — phpMyAdmin or WP-CLI:
wp db query "SELECT * FROM wp_woocommerce_sessions WHERE session_key = '{customer_id}'" - 3. Read the session data — unserialize the
session_valuecolumn to see what’s actually stored - 4. Check caching — page caching plugins that don’t recognize WC cookies will serve a cached version that doesn’t reflect the current cart. Confirm WP Rocket / LiteSpeed / Cloudflare APO is configured to bypass cache when WC cookies are present
- 5. Check the WC session class itself — for advanced debugging, drop
error_log()calls into a custom function that runs atwoocommerce_init
Quick debug helper — drop this into your child theme to log session contents on demand:
// Helper for debugging WC sessions in a dev environment.
// Dumps the current visitor's session contents to the WordPress debug log.
// Call this anywhere the WC session is available (after 'woocommerce_init').
function raj_debug_wc_session() {
if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) {
return;
}
if ( ! isset( WC()->session ) || ! WC()->session->has_session() ) {
error_log( 'No active WooCommerce session for current visitor.' );
return;
}
error_log( 'WC Session Customer ID: ' . WC()->session->get_customer_id() );
error_log( 'WC Session Data: ' . print_r( WC()->session->get_session_data(), true ) );
}
// Trigger via a custom URL parameter in dev only:
add_action( 'wp_loaded', function () {
if ( isset( $_GET['raj_debug_wc_session'] ) && current_user_can( 'manage_options' ) ) {
raj_debug_wc_session();
}
} );Then visit any page with ?raj_debug_wc_session=1 appended (while logged in as admin) to dump the current session to wp-content/debug.log.
Common WooCommerce session mistakes
Patterns that look correct but cause real issues:
- Using PHP $_SESSION instead of WC()->session — breaks page caching everywhere
- Writing directly to wp_woocommerce_sessions via $wpdb — out-of-sync cache; data appears to “disappear” until the next request
- Storing very large data in sessions — session_value is LONGTEXT but serialized PHP doesn’t scale well past ~100KB per session
- Storing sensitive data without encryption — credit card numbers, passwords, API tokens should NEVER be in session storage even temporarily
- Not initializing the session for guests — then trying to set data and being surprised that it’s lost on the next request
- Forgetting that logged-in users use numeric session_key — guest hash patterns don’t apply; check
is_user_logged_in()before querying by hash format - Caching pages WITHOUT bypassing WC cookies — visitors see another user’s cart; major bug
WooCommerce session class reference
For deeper API exploration, the two relevant classes:
- WC_Session — abstract base class defining the session API surface. Reference: WC_Session class reference
- WC_Session_Handler — the concrete implementation that owns the wp_woocommerce_sessions table. Extends WC_Session. Reference: WC_Session_Handler class reference
For most plugin work, you interact with the singleton via WC()->session; you rarely need to instantiate or extend these classes directly. The reference pages are useful when you’re hunting for an undocumented method or working out exactly which API the singleton exposes at runtime.
Basics — FAQs
What is the wp_woocommerce_sessions table?
It is a custom database table that WooCommerce uses to store visitor session data — carts, applied coupons, notices, and any custom data plugins set via WC()->session->set(). Each row represents one active session, linked to the visitor via the wp_woocommerce_session_{hash} cookie. The table has four columns: session_id (auto-increment), session_key (unique 32-char hash for guests or numeric user ID), session_value (serialized PHP array of all stored data), and session_expiry (Unix timestamp).
Does WooCommerce use cookies?
Yes — three primary cookies. wp_woocommerce_session_{hash} stores the session key linking the browser to a database row. woocommerce_cart_hash is a hash of current cart contents used to detect cart changes. woocommerce_items_in_cart is a simple flag indicating the cart is non-empty (used by caching layers to branch). All three are functional cookies required for the shop to work.
How do I set a session value in WooCommerce?
Use WC()->session->set( 'my_key', $value ); after the woocommerce_init action has fired. The value can be any serializable PHP type (array, string, number, object). Retrieve it later with WC()->session->get( 'my_key' ). For guest users, you may need to call WC()->session->set_customer_session_cookie( true ) first to initialize the session.
Advanced — FAQs
How do I change the WooCommerce session expiration time?
Use two filters together. wc_session_expiration sets the full session lifetime (default 48 hours = 172800 seconds). wc_session_expiring sets the “about to expire” threshold (default 47 hours = 169200 seconds) — keep this 1-2 hours before the full expiration. Always update both filters together so the cleanup logic stays consistent.
Can I query the wp_woocommerce_sessions table directly?
Yes for reads — use $wpdb->get_results() or $wpdb->get_row() and unserialize the session_value column with maybe_unserialize(). Useful for debugging, admin dashboards, and cron jobs that audit sessions. Never write directly to the table — WooCommerce maintains a memory cache that gets out of sync. Always use WC()->session->set() for writes.
Why are WooCommerce sessions not working with page caching?
Caching plugins must be configured to bypass cache when WooCommerce cookies are present. WP Rocket, LiteSpeed Cache, and W3 Total Cache all have built-in WooCommerce awareness. For Cloudflare APO, ensure the worker respects the WC cookie set. For custom caching setups, add cookie-aware cache keys or bypass cache entirely when wp_woocommerce_session_* is present.
Performance & compliance — FAQs
How big can the wp_woocommerce_sessions table get?
It depends on traffic + expiration time + guest behavior. A site with 10,000 daily unique visitors and 48-hour expiration typically has ~30,000 active rows. High-traffic stores can hit 500k+ rows. Beyond ~500MB table size, query performance starts to degrade. Mitigations: reduce wc_session_expiration, add a belt-and-suspenders cron that purges rows older than 30 days, monitor table size in your hosting dashboard.
Are WooCommerce session cookies GDPR-compliant without explicit consent?
WooCommerce session cookies are classified as “strictly necessary / functional” — they’re required for the shop to work, so they’re exempt from the consent requirement under GDPR Article 5(3). However, you should still disclose them in your cookie policy. A cookie banner that mentions WooCommerce by name is the safest practice. The right to erasure (Article 17) DOES apply to the session row, and WooCommerce’s built-in personal data eraser handles this automatically.
How do I delete a specific visitor's WooCommerce session?
Two options. Programmatically: WC()->session->destroy_session() destroys the current visitor’s session entirely (cookie + database row). For another visitor: query the table by their session_key (the cookie value or user ID) and delete that row via $wpdb->delete( $wpdb->prefix . 'woocommerce_sessions', array( 'session_key' => $key ) ). The next request from that browser will get a fresh session.


Excellent explanation of WooCommerce sessions and cookies.
Thanks for your valuable comment.