Comments (19)
This was introduced in v14.1.1 stable release.
from next.js.
Yeah I thought this issue would be picked up quickly as I think it's a pretty fundamental issue/regression.
It made it off canary and into a stable release so surely other people are going to be affected by this.
from next.js.
I am facing a similar issue while running playwright tests against a standalone export in a gitlab build pipeline.
uitests:playwright: [WebServer] failed to get redirect response TypeError: fetch failed
uitests:playwright: at Object.fetch (node:internal/deps/undici/undici:11576:11)
uitests:playwright: at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
uitests:playwright: at async globalThis.fetch (/builds/xxx/xxx/apps/uitest/.next/server/chunks/369.js:1:36492)
uitests:playwright: at async rp (/builds/xxx/xxx/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:15:4325)
uitests:playwright: at async rm (/builds/xxx/xxx/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:16:741)
uitests:playwright: at async rq (/builds/xxx/xxx/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:18:1249)
uitests:playwright: at async doRender (/builds/xxx/xxx/node_modules/next/dist/server/base-server.js:1376:30)
uitests:playwright: at async cacheEntry.responseCache.get.routeKind (/builds/xxx/xxx/node_modules/next/dist/server/base-server.js:1537:28)
uitests:playwright: at async NextNodeServer.renderToResponseWithComponentsImpl (/builds/xxx/xxx/node_modules/next/dist/server/base-server.js:1445:28)
uitests:playwright: at async NextNodeServer.renderPageComponent (/builds/xxx/xxx/node_modules/next/dist/server/base-server.js:1842:24) {
uitests:playwright: cause: Error: connect EADDRNOTAVAIL :::3000 - Local (:::0)
uitests:playwright: at internalConnect (node:net:1100:16)
uitests:playwright: at defaultTriggerAsyncIdScope (node:internal/async_hooks:463:18)
uitests:playwright: at node:net:1306:9
uitests:playwright: at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
uitests:playwright: errno: -99,
uitests:playwright: code: 'EADDRNOTAVAIL',
uitests:playwright: syscall: 'connect',
uitests:playwright: address: '::',
uitests:playwright: port: 3000
uitests:playwright: }
uitests:playwright: }
from next.js.
Maybe caused by #62561? I am experiencing a similar issue in 14.1.1-canary.75
but not in 14.1.1-canary.74
.
Edit: Oh sorry I did not read the description 😓.
from next.js.
The problem here is not only with the hostname but also with the protocol. When running behind an nginx on https:
- Before #62561 the protocol is
https
and the hostnamemysite.com
and the redirects work fine. - After #62561 the protocol is still
https
but the hostname is0.0.0.0:3000
which listens on http and not on https (this is handled by nginx). This causes the redirects to break withfailed to get redirect response TypeError: fetch failed
+ERR_SSL_WRONG_VERSION_NUMBER
.
I am debating whether or not to create a separate issue for this, since the root cause is the same one.
from next.js.
I reported a separate issue: #63114. Same root cause.
from next.js.
@shuding Maybe the error i am running into is this one: #62953
from next.js.
I'm not sure but I think I'm seeing the same issue (ERR_SSL_WRONG_VERSION_NUMBER
) when using redirect
in a server action when running locally, specifically when following this supabase + Next.js auth flow doc:
![image](https://private-user-images.githubusercontent.com/11214/320140228-fa161052-c686-4c37-acfa-93ba3b4a3e46.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTY0NDA0OTEsIm5iZiI6MTcxNjQ0MDE5MSwicGF0aCI6Ii8xMTIxNC8zMjAxNDAyMjgtZmExNjEwNTItYzY4Ni00YzM3LWFjZmEtOTNiYTNiNGEzZTQ2LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA1MjMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNTIzVDA0NTYzMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWVhZDdmYzg1YTJiMDljOTcxNzBhOTU0OTQ3ODA1NmZjMTI2OGJjZjExNGVkNjgwZjcyMWI1ZmIwN2U3ZDg0MjAmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.uxtWZ4asCE0TRIl5jABh6d68Qag4Q0Pq1wF7UojOMgk)
![image](https://private-user-images.githubusercontent.com/11214/320140288-a15ef94c-9a45-4a57-93d9-651cb8933800.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTY0NDA0OTEsIm5iZiI6MTcxNjQ0MDE5MSwicGF0aCI6Ii8xMTIxNC8zMjAxNDAyODgtYTE1ZWY5NGMtOWE0NS00YTU3LTkzZDktNjUxY2I4OTMzODAwLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA1MjMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNTIzVDA0NTYzMVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTQ2MWI2NzA0MGE2YWUxMGEyN2U3M2ZjZDhhMzAxNjk2NjRkMzY4NDhkYzJiMDkwNzA2MmM1ZWNlODMwZjE3NWMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.AZyUzBR6YeojNUVE9PyDjSuwZctfZSmgbXpS3lL_5-w)
from next.js.
Hey @shuding, I've just upgraded to canary and redirects are now not working as before.
When calling redirect from a subdomain and logging request headers:
Previous versions: The host was subdomain.localhost:3000
Canary version: Host is localhost:3000
For additional context I am using middleware to rewrite requests. I need to get the subdomain of the incoming request to route it to /app/[tenant]/...
.
I see that x-forwarded-host
in the canary versions does include the subdomain, is it better to use this to determine the subdomain of an incoming request?
from next.js.
@kevinmitch14 Hey Kevin, thanks for the info above. Is this specific to multi tenant apps, or it breaks all cases? I think the #63389 fix still doesn't cover these multi tenant cases yet.
The reason that we moved away from request headers (host
or x-forwarded-host
) is that the requester side can specify any host via these headers, including malicious ones. And the redirection request to that host happens on the server side which can be risky.
One work around idea might be adding an extra header that specifies the original request host/forwarded-host, to the redirection request. So the middleware can know its original host even if the request itself is just localhost:3000
. What do you think?
from next.js.
Hey @shuding, here is a barebones version of the middleware. The issue appears when I use redirect()
because the host passed to getSubdomain()
no longer has the subdomain. It's just localhost:3000
instead of tenant.localhost:3000
Because tenants live on subdomains, when the host is localhost:3000 I just return NextResponse.next()
, which results in the "wrong" redirect. Previously the subdomain is extracted and appended to the pathname
before rewriting.
export function middleware(request: NextRequest) {
const url = request.nextUrl.clone();
const subdomain = getSubdomain(request.headers.get("host"));
const path = url.pathname;
console.log({ subdomain, path });
if (subdomain === "localhost:3000") {
return NextResponse.next();
}
const destinationUrl = url.clone();
destinationUrl.pathname = `/${subdomain}${path}`;
return NextResponse.rewrite(destinationUrl);
}
In my use-case, the tenant will always live on a subdomain (tenant1.example.com
) would equate to tenant1
. How I know which tenant was making the request was through the host header in middleware, as this is the first entry point into the app.
A request to tenant1.example.com/blog
would go through middleware and be rewritten to /[tenant1]/blog
. In the layout I do a check to see if this is a valid tenant via a DB call, if not, notFound()
is invoked.
The initial way I find the current tenant is through middleware via the request host header. Is this part dangerous or is it just the redirection aspect that is considered potentially dangerous?
Just to be clear, you recommend something like the following:
User enters app tenant1.example.com
- Middleware invoked - request
x-forwarded-host : tenant1.example.com
. - Do check if
tenant1
is a valid tenant. (Is it signed up to the product) - Do auth check if user is valid user of
tenant1
- If above are all true, respond with rewrite and append header
x-active-tenant : tenant1
- For each request in middleware, check for presence of
x-active-tenant
- If present, skip step 2. Otherwise, go through all steps.
User completes action w/server action redirect:
- Read
x-active-tenant
from headers, it should be present at this stage if the user is in the app. - If not present, use the
x-forwarded-header
to determine the subdomain and check if valid tenant. - Middleware will receive this request with
host : example.com
,x-forwarded-host : tenant1.example.com
- For security Middleware will read
x-active-tenant
to determine the subdomain to use for rewrite.
Having a read of some docs here I also found this:
As an additional protection Server Actions in Next.js 14 also compares the Origin header to the Host header (or X-Forwarded-Host). If they don't match, the Action will be rejected. In other words, Server Actions can only be invoked on the same host as the page that hosts it. Very old unsupported and outdated browsers that don't support the Origin header could be at risk.
Does this have any relevance? Also a bit confusing, "Host header (or X-Forwarded-Host)", when using a subdomain, the host and forwarded host will be different anyway?
from next.js.
I think multi-tenant is definitely an area for improvement in docs + education.
Here is Next.js "recommended architecture" for Multi-tenant apps: https://github.com/vercel/platforms.
And the middleware specifically, similar to ours, uses the host from the incoming request headers - https://github.com/vercel/platforms/blob/main/middleware.ts
from next.js.
Just tried 14.2.0-canary.29
. Everything works in dev mode, but in a standalone export I am now getting a client-side exception. No issues with 14.1.0. But maybe that's a new regression, and has nothing to do wit this issue. :/
Application error: a client-side exception has occurred (see the browser console for more information).
from next.js.
Application error: a client-side exception has occurred (see the browser console for more information).
So is there more info in the browser console you can post here?
from next.js.
@blizzy78 All I have is the minified react error, which tells me to use the dev environment for full errors.
However in dev mode everything runs fine. The error only occurs in a production standalone build.
7023-678442bb9053eb7a.js:1 Error: Minified React error #130; visit https://react.dev/errors/130?args[]=object&args[]= for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
at iz (fd9d1056-8215d544d1fd3378.js:1:113022)
at d (fd9d1056-8215d544d1fd3378.js:1:34851)
at fd9d1056-8215d544d1fd3378.js:1:37511
at u (fd9d1056-8215d544d1fd3378.js:1:37802)
at fd9d1056-8215d544d1fd3378.js:1:38869
at lD (fd9d1056-8215d544d1fd3378.js:1:57332)
at iZ (fd9d1056-8215d544d1fd3378.js:1:121359)
at ia (fd9d1056-8215d544d1fd3378.js:1:95165)
at fd9d1056-8215d544d1fd3378.js:1:94987
at il (fd9d1056-8215d544d1fd3378.js:1:94994)
from next.js.
@dawi Would you mind explaining a bit about your set up? In which context did it throw and is your app relying on the host headers somewhere?
from next.js.
@shuding I had a closer look using 14.2.0-canary.33
and was able to identify the component that causes the error.
As I already suspected, the error is caused be another change. The routing issues seems to be fixed. :)
The new issue seems to be caused by a change in how the standalone export is build.
We have an array of configuration objects like this:
import PeopleOutlineIcon from '@mui/icons-material/PeopleOutline';
import PhotoLibraryOutlinedIcon from '@mui/icons-material/PhotoLibraryOutlined';
export const APP_PARTS: AppPart[] = [
{
identifier: 'files',
icon: PhotoLibraryOutlinedIcon
},
{
identifier: 'users',
icon: PeopleOutlineIcon
}
]
One part of the configuration is an icon which is of type React.ComponentType<SvgIconProps>
.
We can use it to display the icon somewhere else in the application like this:
function AppPartEntry(props: { appPart: AppPart }) {
const Icon = props.appPart.icon;
return <MenuItem ...>
<ListItemIcon>
<Icon color={'action'}/>
</ListItemIcon>
<ListItemText>
{name}
</ListItemText>
</MenuItem>;
}
Now this runs fine in dev mode with all Next.js Versions (14.1.0 - 14.2.0-canary.33).
But for production, we are using a standalone export, which does not work in 14.2.0-canary.33.
I noticed the following difference:
console.log(Icon) with v14.1.0
![with v14 1 0](https://private-user-images.githubusercontent.com/129870/314544319-7cce2fad-bcda-4ecd-bfb8-a34b86d0bda6.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTY0NDA0OTEsIm5iZiI6MTcxNjQ0MDE5MSwicGF0aCI6Ii8xMjk4NzAvMzE0NTQ0MzE5LTdjY2UyZmFkLWJjZGEtNGVjZC1iZmI4LWEzNGI4NmQwYmRhNi5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwNTIzJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDUyM1QwNDU2MzFaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1mMzNiN2E0ZjUzYmUyMDQ5OTJlY2ZiMjYyZTU4YWMwM2UyZmIwZjRkMjhkMmY5OTFhY2RkOWE4ZDkxNzVhZmU0JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.fTPTMQr8JJuNT_3h1qVkAUzIp-DEIXqRruXkuJjGhik)
console.log(Icon) with 14.2.0-canary.33
![with 14 2 0-canary 33](https://private-user-images.githubusercontent.com/129870/314544329-bde7d258-d251-4a38-b6bc-11afbe0232d7.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTY0NDA0OTEsIm5iZiI6MTcxNjQ0MDE5MSwicGF0aCI6Ii8xMjk4NzAvMzE0NTQ0MzI5LWJkZTdkMjU4LWQyNTEtNGEzOC1iNmJjLTExYWZiZTAyMzJkNy5wbmc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjQwNTIzJTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI0MDUyM1QwNDU2MzFaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT0wNDA2ZmZlM2Q0ZDc5MmVkMmI5MjFhYzNiMjY1OTBkYTE2YWI2NGE3MzlhYjFkYWFmMGUyZGJjNzM1NTc3ODBjJlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCZhY3Rvcl9pZD0wJmtleV9pZD0wJnJlcG9faWQ9MCJ9.MiHuZBsKCD9xneUPK0bKY7shRV8wTz5N8CXcOcqliNI)
from next.js.
@shuding I think this issue can be closed again.
But the client side exception (#62953) still exists in the latest canary.
from next.js.
Hey @shuding, here is a barebones version of the middleware. The issue appears when I use
redirect()
because the host passed togetSubdomain()
no longer has the subdomain. It's justlocalhost:3000
instead oftenant.localhost:3000
Because tenants live on subdomains, when the host is localhost:3000 I just return
NextResponse.next()
, which results in the "wrong" redirect. Previously the subdomain is extracted and appended to thepathname
before rewriting.export function middleware(request: NextRequest) { const url = request.nextUrl.clone(); const subdomain = getSubdomain(request.headers.get("host")); const path = url.pathname; console.log({ subdomain, path }); if (subdomain === "localhost:3000") { return NextResponse.next(); } const destinationUrl = url.clone(); destinationUrl.pathname = `/${subdomain}${path}`; return NextResponse.rewrite(destinationUrl); }In my use-case, the tenant will always live on a subdomain (
tenant1.example.com
) would equate totenant1
. How I know which tenant was making the request was through the host header in middleware, as this is the first entry point into the app.A request to
tenant1.example.com/blog
would go through middleware and be rewritten to/[tenant1]/blog
. In the layout I do a check to see if this is a valid tenant via a DB call, if not,notFound()
is invoked.The initial way I find the current tenant is through middleware via the request host header. Is this part dangerous or is it just the redirection aspect that is considered potentially dangerous?
Just to be clear, you recommend something like the following: User enters app
tenant1.example.com
- Middleware invoked - request
x-forwarded-host : tenant1.example.com
.- Do check if
tenant1
is a valid tenant. (Is it signed up to the product)- Do auth check if user is valid user of
tenant1
- If above are all true, respond with rewrite and append header
x-active-tenant : tenant1
- For each request in middleware, check for presence of
x-active-tenant
- If present, skip step 2. Otherwise, go through all steps.
User completes action w/server action redirect:
- Read
x-active-tenant
from headers, it should be present at this stage if the user is in the app.- If not present, use the
x-forwarded-header
to determine the subdomain and check if valid tenant.- Middleware will receive this request with
host : example.com
,x-forwarded-host : tenant1.example.com
- For security Middleware will read
x-active-tenant
to determine the subdomain to use for rewrite.Having a read of some docs here I also found this:
As an additional protection Server Actions in Next.js 14 also compares the Origin header to the Host header (or X-Forwarded-Host). If they don't match, the Action will be rejected. In other words, Server Actions can only be invoked on the same host as the page that hosts it. Very old unsupported and outdated browsers that don't support the Origin header could be at risk.
Does this have any relevance? Also a bit confusing, "Host header (or X-Forwarded-Host)", when using a subdomain, the host and forwarded host will be different anyway?
@shuding would be great to know if I understood your original response here!
from next.js.
Related Issues (20)
- Opentelemetry - Spans for static resources and API pages not part of page load trace
- Enabling `experimental.reactCompiler` causes hydration error when using styled-jsx
- Module not found: Can't resolve '@node-rs/argon2-wasm32-wasi' HOT 4
- __turbopack_resolve_absolute_path__ is not a function HOT 2
- window.history.pushState does not work with transitions HOT 2
- ES Modules may not assign module.exports or exports.*, Use ESM export syntax HOT 1
- Parallel Route Modal closed using router.push cannot open again until hard refresh HOT 3
- "res.status(200).json() is not a function" is this a bug? HOT 3
- next JS 13.0.6 HOT 2
- Component Reinitialization Issue with `loading.tsx` and `revalidateTag` HOT 2
- CSS module of client component is injected to DOM before global css in layout regardless of import order HOT 3
- Docs: Pages and layouts section not opening (again) HOT 2
- MSW Turbopack fails to import HOT 7
- ReferenceError: window is not defined... in client component? HOT 7
- Next 14 is not working properly in docker on windows with bun, but normal at local terminal HOT 7
- Sourcemaps are not loaded in index pages with a dynamic route level deeper than one
- SVG with icons not updating after change HOT 6
- [backport request] Backport fix of an already-fixed bug of next-swc HOT 7
- FileList is not defined on client component HOT 3
- Memory Leak with Image Optimization in Next.js Docker Environment
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from next.js.