Highlights
🐞 Bug fixes
-
Fixed HTTP header conflict between Content-Length and Transfer-Encoding in res.send - by @YuryShkoda in #4893
Fixed the behavior of
res.send()to prevent conflicts betweenContent-LengthandTransfer-EncodingHTTP headers in responses. TheContent-Lengthheader inres.send()is now only added when aTransfer-Encodingheader is not present, complying with the HTTP specification that states both headers should not coexist in the same response. ETag generation is unaffected by the presence of aTransfer-Encodingheader - by @cuishuang in #7459
-
Upgrade
qsto^6.16.0, which fixes CVE-2026-2391 (GHSA-w7fw-mjwx-w883), CVE-2026-82417 (GHSA-4mjr-xmp4-gh2g) and CVE-2026-82562 (GHSA-x5fp-wj9c-mxmx):arrayLimitbypasses in comma parsing and a denial of service via an attacker-controlledisBuffer- by @davetashner in #7057 and #7478, and @cyphercodes in #7305 -
Upgrade
proxy-addrto^2.0.8, which fixes CVE-2026-90711 - by @lazerg in #7474
🚀 Improvements
-
Allow conditional revalidation for QUERY requests.
req.freshpreviously only validated freshness for GET and HEAD requests, so QUERY responses never returned 304 despite a matching validator. Since QUERY is a safe, idempotent, and cacheable method that supports conditional requests, it is now included in the freshness check - by @Cherry in #7366// QUERY /reports with If-None-Match: "12345" app.query('/reports', (req, res) => { res.set('ETag', '"12345"'); res.send(results); // now responds 304 Not Modified });
-
Improve HTML structure in
res.redirect()responses when HTML format is accepted by adding<!DOCTYPE html>,<title>, and<body>tags for better browser compatibility - by @Bernice55231 in #5167 -
When calling
app.renderwith options set to null, the locals object is handled correctly, preventing unexpected errors and making the method behave the same as when options is omitted or an empty object is passed - by AkaHarshit in #6903app.render('index', null, callback); // now works as expected
-
Upgrade
content-typeto^2.0.0, bringing a faster parser (~1.5x quickerContent-Typeparsing/formatting inres.send()) along with a behavior change:res.send()now keeps any existing parameters when adding the charset and no longer throws on aContent-Typethat fails to parse.type-isis upgraded to^2.1.0as part of the same change - by @blakeembrey in #7234res.set('Content-Type', 'text/plain; foo=bar').send('hey'); // -> Content-Type: text/plain; foo=bar; charset=utf-8
-
The default error handler now logs the full error object instead of only its stack trace, so nested details such as
Error.causeand library-specific properties (e.g. Sequelize'sparent/original) are no longer swallowed - by @Nitin-Mohapatra in #6464 -
Upgrade
content-dispositionto^2.0.1, which changes theContent-Dispositionheader emitted byres.download(),res.attachment(), andres.sendFile(): file names that are valid HTTP tokens are no longer wrapped in quotes. This is equivalent per RFC 6266, but applications asserting on the exact header bytes should update their expectations - by @blakeembrey in #7233res.attachment('user.html'); // before -> Content-Disposition: attachment; filename="user.html" // after -> Content-Disposition: attachment; filename=user.html
-
Upgrade
body-parserto^2.3.0, which fixes CVE-2026-12590 (GHSA-v422-hmwv-36x6): an invalidlimitoption value caused request body size enforcement to be silently disabled (fail-open), allowing a denial of service via arbitrarily large payloads. Invalidlimitvalues now throw at parser initialization instead of being ignored - by @Mayvis in #7390
⚡ Performance
- Avoid duplicate Content-Type header processing in
res.send()when sending string responses without an explicit Content-Type header - by @bjohansebas in #6991
What's Changed
- 📝 add note to history by @ctcpip in #6946
- docs: add @rxmarbles to triagers by @rxmarbles in #6953
- docs: use global Security policy by @UlisesGascon in #6570
- doc: fix security.md link to point to security tab by @jonchurch in #6976
- build(deps): bump actions/download-artifact from 6.0.0 to 7.0.0 by @dependabot[bot] in #6961
- fix: enhance req.acceptsCharsets method by @Abdel-Monaam-Aouini in #6088
- build(deps): bump actions/upload-artifact from 5.0.0 to 6.0.0 by @dependabot[bot] in #6965
- build(deps): bump github/codeql-action from 4.31.6 to 4.31.9 by @dependabot[bot] in #6964
- fix(deps): update version lib qs by @gabrieel1007 in #6969
- build(deps): bump actions/checkout from 6.0.0 to 6.0.1 by @dependabot[bot] in #6963
- build(deps): bump actions/setup-node from 6.0.0 to 6.1.0 by @dependabot[bot] in #6962
- nominate @krzysdz to triage team by @bjohansebas in #6482
- Polish HTML structure of the response in the res.redirect() function by @Bernice55231 in #5167
- docs: fix JSDoc for req.accepts() return value and parameter format by @marcosmol204 in #6936
- test: added unit tests for utils.compileETag to cover valid and invalid inputs by @sukdev24 in #6534
- chore: remove benchmarks directory by @bjohansebas in #6992
- feat: do not modify the Content-Type twice when sending strings by @bjohansebas in #6991
- feat: Allow passing null or undefined as the value for options in app.render by @AkaHarshit in #6903
- test: add test for normalizeType fallback when mime lookup fails by @Ayoub-Mabrouk in #6894
- fix: search example to support Redis v4+ and Express 4/5 by @vinybrun in #6274
- docs: Add @GroophyLifefor to the triage team by @bjohansebas in #6995
- build(deps): bump actions/checkout from 6.0.1 to 6.0.2 by @dependabot[bot] in #7011
- build(deps): bump actions/setup-node from 6.1.0 to 6.2.0 by @dependabot[bot] in #7012
- build(deps): bump github/codeql-action from 4.31.9 to 4.32.0 by @dependabot[bot] in #7013
- Remove duplicate tests in res.location and res.jsonp by @GroophyLifefor in #6996
- fix: bump qs minimum to ^6.14.2 for CVE-2026-2391 by @davetashner in #7057
- docs: fix README security policy by @pavan-sh in #7029
- include edge case tests for
res.type()by @IshitaSingh0822 in #7037 - build(deps): bump github/codeql-action from 4.32.0 to 4.32.4 by @dependabot[bot] in #7072
- build(deps): bump actions/download-artifact from 7.0.0 to 8.0.0 by @dependabot[bot] in #7073
- build(deps): bump actions/upload-artifact from 6.0.0 to 7.0.0 by @dependabot[bot] in #7074
- docs: replace dummy with placeholder in example comments by @samtuckerdavis in #7064
- docs: remove dead link from Readme by @ayushshukla1807 in #7136
- fix: update deprecated npm install docs URL in Readme.md by @Vansh1811 in #7159
- build(deps): bump actions/download-artifact from 8.0.0 to 8.0.1 by @dependabot[bot] in #7148
- build(deps): bump actions/setup-node from 6.2.0 to 6.3.0 by @dependabot[bot] in #7149
- build(deps): bump github/codeql-action from 4.32.4 to 4.35.1 by @dependabot[bot] in #7150
- fixed typo in history.md by @opensourcezeal in #7191
- chore: ensure safe config in the npmrc by @sheplu in #7144
- ci: build express with node.js v26 by @shivarm in #7218
- build(deps): bump actions/setup-node from 6.3.0 to 6.4.0 by @dependabot[bot] in #7210
- build(deps): bump actions/upload-artifact from 7.0.0 to 7.0.1 by @dependabot[bot] in #7211
- build(deps): bump github/codeql-action from 4.35.1 to 4.35.2 by @dependabot[bot] in #7212
- fix: bump qs minimum to 6.15.2 by @cyphercodes in #7305
- Upgrade
content-typeby @blakeembrey in #7234 - Improve error logging by logging full error object by @Nitin-Mohapatra in #6464
- fix: replace deprecated trimRight() with trimEnd() by @tejgokani in #7265
- build(deps): bump github/codeql-action from 4.35.2 to 4.36.0 by @dependabot[bot] in #7297
- Upgrade
content-dispositionby @blakeembrey in #7233 - fix(res.send): add Content-Length header only if Transfer-Encoding is not present by @YuryShkoda in #4893
- docs: use the new logo by @bjohansebas in #7316
- build(deps): bump actions/checkout from 6.0.2 to 7.0.0 by @dependabot[bot] in #7345
- build(deps-dev): bump morgan from 1.10.1 to 1.11.0 by @dependabot[bot] in #7353
- feat: allow conditional revalidation for QUERY requests by @Cherry in #7366
- build(deps-dev): bump hbs from 4.2.0 to 4.2.1 by @dependabot[bot] in #7152
- deps: bump body-parser to ^2.3.0 to fix CVE-2026-12590 by @Mayvis in #7390
- build(deps): bump actions/checkout from 7.0.0 to 7.0.1 by @dependabot[bot] in #7403
- docs(res.location): clean up deprecated back string references by @spellsaif in #7406
- build(deps): bump coverallsapp/github-action from 2.3.7 to 2.3.8 by @dependabot[bot] in #7399
- build(deps): bump github/codeql-action/upload-sarif from 4.36.0 to 4.37.3 by @dependabot[bot] in #7400
- chore: group github actions updates by @Phillip9587 in #7460
- build(deps-dev): bump hbs from 4.2.1 to 4.3.0 by @dependabot[bot] in #7450
- build(deps): bump the github-actions group with 5 updates by @dependabot[bot] in #7462
- build(deps-dev): bump morgan from 1.11.0 to 1.12.0 by @dependabot[bot] in #7461
- docs: fix capitalization of GitHub Discussions in Readme by @Kimkoungsoo in #7426
- ci: add npm staged publication with dist-tag support by @UlisesGascon in #7464
- fix(res.send): preserve ETag generation with Transfer-Encoding by @cuishuang in #7459
- deps: proxy-addr@^2.0.8 by @lazerg in #7474
- deps: bump qs minimum to 6.16.0 by @davetashner in #7478
- build(deps-dev): bump morgan from 1.12.0 to 1.12.1 by @dependabot[bot] in #7493
- docs: add missing apostrophe in "can\u0027t" (lib + example) by @haimingZZ in #7483
- build(deps-dev): bump hbs from 4.3.0 to 4.3.1 by @dependabot[bot] in #7500
- build(deps): bump the github-actions group with 3 updates by @dependabot[bot] in #7499
- docs: fix pass-through typos by @whoalin1 in #7509
- docs: fix typos and a stale API reference in lib comments by @fmtappendf in #7512
- Release: 5.3.0 by @UlisesGascon in #7468
New Contributors
- @rxmarbles made their first contribution in #6953
- @gabrieel1007 made their first contribution in #6969
- @Bernice55231 made their first contribution in #5167
- @marcosmol204 made their first contribution in #6936
- @sukdev24 made their first contribution in #6534
- @AkaHarshit made their first contribution in #6903
- @vinybrun made their first contribution in #6274
- @GroophyLifefor made their first contribution in #6996
- @davetashner made their first contribution in #7057
- @pavan-sh made their first contribution in #7029
- @IshitaSingh0822 made their first contribution in #7037
- @samtuckerdavis made their first contribution in #7064
- @ayushshukla1807 made their first contribution in #7136
- @Vansh1811 made their first contribution in #7159
- @opensourcezeal made their first contribution in #7191
- @cyphercodes made their first contribution in #7305
- @Nitin-Mohapatra made their first contribution in #6464
- @tejgokani made their first contribution in #7265
- @YuryShkoda made their first contribution in #4893
- @Mayvis made their first contribution in #7390
- @spellsaif made their first contribution in #7406
- @Kimkoungsoo made their first contribution in #7426
- @cuishuang made their first contribution in #7459
- @haimingZZ made their first contribution in #7483
- @whoalin1 made their first contribution in #7509
- @fmtappendf made their first contribution in #7512
Full Changelog: v5.2.1...v5.3.0