fix(web): rip @upstash/ratelimit from /api/visit#1
Open
0motionguy wants to merge 1 commit into
Open
Conversation
The IP-based ratelimit on /api/visit was costing $63.94 / 5 days on
Upstash (606 GB bandwidth, 27 M ops) because the endpoint sits bare on
Vercel and got hammered at ~62 req/s by scrapers probing the SSRF-guarded
outbound fetcher. Every probe hit triggered one Lua ratelimit call.
Phase 1 of the fix removed UPSTASH_* env vars from the Vercel project so
the module fail-opened. This is Phase 3 — delete the dead code path
entirely so re-adding env vars cannot resurrect the cost surface.
Files removed: web/app/api/visit/rate-limit.{ts,test.ts}.
Deps removed: @upstash/ratelimit, @upstash/redis.
route.ts: drop rate-limit import, ratelimit invocation, the rl.headers
merge in `respond`, and the now-unused getClientIp helper.
Next step (out of scope here): put Cloudflare in front of visitportal.dev
with a free-tier rate-limit rule on /api/visit so the abuse traffic gets
dropped at the edge instead of consuming Vercel function invocations.
Verified:
- pnpm test (24/24 pass)
- pnpm build (clean)
- Deployed to prod (dpl_DaNY4XnPeeCeUTXiGhhZu1sWCoab); /api/visit returns
400/502/200 as before, with no X-Ratelimit-* headers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Summary
@upstash/ratelimit+@upstash/redisdeps fromweb/.web/app/api/visit/rate-limit.ts+ its test (kept the SSRF guard; that's the real defence).getClientIphelper fromweb/app/api/visit/route.ts.Why
/api/visitis a same-origin SSRF-guarded outbound fetcher. Scrapers and SSRF probers hammered it at ~62 req/s for 5 days, and the 10/min/IP@upstash/ratelimittriggered one Lua call per hit. That's 27M Upstash commands / 606 GB billed bandwidth / $63.94 in 5 days. The rate-limit was cosmetic — SSRF guard rejects every abuse attempt regardless. Phase 1 already removed the prod env vars (fails open by design). This PR makes the decoupling permanent.Verification (2026-05-23)
pnpm test24/24 passpnpm buildclean (Next 15.5.15)dpl_DaNY4XnPeeCeUTXiGhhZu1sWCoab(aliased to visitportal.dev)curl -I https://visitportal.dev/api/visitreturns 400/502/200 as before, noX-Ratelimit-*headers (proof the Upstash path is gone)https://visitportal.dev/home page 200Follow-up (not in this PR)
Put Cloudflare in front of
visitportal.devwith a free-tier WAF rate-limit on/api/visitso scraper traffic drops at the edge instead of consuming Vercel function invocations.🤖 Generated with Claude Code