express-rate-limit (standardHeaders: true) sets Retry-After on the 429 response. Browser JS reading the response across CORS can't see headers outside the CORS-safelisted set unless the server explicitly exposes them via Access-Control-Expose-Headers.
Retry-After is NOT on the CORS safelist, so fetch().headers.get('Retry-After') returns null on a 429 from a cross-origin client. Without it, clients fall back to fixed-delay retry instead of honoring the server's back-off.
Fix: add 'Retry-After' to server.js exposedHeaders. Update tests/api/cors-expose-headers.test.js to mirror the entry. Add an assertion in tests/api/rate-limit.test.js confirming the 429 response actually carries the header.
Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/
express-rate-limit (standardHeaders: true) sets
Retry-Afteron the 429 response. Browser JS reading the response across CORS can't see headers outside the CORS-safelisted set unless the server explicitly exposes them viaAccess-Control-Expose-Headers.Retry-Afteris NOT on the CORS safelist, sofetch().headers.get('Retry-After')returns null on a 429 from a cross-origin client. Without it, clients fall back to fixed-delay retry instead of honoring the server's back-off.Fix: add
'Retry-After'to server.jsexposedHeaders. Updatetests/api/cors-expose-headers.test.jsto mirror the entry. Add an assertion intests/api/rate-limit.test.jsconfirming the 429 response actually carries the header.Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/