Solana RPC HTTP Tuning — Keep-Alive, Connection Pooling, and TLS Optimisation

How to tune your HTTP client for Solana RPC. Keep-alive settings, connection pooling, TLS handshake optimisation, and the patterns that affect production latency.

BoltTx Team··6 min read
solanarpchttpkeep-alivetlsperformance

If you're sending many transactions to Solana, the HTTP layer between your client and the RPC matters. Naive HTTP usage adds tens to hundreds of milliseconds per call from connection setup, TLS handshake, and lack of connection reuse. For latency-sensitive code, this is a meaningful chunk of your latency budget.

This piece covers how to tune the HTTP client for Solana RPC: keep-alive settings, connection pooling, TLS optimisation, and the patterns that distinguish production-grade clients from naive ones.

What Each HTTP Call Actually Does

When your code calls an RPC method, the HTTP request goes through:

  1. DNS lookup (usually cached)
  2. TCP connection establishment (~1 round trip)
  3. TLS handshake (~1-2 round trips)
  4. Send HTTP request
  5. Server processes
  6. Receive HTTP response

Steps 2-3 are connection setup. They're expensive — often 50-200ms depending on geography. If you do them on every call, that overhead compounds.

The fix: keep connections open and reuse them. This is "keep-alive" or "persistent connections."

Keep-Alive Defaults

Most HTTP clients have keep-alive enabled by default — but the defaults aren't always tuned for high-volume use.

fetch in Node.js (modern): Has a default agent with keep-alive. The defaults work but aren't tuned for high-volume use.

fetch in browsers: Browser-controlled; you don't tune it.

Axios in Node: Uses Node's HTTP agent. Defaults are similar.

Reqwest in Rust: Has a connection pool by default. Configurable.

Python requests: Per-call by default; use a Session for keep-alive.

For production code, you usually want to:

  1. Verify keep-alive is on
  2. Configure pool size for your concurrency
  3. Tune timeouts appropriately

Configuring Keep-Alive in Node.js

For high-volume Node.js code:

import { Connection } from "@solana/web3.js";
import { Agent } from "undici";

// Create a tuned dispatcher
const agent = new Agent({
  keepAliveTimeout: 30_000,        // Keep connections idle 30s
  keepAliveMaxTimeout: 600_000,    // Max keep-alive 10min
  connect: {
    timeout: 10_000,               // Connection timeout 10s
  },
  pipelining: 0,                   // Disable pipelining (RPC doesn't benefit)
});

// Use the agent (web3.js v2 supports custom dispatchers)
const connection = new Connection(RPC_URL, {
  commitment: "processed",
  // Pass via fetchMiddleware for v1
});

The exact API depends on your web3.js version. Check the SDK docs for current patterns.

Connection Pool Sizing

Pool size = max concurrent connections. Considerations:

Too small: Concurrent requests block waiting for a connection. Latency spikes under load.

Too large: Memory overhead. Some servers refuse too many connections from one source.

Right sizing: Match your peak concurrency. For most bots, 10-50 is plenty. For high-volume backends, hundreds.

For Solana RPC, the same RPC handles many of your requests; pool size for that one host should match your peak concurrency to it.

TLS Handshake Optimisation

TLS handshake adds 1-2 round trips on connection setup. Once you have keep-alive working, this is a one-time cost per connection. But:

TLS session resumption can reduce subsequent handshake costs. Most modern clients do this automatically.

HTTP/2 allows multiplexing many requests over one connection. Reduces total connections needed. Most Solana RPCs support HTTP/2.

TLS 1.3 has shorter handshake than older versions. Most modern stacks default to it.

For most production setups, defaults work fine. Tune only if you've measured handshake time as a bottleneck.

Common HTTP-Layer Mistakes

Creating a new HTTP client per request. Throws away connection reuse. Symptoms: high tail latency, slow under load.

Disabling keep-alive accidentally. Some configurations turn it off. Verify it's on.

Pool too small for concurrency. Pool blocks become the bottleneck, not the network.

Pool too large. Memory waste; some providers rate-limit by connection count.

Long-lived connections accumulating issues. Sometimes very long-lived TCP connections develop problems. Reasonable max keep-alive (5-30 minutes) balances reuse with refresh.

Not handling reset connections. Servers sometimes close connections; client should detect and refresh, not crash.

Measuring HTTP-Layer Performance

A simple diagnostic:

// Log per-call timing
const start = Date.now();
const result = await connection.getLatestBlockhash();
const latency = Date.now() - start;

console.log(`Latency: ${latency}ms`);

Run during normal operation. If you're seeing wildly variable latency for what should be similar calls, suspect HTTP-layer issues.

For more detailed analysis:

Most issues are visible from per-call timing histograms.

Solana-Specific HTTP Patterns

For Solana write traffic specifically:

One client per RPC. Don't share clients across RPCs. Each RPC's connections should be its own pool.

Sub-second timeouts on writes. If your sendTransaction takes more than 2-3 seconds, something is wrong. Don't wait minutes.

Aggressive retries are HTTP errors, not transaction errors. If the HTTP call fails, retry. If the transaction is submitted and didn't land, that's a transaction-level issue.

Confirmation polling on a separate connection. After sending, polling for confirmation can use a different connection so it doesn't block your next send.

What to Do This Week

If you're optimising your Solana client:

  1. Verify keep-alive is on. Default in most stacks but verify.
  2. Configure pool size for your concurrency. Match peak load.
  3. Set explicit timeouts. Connection, request, total. Don't rely on defaults.
  4. Profile your HTTP layer. Per-call latency histograms tell the story.
  5. Use HTTP/2 if available. Multiplexing helps under load.
  6. For Rust specifically, configure your reqwest client explicitly. Don't rely on Client::new() defaults.
  7. Don't over-tune. Most bots don't need extreme HTTP optimisation; most issues are at the RPC layer, not the HTTP layer.

Try BoltTx With Tuned HTTP

BoltTx's endpoint supports HTTP/2 and standard keep-alive patterns. Configure your client appropriately:

import { Connection } from "@solana/web3.js";

const connection = new Connection(
  "https://bolttx.io/?api-key=YOUR_API_KEY",
  "processed"
);

// For high-volume, tune your underlying HTTP agent.

Free tier signup. Sub-second confirmation behaviour assumes properly-tuned HTTP on the client side.

FAQ

Does keep-alive help if my bot only sends one transaction at a time? A little, for the second-and-onwards transactions. Most useful for higher-volume code.

Should I use HTTP/2 for Solana RPC? Yes if available. Most modern providers support it.

What about streaming protocols outside HTTP? For high-volume streaming workloads, purpose-built streaming protocols can have advantages. For request-response RPC calls, HTTP/2 with keep-alive is comparable.

Can I use HTTP pipelining? Theoretically yes; in practice rarely beneficial for RPC. Use HTTP/2 multiplexing instead.

Does TLS handshake show up in my Solana send latency? Only on the first connection. Once keep-alive is working, subsequent sends skip handshake.

Further Reading

Back to all posts