You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Vite dev server: proxy config normalization reorders glob keys, drops string context and misses URLs with a query string #34257
Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
The webpack-based @angular-devkit/build-angular:dev-server
Description
normalizeProxyConfiguration in packages/angular/build/src/utils/load-proxy-config.ts turns the proxy file into the object Vite expects. Three things get lost along the way. In each case the request isn't proxied and falls through to the app (index.html), and nothing shows up in the ng serve output.
Glob entries lose their position. Each glob key is replaced with normalizedProxy[pattern] = …; delete normalizedProxy[key], which moves it to the end of the object. Vite uses the first entry that matches, so a specific glob written first loses to a broader plain key written after it.
A string context drops the whole entry. In the array form, an entry whose context isn't an array is skipped. webpack-dev-server passes context to http-proxy-middleware as pathFilter, which accepts a single string, so context: '/legacy' works there. The array form conversion was added after proxyConfig option is not working with esbuild dev server #25337 so that webpack-style configs keep working, and this case slipped through.
Globs are matched against the URL including its query string. The regex built by picomatch ends with $ and Vite tests it against req.url, so /search/** matches /search but not /search?q=angular. http-proxy-middleware matches both globs and plain keys against the pathname only.
Minimal Reproduction
A new app served by @angular/build:dev-server, plus a backend that echoes the port and URL it receives:
curl localhost:4200/search is proxied, but curl 'localhost:4200/search?q=angular' returns index.html.
Exception or Error
None. The requests are just not proxied.
Your Environment
Angular CLI : 22.2.0
Angular : 22.2.0
Node.js : 24.21.0
Package Manager : npm 11.19.0
Operating System : darwin arm64
@angular/build : 22.2.0
@angular/cli : 22.2.0
vite : 8.3.0
The code is the same on main.
Anything else relevant?
#34258 fixes the first two points. The third one isn't part of it: for a trailing /**, picomatch puts a $ inside the generated regex, so it takes more than appending an optional query string, and it changes which requests get proxied.
Command
serve
Is this a regression?
The previous version in which this bug was not present was
The webpack-based
@angular-devkit/build-angular:dev-serverDescription
normalizeProxyConfigurationinpackages/angular/build/src/utils/load-proxy-config.tsturns the proxy file into the object Vite expects. Three things get lost along the way. In each case the request isn't proxied and falls through to the app (index.html), and nothing shows up in theng serveoutput.normalizedProxy[pattern] = …; delete normalizedProxy[key], which moves it to the end of the object. Vite uses the first entry that matches, so a specific glob written first loses to a broader plain key written after it.contextdrops the whole entry. In the array form, an entry whosecontextisn't an array is skipped. webpack-dev-server passescontextto http-proxy-middleware aspathFilter, which accepts a single string, socontext: '/legacy'works there. The array form conversion was added after proxyConfig option is not working with esbuild dev server #25337 so that webpack-style configs keep working, and this case slipped through.$and Vite tests it againstreq.url, so/search/**matches/searchbut not/search?q=angular. http-proxy-middleware matches both globs and plain keys against the pathname only.Minimal Reproduction
A new app served by
@angular/build:dev-server, plus a backend that echoes the port and URL it receives:1. Order with
ng serve --proxy-config proxy.conf.json:{ "/api/auth/**": {"target": "http://localhost:3998"}, "/api/": {"target": "http://localhost:3999"} }curl localhost:4200/api/auth/loginreturns3999 got /api/auth/login. I expected it to go to 3998.2. String
contextwithng serve --proxy-config proxy.conf.mjs:curl localhost:4200/legacy/usersreturnsindex.html.loadProxyConfigurationreturns{}for this file.3. Query string with
ng serve --proxy-config proxy.conf.json:{ "/search/**": {"target": "http://localhost:3999"} }curl localhost:4200/searchis proxied, butcurl 'localhost:4200/search?q=angular'returnsindex.html.Exception or Error
Your Environment
The code is the same on
main.Anything else relevant?
#34258 fixes the first two points. The third one isn't part of it: for a trailing
/**, picomatch puts a$inside the generated regex, so it takes more than appending an optional query string, and it changes which requests get proxied.