SockJS Fallback for Older Browsers — Concept Explained
Fast recall
Quick revision
Core answer
Whether the candidate knows why production apps still include SockJS despite wide WebSocket browser support.
Thinking SockJS is only for old browsers — corporate firewalls and HTTP proxies that don't support WebSocket upgrades are a more common real-world issue.
Describe the SockJS transport negotiation process and mention that SockJS server info endpoint (/info) is hit first to determine the best transport.
Interview-ready guide
Interview answer
SockJS is not about supporting old browsers — every modern browser has supported WebSocket natively for years. The real problem it solves is corporate proxies and enterprise network infrastructure that strips the Upgrade header or refuses to maintain persistent connections.
What SockJS does:
- Wraps WebSocket with a negotiation layer: probes the server's
/infoendpoint to discover capabilities, tries native WebSocket first, and automatically falls back to XHR streaming or XHR long-polling if the handshake fails - From the application layer, the API looks identical regardless of transport; in Spring, adding
.withSockJS()registers all fallback URL patterns automatically - Transport fallback order: native WebSocket → XHR streaming → XHR long-polling; each step degrades latency and throughput but increases compatibility with restrictive infrastructure
The operational costs:
- Multiple endpoint paths requiring consistent CORS, cookie, and proxy configuration across all transports
- A separate SockJS heartbeat every 25 seconds that keeps transports alive through proxies that kill idle HTTP connections — this is independent of STOMP heartbeats and operates at the transport layer
- Randomized URL paths defeat path-based load balancer routing; must use cookie or IP-hash affinity for SockJS endpoints
When to use it:
- When users might be behind corporate networks you don't control — enterprise environments, financial institutions, government networks
- Skip it only when you own the entire network path: internal microservices communicating over a controlled network, or mobile apps on clean connections
I recommend including SockJS by default for any user-facing WebSocket endpoint where you cannot guarantee the network path is clean — the cost is minor compared to debugging proxy-related connection failures in production.
SockJS Fallback for Older Browsers — Concept Explained
Direct Answer
SockJS is a JavaScript/Java library that provides a WebSocket-like API with automatic fallback transports (XHR streaming, long polling) for environments where native WebSockets are blocked or unavailable, such as corporate proxies or older browsers.
Deep Dive & Architecture
You write a .proto file declaring your messages and service contracts, then use the protoc compiler (typically via the protobuf-maven-plugin or Gradle's protobuf plugin) to auto-generate Java source files.
The generated stubs include POJOs for messages and abstract base classes for services that you implement on the server side and use as type-safe clients.
SockJS is a JavaScript/Java library that provides a WebSocket-like API with automatic fallback transports (XHR streaming, long polling) for environments where native WebSockets are blocked or unavailable, such as corporate proxies or older browsers.
Interviewers want to see that you know the full loop from schema to compilable Java code.
Code Example
Rate limiting protects an API from abuse and noisy tenants. The token-bucket algorithm (Bucket4j) with Redis as the store works across multiple app instances.
<dependency>
<groupId>com.bucket4j</groupId>
<artifactId>bucket4j-redis</artifactId>
<version>8.10.0</version>
</dependency>
@Configuration
public class RateLimitConfig {
@Bean
public ProxyManager<String> proxyManager(LettuceConnectionFactory cf) {
RedisClient client = RedisClient.create("redis://localhost:6379");
return Bucket4jLettuce.casBasedBuilder(client).build();
}
}
@Component
@RequiredArgsConstructor
public class RateLimitingFilter extends OncePerRequestFilter {
private final ProxyManager<String> buckets;
private Bucket resolveBucket(String key, int capacity, Duration window) {
BucketConfiguration cfg = BucketConfiguration.builder()
.addLimit(Bandwidth.classic(capacity,
Refill.intervally(capacity, window)))
.build();
return buckets.builder().build(key, () -> cfg);
}
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse resp,
FilterChain chain) throws IOException, ServletException {
String apiKey = Optional.ofNullable(req.getHeader("X-API-Key"))
.orElse(req.getRemoteAddr());
Bucket bucket = resolveBucket("rl:" + apiKey, 100, Duration.ofMinutes(1));
ConsumptionProbe probe = bucket.tryConsumeAndReturnRemaining(1);
resp.setHeader("X-RateLimit-Limit", "100");
resp.setHeader("X-RateLimit-Remaining", String.valueOf(probe.getRemainingTokens()));
if (probe.isConsumed()) {
chain.doFilter(req, resp);
} else {
long retryAfter = probe.getNanosToWaitForRefill() / 1_000_000_000L;
resp.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
resp.setHeader("Retry-After", String.valueOf(retryAfter));
resp.getWriter().write("{\"error\":\"Too many requests\"}");
}
}
}
// Per-endpoint limits via annotation + AOP (sketch)
@RateLimit(capacity = 10, window = "1m")
@PostMapping("/auth/login")
public ResponseEntity<?> login(@RequestBody LoginRequest req) { ... }
- Token bucket is the usual pick: burst up to
capacity, refillNtokens per window. Simple, fair, and easy to communicate to API consumers. - Use Redis (or a similar shared store) — in-memory limits in each pod under-count a user making requests to multiple instances behind a load balancer.
- Key the bucket by the strongest identity you have, in priority order: authenticated user → API key → IP. Never rely on IP alone (NAT and proxies share addresses).
- Always emit
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset, and on429setRetry-After— clients then back off politely instead of hammering. - For gateway-level limits, Spring Cloud Gateway's
RequestRateLimiterfilter (also backed by Redis) avoids adding a filter per service.
Common Mistakes & Pitfalls
Thinking SockJS is only for old browsers — corporate firewalls and HTTP proxies that don't support WebSocket upgrades are a more common real-world issue.
Check your understanding
Answer these without looking back at the article.
- How do you define a Protocol Buffer schema and generate Java code from it?
- How do you secure a REST API? Explain CORS and CSRF in the context of REST APIs.
- How do you implement rate limiting in a Spring Boot REST API?
Related interview prep categories
1Want a guided curriculum order?
These modules are also sequenced in our flagship backend interview track — open it when you prefer an ordered path instead of browsing by topic.