Repository navigation
[NO-TICKET] Refresh rules on Vercel without waiting out a backoff - #459
Merged
Merged
Conversation
A Next.js middleware on Vercel calls the guard with the request alone, so
the guard found no waitUntil and its 30-second background rules check
was cut off when the response went out. Each cut-off counted as a failed
refresh and pushed back the refresh a request waits for, up to 8 times
the 5-minute interval, so a copy of the app could keep a deleted rule
for about 40 minutes.
- A background check that fails or is cut off now backs off only its own
deadline; the waited refresh still comes due on time.
- hostWaitUntil also reads Vercel's request context
(globalThis[Symbol.for('@vercel/request-context')].get().waitUntil),
the place @vercel/functions reads it, so background checks on Vercel
finish after the response.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
/review |
|
🎯 Elite quality (96%) — Fixes background-check backoff starving stale refreshes and adds Vercel waitUntil support with targeted regression tests. 📦 Small |
devlob
approved these changes
Oct 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No ticket.
What changed
On a v0 app published to Vercel, a custom rule removed in Patchstack kept blocking: every request to
/?q=attackteststill got a 403 from rule 22327 after the rule was deleted. The status page meanwhile showed the published app fetching rules a few minutes earlier. Rule changes now reach a Vercel app on the normal schedule: the 30-second background check, and at worst the 5-minute refresh a request waits for.Why the deleted rule stayed
The Next.js middleware the guard is wired into is called by Next with the request alone (
middleware(request)), soprotection.fetchGuard()(request)had nowaitUntilto hand work to. Two things followed:Vercel runs many copies of an app. One copy could fetch the new rules (the status page's "fetched 4 minutes ago") while the copies answering visitors still ran the old ones.
Fix
src/protect/rules/request-refresh.js:src/protect/runtime.js:hostWaitUntilalso reads Vercel's request context,globalThis[Symbol.for('@vercel/request-context')].get().waitUntil. This is where Vercel's own@vercel/functionspackage reads it (checked in 3.10.4'sget-context.js). Background checks and block reports on Vercel now finish after the response, even when the framework passes the request alone.@patchstack/connectand redeploying.Verified
tests/protect/runtime-synchronization.test.ts, two new tests that both fail on the previous code:fetchGuard()(request)(the request alone, as Next calls it) is handed to that context'swaitUntil.npm run typecheckandnpm run buildare green, and so are the disclosure, capability and install-prompt checks. Fullnpm test: 4647 passed. Two timing tests timed out under full-suite load and pass when run alone.Out of scope, worth a follow-up
event(NextFetchEvent) to the guard instead of relying on Vercel's context, which would also cover Next hosted elsewhere. That is a template change: existing unedited guards would need re-running setup to upgrade, andprotect --checkcompares against the current template. So it is left for its own PR.Docs:
AGENT-INSTALL.mdupdated: wherewaitUntilis found now includes Vercel's request context, and a failed background check no longer delays the waited refresh. Shipped-doc change, so the hostile field test is owed after the release that carries it.🤖 Generated with Claude Code