r/ethdev • u/tomoglava • 1d ago
Information How do you protect a public RPC endpoint that runs on your own Ethereum nodes?
Getting close to deploying a dApp (Ethereum mainnet only) and I'll be running 2 of my own nodes behind it, with Alchemy or Infura as a fallback. The frontend runs in the browser, so the RPC URL is public by definition and anyone can grab it from devtools and hammer it with scripts. I want to keep it from getting flooded, whether that's an actual attack or just bots and other apps freeloading on it.
Here's what I'm planning so far.
- A reverse proxy in front of both nodes handling rate limiting, method allowlisting and caching. The nodes' RPC ports only listen on localhost/a private network, so nothing reaches them except through the proxy
- Only the P2P ports open to the internet, Engine API (8551) never exposed
- Only the eth, net and web3 namespaces enabled (no debug, trace, admin, txpool), WebSocket off unless I need it
- Caps on batch size, eth_getLogs block ranges, and gas/timeouts for eth_call and eth_estimateGas
- Origin/CORS restrictions, which at least stop other sites from using my RPC in-browser (useless against scripts, I know)
A few things I'm still not sure about.
Should rate limiting be per time window (X requests per second/minute) or a cap on total requests (per session or per day)? Or both, like a burst limit plus a daily quota?
Is it normal practice to track and limit per IP? And do people actually ban IPs that look like attackers, e.g. ones that keep slamming into the limit nonstop? If so, temporary bans that escalate, or permanent? Also, how do you avoid hurting real users who share an IP (offices, VPNs) when attackers can rotate IPs anyway?
Do you count HTTP requests or individual RPC calls? One JSON-RPC batch can carry hundreds of calls, and an eth_getLogs over a wide block range costs way more than eth_blockNumber. Do you weight limits by method?
Is "own nodes as primary, Alchemy/Infura as fallback" a sane setup? Do you fail over in the proxy or in the frontend? A key in the frontend can be grabbed by anyone, but if it lives in the proxy, couldn't an attacker just overload my nodes and burn through my provider quota?
How do you load balance and health check 2 nodes? I'm thinking of running a different client on each and pulling a node out of rotation when it falls a few blocks behind, so users don't see inconsistent state between requests. I'm also considering eRPC for this since it handles failover to Alchemy/Infura, caching and rate limiting. Has anyone run it in production? Should I go with it or just stick with nginx/HAProxy?
If you're running something similar in production, sharing any experience would be really helpful, even if it's just what didn't work. Thanks!