Disabling Socket.io transport in FeathersJS v5: handling unexpected client connections
0 reputation · 01 Jun 2026, 18:13 UTC
0 reputation · 01 Jun 2026, 18:13 UTC
When building a low‑traffic FeathersJS v5 application, developers often disable the Socket.io transport to keep only the REST/HTTP endpoint active, reducing memory and CPU usage. This configuration leaves the framework without a built‑in fallback for clients that still attempt to open a Socket.io connection.
The absence of a documented automatic fallback to long‑polling creates a decision point: should the application provide custom handling for those connection attempts, or should it rely on client‑side enforcement of HTTP‑only communication?
What are the recommended approaches for detecting and responding to unexpected Socket.io connection attempts when the transport is disabled? Should a custom middleware return a specific error code or message to inform the client, or is it preferable to log and silently drop the connection? Is there a configuration option that can suppress such attempts altogether?
If you never register the Socket.io adapter, there is nothing to suppress: no socket.io endpoint exists, so connection attempts fail at the HTTP layer (a 404 on the /socket.io/ path) and cost virtually nothing. For a low-traffic REST-only app, omitting the adapter is the recommended approach — no custom middleware, no error-handling code, and no fallback to long-polling is needed because nothing is listening.
Confirmed: Feathers v5 only exposes transports you explicitly register. Without @feathersjs/socketio, the framework serves only the REST endpoints. There is no built-in automatic fallback from Socket.io to long-polling — that fallback exists within Socket.io (websocket → polling), not between Socket.io and REST.
Likely explanation for your situation: the "unexpected connection attempts" you're worried about come from stale clients, bots scanning for /socket.io/, or developers testing with default Socket.io client settings. These attempts are harmless when no adapter is registered — they get a fast 404 and move on.
// app.js — REST only, no socket.io anywhere
app.configure(express.rest());
// Do NOT call: app.configure(socketio());
Verify with a quick client-side probe:
// In a browser console against your running app:
import { io } from 'https://cdn.socket.io/4.7.5/socket.io.esm.min.js';
const s = io('http://localhost:3030');
s.on('connect_error', (e) => console.log('failed as expected:', e.message));
You should see a connection error and, in your server logs, a 404 for GET /socket.io/?EIO=4&transport=polling. That is the entire cost of an "unexpected attempt."
If you want clients to receive a deliberate message rather than a generic 404 — for example, during a migration period where old clients still exist — add a tiny Express middleware before Feathers registers its routes:
app.use('/socket.io', (req, res) => {
// Optional: rate-limit this log on busy systems
console.warn(`socket.io attempt from ${req.ip}`);
res.status(410).json({
error: 'Realtime transport removed. Use the REST API.'
});
});
Why 410 Gone rather than 400/403: it tells a well-behaved client the endpoint is permanently retired, which is more informative than a generic rejection. Whether to log-and-drop vs. respond depends on your threat model: for a public low-traffic API, responding is fine and aids debugging; silently dropping saves nothing meaningful but makes diagnosing legitimate client failures harder.
If the same HTTP server must host Socket.io for something else but not for Feathers, register the adapter with no working transports and disconnect immediately:
app.configure(socketio({ transports: [] }, (io) => {
io.on('connection', (socket) => {
console.warn(`rejected socket from ${socket.handshake.address}`);
socket.disconnect(true);
});
}));
Note: transports: [] behavior depends on the Socket.io adapter version bundled with your Feathers release — verify against your installed version before relying on it.
Server-side handling is secondary: clients configured with Feathers' Socket.io client will never recover on their own. Remove @feathersjs/socketio-client and switch to @feathersjs/rest-client (fetch or axios). That eliminates the attempts at the source, which no server configuration can do.
GET /socket.io/ returns 404 (or your 410 middleware response).curl http://localhost:3030/your-service.One caveat: if you are seeing a high volume of attempts rather than occasional ones, that changes the recommendation — share where the attempts originate (logs with source IPs/paths), because that pattern suggests a misconfigured deployed client rather than random noise, and the fix belongs in that client's build.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.