🚦 fix: ERR_ERL_INVALID_IP_ADDRESS and IPv6 Key Collisions in IP Rate Limiters#12319
Merged
danny-avila merged 3 commits intoMar 20, 2026
Merged
Conversation
Six IP-based rate limiters are missing the `keyGenerator: removePorts` option that is already used by the auth-related limiters (login, register, resetPassword, verifyEmail). Without it, reverse proxies that include ports in X-Forwarded-For headers cause ERR_ERL_INVALID_IP_ADDRESS errors from express-rate-limit. Fixes danny-avila#12318
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes ERR_ERL_INVALID_IP_ADDRESS errors for IP-based express-rate-limit middleware when LibreChat is deployed behind reverse proxies that include port numbers in X-Forwarded-For/req.ip. It does this by applying the existing removePorts key generator (already used by auth limiters) to the remaining IP-based limiters for consistent behavior.
Changes:
- Imported
removePortsfrom~/server/utilsin six limiter modules. - Added
keyGenerator: removePortsto each IP-based limiter options object to normalizereq.ipvalues that include ports.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| api/server/middleware/limiters/uploadLimiters.js | Add removePorts as keyGenerator for the file-upload IP limiter. |
| api/server/middleware/limiters/messageLimiters.js | Add removePorts as keyGenerator for the message IP limiter. |
| api/server/middleware/limiters/ttsLimiters.js | Add removePorts as keyGenerator for the TTS IP limiter. |
| api/server/middleware/limiters/sttLimiters.js | Add removePorts as keyGenerator for the STT IP limiter. |
| api/server/middleware/limiters/importLimiters.js | Add removePorts as keyGenerator for the import IP limiter. |
| api/server/middleware/limiters/forkLimiters.js | Add removePorts as keyGenerator for the fork IP limiter. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The original regex `/:\d+[^:]*$/` treated the last colon-delimited segment of bare IPv6 addresses as a port, mangling valid IPs (e.g. `::1` → `::`, `2001:db8::1` → `2001:db8::`). Distinct IPv6 clients could collapse into the same rate-limit bucket. Use `net.isIP()` as a fast path for already-valid IPs, then match bracketed IPv6+port and IPv4+port explicitly. Bare IPv6 addresses are now returned unchanged. Also fixes pre-existing property ordering inconsistency in ttsLimiters.js userLimiterOptions (keyGenerator before store).
danny-avila
force-pushed
the
fix/rate-limiter-ip-keyGenerator
branch
3 times, most recently
from
March 19, 2026 23:48
2f32798 to
38a8842
Compare
…order - Move removePorts implementation to packages/api/src/utils/removePorts.ts with proper Express Request typing - Reduce api/server/utils/removePorts.js to a thin re-export from @librechat/api for backward compatibility - Consolidate removePorts import with limiterCache from @librechat/api in all 6 limiter files, fixing import order (package imports shortest to longest, local imports longest to shortest) - Remove narrating inline comments per code style guidelines
danny-avila
force-pushed
the
fix/rate-limiter-ip-keyGenerator
branch
from
March 20, 2026 00:13
38a8842 to
baa3b29
Compare
danny-avila
approved these changes
Mar 20, 2026
danny-avila
approved these changes
Mar 20, 2026
1 task
3 tasks
jcbartle
pushed a commit
to jcbartle/LibreChat
that referenced
this pull request
May 11, 2026
…Limiters (danny-avila#12319) * fix: Add removePorts keyGenerator to all IP-based rate limiters Six IP-based rate limiters are missing the `keyGenerator: removePorts` option that is already used by the auth-related limiters (login, register, resetPassword, verifyEmail). Without it, reverse proxies that include ports in X-Forwarded-For headers cause ERR_ERL_INVALID_IP_ADDRESS errors from express-rate-limit. Fixes danny-avila#12318 * fix: make removePorts IPv6-safe to prevent rate-limit key collisions The original regex `/:\d+[^:]*$/` treated the last colon-delimited segment of bare IPv6 addresses as a port, mangling valid IPs (e.g. `::1` → `::`, `2001:db8::1` → `2001:db8::`). Distinct IPv6 clients could collapse into the same rate-limit bucket. Use `net.isIP()` as a fast path for already-valid IPs, then match bracketed IPv6+port and IPv4+port explicitly. Bare IPv6 addresses are now returned unchanged. Also fixes pre-existing property ordering inconsistency in ttsLimiters.js userLimiterOptions (keyGenerator before store). * refactor: move removePorts to packages/api as TypeScript, fix import order - Move removePorts implementation to packages/api/src/utils/removePorts.ts with proper Express Request typing - Reduce api/server/utils/removePorts.js to a thin re-export from @librechat/api for backward compatibility - Consolidate removePorts import with limiterCache from @librechat/api in all 6 limiter files, fixing import order (package imports shortest to longest, local imports longest to shortest) - Remove narrating inline comments per code style guidelines --------- Co-authored-by: Danny Avila <danny@librechat.ai>
ThomasVuNguyen
pushed a commit
to ThomasVuNguyen/LibreChat
that referenced
this pull request
Jul 15, 2026
…Limiters (danny-avila#12319) * fix: Add removePorts keyGenerator to all IP-based rate limiters Six IP-based rate limiters are missing the `keyGenerator: removePorts` option that is already used by the auth-related limiters (login, register, resetPassword, verifyEmail). Without it, reverse proxies that include ports in X-Forwarded-For headers cause ERR_ERL_INVALID_IP_ADDRESS errors from express-rate-limit. Fixes danny-avila#12318 * fix: make removePorts IPv6-safe to prevent rate-limit key collisions The original regex `/:\d+[^:]*$/` treated the last colon-delimited segment of bare IPv6 addresses as a port, mangling valid IPs (e.g. `::1` → `::`, `2001:db8::1` → `2001:db8::`). Distinct IPv6 clients could collapse into the same rate-limit bucket. Use `net.isIP()` as a fast path for already-valid IPs, then match bracketed IPv6+port and IPv4+port explicitly. Bare IPv6 addresses are now returned unchanged. Also fixes pre-existing property ordering inconsistency in ttsLimiters.js userLimiterOptions (keyGenerator before store). * refactor: move removePorts to packages/api as TypeScript, fix import order - Move removePorts implementation to packages/api/src/utils/removePorts.ts with proper Express Request typing - Reduce api/server/utils/removePorts.js to a thin re-export from @librechat/api for backward compatibility - Consolidate removePorts import with limiterCache from @librechat/api in all 6 limiter files, fixing import order (package imports shortest to longest, local imports longest to shortest) - Remove narrating inline comments per code style guidelines --------- Co-authored-by: Danny Avila <danny@librechat.ai>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Six IP-based rate limiters (
uploadLimiters,messageLimiters,ttsLimiters,sttLimiters,importLimiters,forkLimiters) are missing thekeyGenerator: removePortsoption, causingERR_ERL_INVALID_IP_ADDRESSerrors when deployed behind reverse proxies that include port numbers inX-Forwarded-Forheaders.The
removePortsutility already exists atapi/server/utils/removePorts.jsand is already used by the four auth-related limiters (loginLimiter,registerLimiter,resetPasswordLimiter,verifyEmailLimiter). This PR applies the same pattern to the remaining six IP-based limiters for consistency.Fixes #12318
Changes
For each of the 6 affected files:
const { removePorts } = require('~/server/utils');importkeyGenerator: removePortsto theipLimiterOptionsobjectTesting
ghcr.io/danny-avila/librechat-dev:latest) on Azure App Service with MongoDB AtlasERR_ERL_INVALID_IP_ADDRESSerrors behind Azure App Service reverse proxyremovePortsgracefully handles IPs without ports)