router.use() silently accepts express() sub-apps without prototype restoration
When an express() sub-app is mounted via router.use() instead of app.use(), the request and response prototypes are swapped by app.handle but never restored. The sub-app also never fires the mount event, so it does not inherit the parent's trust proxy setting. Any middleware that runs after the sub-app calls next() reads req.ip, req.secure, and req.hostname under the wrong app's trust policy, silently and without any error.
app.handle (lib/application.js:169-170) performs:
js Object.setPrototypeOf(req, this.request) Object.setPrototypeOf(res, this.response)
The only code that reverses this swap is the mounted_app closure built by app.use at lib/application.js:230-237. router.use never builds that closure, so the swap is permanent for the lifetime of the request.
The router already saves and restores request state across boundaries at index.js:171:
js let done = restore(callback, req, 'baseUrl', 'next', 'params')
The prototype is simply not on the list.
Add proto (or Object.getPrototypeOf equivalents) to the restore call so the router's own cleanup undoes the prototype swap on every exit path — next(), next(err), and unhandled throws alike.
js // index.js — inside Router.prototype.handle, before the loop var reqProto = Object.getPrototypeOf(req) var resProto = Object.getPrototypeOf(res)
var done = restore(callback, req, 'baseUrl', 'next', 'params') var _done = done done = function(err) { Object.setPrototypeOf(req, reqProto) Object.setPrototypeOf(res, resProto) _done(err) }
Source: expressjs/express