Skip to content

fix(vps): rate limit by the real client IP behind Cloudflare - #702

Open
nelitow wants to merge 2 commits into
mainfrom
nj/fix/rate-limit-behind-cloudflare
Open

fix(vps): rate limit by the real client IP behind Cloudflare#702
nelitow wants to merge 2 commits into
mainfrom
nj/fix/rate-limit-behind-cloudflare

Conversation

@nelitow

@nelitow nelitow commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

When app-testnet.fuel.network moves to Cloudflare in front of Railway, the first X-Forwarded-For hop is a Cloudflare address for every visitor, so the per-IP rate limit would collapse onto a handful of keys. nginx now keys on CF-Connecting-IP whenever the first hop is inside Cloudflare's published ranges, and on the first hop otherwise. This commit was on #699 but landed after the squash merge, so main did not get it. It also adds explicit GITHUB_TOKEN permissions to the two api-lite workflows, which CodeQL flagged on #700.

Changes

Area Change
nginx template geo block over Cloudflare's IP ranges plus a map that picks CF-Connecting-IP behind Cloudflare and the first forwarded hop otherwise, exposed as $client_ip
Explorer image Fetches Cloudflare's IPv4 and IPv6 ranges at build time into /etc/nginx/cloudflare-ips.conf; the build fails on an empty fetch
Railway deploy notes RATE_LIMIT_KEY=$client_ip for the explorer service, with the reasoning next to it
CI workflows permissions: contents: read on the uptime probe; contents: read and packages: read on the api-lite e2e job

🤖 Generated with Claude Code

nelitow and others added 2 commits September 3, 2026 19:18
…d hop is a Cloudflare address

The explorer's public domain is a Cloudflare-proxied CNAME to Railway, so the first X-Forwarded-For hop is a Cloudflare address for every visitor. A geo block over Cloudflare's published ranges, fetched at image build, selects CF-Connecting-IP only for those requests; direct traffic keeps the first hop, which Railway's edge rewrites.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
fuel-explorer-v2-vite Ready Ready Preview Sep 3, 2026 10:21pm UTC

Request Review

@cursor

cursor Bot commented Sep 3, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes edge rate-limit identity and trusts CF-Connecting-IP only when XFF’s first hop is in Cloudflare ranges; misconfigured geo lists or proxy chains could mis-key limits. Build-time curl to Cloudflare adds an external dependency during image builds.

Overview
nginx rate limiting now derives a $client_ip variable: when the first X-Forwarded-For hop matches Cloudflare’s published ranges, limits use CF-Connecting-IP; otherwise behavior stays on the first XFF hop (or direct peer). Railway deploy docs switch RATE_LIMIT_KEY from $xff_first to $client_ip so traffic through Cloudflare does not share one bucket per edge IP.

The explorer Docker image downloads Cloudflare IPv4/IPv6 lists at build time into /etc/nginx/cloudflare-ips.conf (build fails if either fetch is empty).

GitHub Actions add explicit least-privilege permissions on the api-lite uptime workflow (contents: read) and the api-lite E2E job (contents: read, packages: read) for CodeQL.

Reviewed by Cursor Bugbot for commit 8b59c98. Bugbot is set up for automated code reviews on this repo. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants