WooCommerce geolocation_ajax Investigation and Fix for Triple Redirects on Single Product Pages (TTFB Reduced from 2.5s to 0.17s)

Issue

The Time to First Byte (TTFB) for individual product pages in a WooCommerce store is as high as 2.5 seconds, even though WP Rocket page caching is enabled and the homepage loads quickly (0.18s).

Tracing the request step-by-step using curl reveals that the product page undergoes three server-side redirects before reaching the final cached page:

/item/flarum/     →301→  /item/flarum              (0.78s, WordPress removes trailing slash)
/item/flarum      →307→  /item/flarum/?v=160222...  (0.81s, WC geolocation appends ?v= with trailing slash)
/item/flarum/?v=  →301→  /item/flarum?v=160222...   (0.75s, WordPress again removes trailing slash)
/item/flarum?v=   →200                              (0.18s, cache hit)

Although the final page is successfully served from WP Rocket’s cache (0.18s), users endure three costly server-side redirects (~2.3s total) just to reach it.

Root Cause Analysis

The root cause lies in WooCommerce core’s WC_Cache_Helper::geolocation_ajax_redirect() method (class-wc-cache-helper.php).

When woocommerce_default_customer_address is set to geolocation_ajax, WooCommerce performs a 307 redirect on every non-cart/non-checkout page, appending a ?v= parameter containing a geolocation hash to the URL—enabling cache differentiation per user region.

The problem stems from how the redirect URL is constructed:

$redirect_url = trailingslashit( home_url( $wp->request ) );

trailingslashit() forcibly adds a trailing slash. However, if WordPress’s permalink structure does not include trailing slashes (e.g., /%category%/%postname%), WordPress itself will issue another 301 redirect to strip it off. This creates a chain:

  1. WordPress 301 — removes trailing slash
  2. WooCommerce 307 — appends ?v= and reinstates the trailing slash
  3. WordPress 301 — removes trailing slash again
  4. Final cached page is served

Each redirect incurs a full round-trip server request (~0.7–0.8s), resulting in ~2.3s of pure overhead.

Solution

For digital-goods stores (e.g., plugins, themes, software) that do not require region-specific pricing or tax rates, simply disable geolocation_ajax:

wp option update woocommerce_default_customer_address base_address --url=mall.weixiaoduo.com

This changes the default customer address setting from geolocation_ajax to base_address, meaning all users receive the store’s base address without geolocation-based redirects.

After applying this change, clear your page cache so WP Rocket regenerates cached files without the ?v= parameter.

Results After Fix

Metric Before Fix After Fix
Product Page TTFB (first visit) ~2.5s (3 redirects) ~1.2s (direct render)
Product Page TTFB (cached) ~2.5s (redirects are never cached) 0.17s (direct cache hit)
307 Redirects Triggered on every visit Fully eliminated
Homepage TTFB 0.18s (unaffected) 0.17s (unchanged)

Applicability

This fix applies when:

  • You operate a digital-goods store (plugins, themes, SaaS tools, etc.) where pricing/tax rules are uniform globally
  • You use WP Rocket or similar page-caching plugins
  • Your permalink structure excludes trailing slashes (e.g., /%postname%/)

If your store does require region-specific pricing or tax calculations, disabling geolocation_ajax entirely is not viable. Instead, implement a must-use plugin (mu-plugin) that hooks into the redirect logic before geolocation_ajax_redirect() and bypasses trailingslashit(). This eliminates at least one unnecessary redirect.

Diagnostic Commands

Use curl to trace the redirect chain:

# Check for 307 redirects
curl -sS -D - -o /dev/null "https://yourdomain.com/product/slug" 2>&1 | grep -iE "HTTP/|location|cache"

# Trace full redirect chain and measure TTFB per hop
curl -sS -o /dev/null -w "TTFB: %{time_starttransfer}s | HTTP: %{http_code}\n" -L "https://yourdomain.com/product/slug"

# Verify current setting
wp option get woocommerce_default_customer_address

If you observe a 307 redirect with a Location header containing ?v=, geolocation_ajax is active and causing the issue.

Multisite 环境下的注意事项

:globe_with_meridians: 多站点网络中,WooCommerce 的 geolocation_ajax 问题需要特别注意网络级和站点级的区别。

网络级配置

  • woocommerce_default_customer_address 是站点级选项,每个子站点独立设置
  • 如果多个子站点都使用 WooCommerce,需要分别处理
  • 使用 wp site list 获取站点列表,然后批量更新:
# 获取所有站点 ID
wp site list --field=url

# 为每个站点更新设置
wp option update woocommerce_default_customer_address base_address --url=site1.example.com
wp option update woocommerce_default_customer_address base_address --url=site2.example.com

Multisite 缓存考虑

  • WP Rocket 在 Multisite 中通常是站点级激活
  • 清除缓存时需要考虑网络级缓存插件(如 W3 Total Cache Multisite 版)
  • 如果使用对象缓存(Redis/Memcached),确保缓存键包含站点 ID

替代方案(保留地理定位)

如果确实需要地理定位功能,可以在 mu-plugin 中修复尾斜杠问题:

// wp-content/mu-plugins/fix-wc-geolocation.php
add_filter('woocommerce_geolocation_ajax_redirect_url', function($redirect_url) {
    // 移除强制尾斜杠,保持与 WordPress 固定链接设置一致
    return untrailingslashit($redirect_url);
}, 10, 1);

性能监控

在 Multisite 中,建议使用网络级监控工具:

  • Query Monitor 插件(网络激活)
  • New Relic 或 APM 工具
  • 检查 wp_blogswp_site 表查询性能

相关讨论

更多 Multisite 性能优化技巧可参考 /c/multisite 分类下的讨论。对于部署相关问题,建议咨询 @deploy