Run a Node Behind a Proxy

circle-exclamation

If you plan to run a Stacks node with publicly accessible RPC endpoints, it is strongly recommended to place the node behind a reverse proxy with rate limiting. Without rate limiting, a public node can be overwhelmed by excessive requests, leading to degraded performance or denial of service.

This guide provides minimal, production-tested configurations for two popular reverse proxies. Choose one — you do not need both:

  • Nginx — simpler configuration, widely known, good baseline rate limiting.

  • HAProxy — more advanced abuse detection via stick tables, HTTP proxying with automatic IP blocking.

Ports overview

A Stacks node deployment typically exposes the following services:

Service
Default Port
Protocol
Proxy?

Stacks RPC

20443

HTTP

Yes

Stacks P2P

20444

TCP

No

Stacks API

3999

HTTP

Yes, if running

Bitcoin RPC

8332

HTTP

Yes, if exposed

Bitcoin P2P

8333

TCP

No

circle-info

The P2P ports (20444, 8333) use custom binary protocols for peer-to-peer communication, not HTTP. You can leave them open directly to the network. The proxy configurations below focus on the RPC/API ports which serve HTTP traffic and are the primary target for abuse.

Optional: P2P ports can also benefit from rate-limiting. While unlikely, a denial-of-service attack could flood the P2P port so the node only communicates with malicious peers. Adding connection-rate limits on P2P ports won't hurt and provides an extra layer of protection.

Configure the Stacks node

Before setting up the proxy, configure your Stacks node so its RPC endpoint is not directly reachable from the public internet. The proxy will be the only public-facing service.

Since the proxy needs to listen on the standard public ports (e.g. 20443), the node itself must bind to different ports to avoid conflicts. The examples below use offset ports (30443, 33999) for the node's RPC and API, while the proxy owns the public-facing ports (20443, 3999). P2P stays on its standard port and is not proxied.

Bare metal

In your node's configuration file (e.g. Stacks.toml), bind the RPC to a localhost address on an offset port:

The proxy will listen on port 20443 and forward RPC traffic to the offset port. P2P binds directly on the standard port 20444 and does not go through the proxy.

Docker (stacks-blockchain-docker)

When running with stacks-blockchain-dockerarrow-up-right, the node's ports are controlled by the Docker Compose configuration. By default, ports are exposed on all interfaces (0.0.0.0). To restrict the RPC and API to localhost (so only the proxy can reach them), edit compose-files/common.yaml and change the port mappings. P2P is published directly on the standard port:

The format is host_ip:host_port:container_port. The node inside the container keeps its default ports — only the host side changes. Offset host ports (30443, 33999) are necessary because the proxy already occupies the standard ports (20443, 3999) on the host. Binding to 127.0.0.1 ensures the container ports are only reachable from the host (where the proxy runs), not from the public internet. P2P is published directly on the standard port 20444.

circle-info

Inter-container communication (e.g. the API receiving events from the blockchain node) uses Docker's internal network and service names, not published host ports. These port mapping changes do not affect container-to-container traffic.

Nginx

Nginx can serve as a reverse proxy with rate limiting using the limit_req module. The configuration below rate-limits the Stacks RPC and Stacks API endpoints.

Enable the site and restart Nginx:

Verify

HAProxy

HAProxy provides fine-grained connection tracking and abuse detection via stick tablesarrow-up-right. The configuration below proxies Stacks RPC and API traffic over HTTP, automatically rejecting clients that exceed request rate thresholds.

circle-info

Adjust maxconn, rate thresholds (ge 25), stick-table sizes, and expiry times to suit your traffic patterns. The values below are conservative defaults.

Verify

circle-info

How the abuse table works: HAProxy tracks each client IP's HTTP request rate. When a client exceeds the threshold (e.g. 25 HTTP requests in 10 seconds), its gpc0 counter is incremented and all subsequent requests from that IP are denied with HTTP 429. The stick-table entry expires after 30 minutes, lifting the block automatically.

Firewall considerations

Ensure that only the proxy's listening ports and the P2P ports are reachable from the public internet. The node's RPC should only be accessible via the proxy (localhost).

circle-exclamation

Last updated

Was this helpful?