router.use() silently accepts express() sub-apps without prototype restoration · Issue #7427 · expressjs/express · GitHub
Skip to content

router.use() silently accepts express() sub-apps without prototype restoration #7427

Open

Description

@synapes620

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)
}

Activity

  1. added theissue type on Aug 25, 2026
  2. krzysdz commented on Aug 25, 2026

    @krzysdz
    Contributor

    This is expected. Mounting sub-apps works only on an Application object. The mount event docs mention that it is fired on a sub-app, when it is mounted on a parent app. Similarly app.use() docs mention sub-apps, but the docs for router.use() don't explicitly mention them (only "middleware functions").

    Since router.use() does not expect an app object, it treats it as middleware. If middleware modifies the req/res objects, these modifications should persist.

    Refs:

  3. removed theissue type on Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions