#10826·axios

XHR adapter: avoid main-thread jank from synchronous document.cookie reads (use cookieStore where available)

Author: jasonsaaymanCreated Apr 29, 2026Updated Jul 15, 2026

Background

axios reads the XSRF cookie via document.cookie inside lib/helpers/cookies.js, called from lib/helpers/resolveConfig.js. document.cookie access is synchronous and main-thread-blocking; under contention it can show up as a real jank source in browser performance traces. The whatwg/html issue tracking this is whatwg/html#11658, and the Cookie Store API is the asynchronous alternative when available.

#7040 (now closed) was a first pass at this. The motivation was sound but the implementation had several blockers (regressed CVE fixes through a stale base, double cookie-value encoding, non-standard-env fallback dropped, broad XHR re-indent, opt-in flag rather than auto-detection). See the close comment on that PR for the detail.

What we want

A jank-avoiding read path for the XSRF cookie that:

  • Is XHR-only. The fetch adapter is already async; switching its config-resolution to cookieStore adds a microtask without saving jank. The change should be confined to where it actually helps.
  • Is automatic, not opt-in. No new public config option. Detect window.cookieStore at runtime; use it for XSRF reads when present, fall back to the existing document.cookie path when not.
  • Round-trips cookie values correctly. cookieStore.get returns the raw value; do not decodeURIComponent it. cookieStore.set accepts the raw value; do not encodeURIComponent it. Encoding is a document.cookie-specific quirk.
  • Preserves existing security hardening. The own(key) prototype-pollution guard from GHSA-q8qp-cvcw-x6jj and the strict withXSRFToken === true || == null check from GHSA-xx6v-rp6x-q39c must stay intact in any restructure of resolveConfig.js. Same for the encodeUTF8 helper that replaced unescape(encodeURIComponent(...)).
  • Preserves the non-standard-env no-op. cookies.js returns a {write(){}, read(){return null}, remove(){}} shim in Web Worker / React Native envs. Any new export must keep that fallback.

Implementation notes / constraints

  • The XHR adapter currently does const _config = resolveConfig(config) synchronously inside dispatchXhrRequest. The smallest change is to make dispatchXhrRequest async (or do resolveConfig(config).then(...)) without re-indenting the entire body.
  • resolveConfig returning a Promise needs to be a controlled change: any other internal caller that assumes sync should be checked. Quick scan: lib/adapters/fetch.js, lib/adapters/xhr.js, possibly tests.
  • Tests should mock window.cookieStore explicitly to verify the async branch actually runs (existing approach asserted via document.cookie, which co-stores with cookieStore and didn't differentiate).
  • Behaviour in browsers without cookieStore (Safari at the time of writing being the conspicuous one) must be unchanged.

Out of scope

  • Any general async-resolution work beyond XSRF cookie reads.
  • Changes to the fetch adapter.
  • New public config options.

References