sow*_*dri 8 nginx next.js nginx-reverse-proxy
语境:
我在 Nginx 后面部署了 NextJS。这个想法是使用 NextJS 创建多个托管在不同域中的网站。每个域在 Nginx 中都有一个条目,它将指向pages/cafes/[cafeId]
NextJS 中的特定路径。所有网站都只有一个 NextJs 部署,每个域都将使用static
nginx 中的代理进行路由。
配置文件
server {
listen 80;
server_name www.cafe-one.local;
location = / {
proxy_pass http://localhost:3000/cafes/cafe_id_1;
...
}
location / {
proxy_pass http://localhost:3000/;
...
}
}
server {
listen 80;
server_name www.cafe-two.local;
location = / {
proxy_pass http://localhost:3000/cafes/cafe_id_2;
...
}
location / {
proxy_pass http://localhost:3000/;
...
}
}
Run Code Online (Sandbox Code Playgroud)
页面/[cafeId]/index.js
export const getStaticPaths = async () => {
return {
paths: [], // no website rendered during build time, there are around 1000+ sites
fallback: true
};
};
export const getStaticProps = async context => {
const cafeId = context.params.cafeId;
const cafe = await ... // get data from server
return {
props: {
cafe
},
revalidate: 10 // revalidate every 10 seconds
};
};
export default function CafeWebsite(props) {
const router = useRouter();
// getStaticProps() is not finished
if (router.isFallback) {
return <div>Loading...</div>;
}
return <div>{props.cafe.name}</div>;
}
Run Code Online (Sandbox Code Playgroud)
问题:
当我访问时www.cafe-one.local
,我进入了加载屏幕,但是 NextJS 抛出了一个关于The provided as value (/) is incompatible with the href value (/cafes/[cafeId])
. 这是可以理解的,因为当前的 URL 不是 NextJS 所期望的。
题:
如何解决这个问题,使得 NextJS 可以在 Nginx 反向代理之前使用?
任何帮助表示赞赏。
提前致谢。
2021 年 10 月编辑:Next.js 12 现在支持中间件,无需代理服务器即可实现动态子域名。看官方例子。
原始答案:感谢 Ale 提供的replaceState 想法,这就是我们现在处理它的方式:
/original.host.com/
路径中的第一项:server {
listen 8080;
# redirect HTTP to HTTPS
if ($http_x_forwarded_proto = "http") {
return 301 https://$host$request_uri;
}
# (not needed if you set assetPrefix in next.config.js to https://myapp.com)
location /_next {
proxy_pass https://myapp.com;
}
# a separate line for root is needed to bypass nginx messing up the request uri
location = / {
proxy_pass https://myapp.com/$host;
}
# this is the primary proxy
location / {
proxy_pass https://myapp.com/$host$request_uri;
}
}
Run Code Online (Sandbox Code Playgroud)
_app.tsx
,我们注册了一个在 Next.js 完成更改路线后运行的效果(这不会在第一次渲染时触发)。server {
listen 8080;
# redirect HTTP to HTTPS
if ($http_x_forwarded_proto = "http") {
return 301 https://$host$request_uri;
}
# (not needed if you set assetPrefix in next.config.js to https://myapp.com)
location /_next {
proxy_pass https://myapp.com;
}
# a separate line for root is needed to bypass nginx messing up the request uri
location = / {
proxy_pass https://myapp.com/$host;
}
# this is the primary proxy
location / {
proxy_pass https://myapp.com/$host$request_uri;
}
}
Run Code Online (Sandbox Code Playgroud)
所有特定于域的页面都位于pages/[domain]/
,例如pages/[domain]/mypage.tsx
。
最后,我们将原始主机名添加到每个 前面href
,例如
<Link href="/original.host.com/mypage">...</Link>
或者Router.push('/original.host.com/mypage')
。没必要再用了as
。
Next.js 现在将导航到https://original.host.com/original.host.com/mypage
瞬间,然后https://original.host.com/mypage
在转换完成后将其替换为。
为了使 SSR 适用于每个特定于域的页面,我们在每个页面中添加getStaticPaths
/ getStaticProps
,以便 Next.js 知道为每个域生成页面的单独版本(否则router.query.domain
在 SSR 中将为空,我们会收到错误路径不匹配)。请注意,这不会对性能产生负面影响,因为页面将在第一次请求后被缓存。
useEffect(() => {
const handleRouteChange = (url: string) => {
const paths = url.split('/')
if (paths[1] === location.host) {
// remove /original.host.com/ from the path
// note that passing history.state as the first argument makes back/forward buttons work correctly
history.replaceState(history.state, '', `/${paths.slice(2).join('/')}`)
}
}
router.events.on('routeChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)
}
}, [])
Run Code Online (Sandbox Code Playgroud)
passHref
而是将假 URL 设置为href
,取消 中的默认操作onClickCapture
,并触发中的路由器onClick
(您可以将其提取到可重用组件中):// In pages/[domain]/mypage.tsx
export default function MyPage(params: MyPageProps) {
const router = useRouter()
// SSR provides domain as a param, client takes it from router.query
const domain = params.domain || router.query.domain
// ... rest of your page
}
export const getStaticPaths: GetStaticPaths = async () => ({
fallback: 'blocking',
paths: [],
})
export const getStaticProps: GetStaticProps<SpaceEditPageProps> = async ({
params,
}) => {
return {
props: {
domain: params?.domain as string,
},
}
}
Run Code Online (Sandbox Code Playgroud)
我一直在处理同样的问题,但是为了将不同的子域映射到 NextJS 应用程序中的动态路由。
我无法找到该The provided as value (/) is incompatible with the href value
错误的正确解决方案,但我找到了一个有点古怪的解决方法。
首先,您必须将请求从 重定向my-domain.com
到my-domain.com/path-to-dynamic-route
。然后,您必须将所有请求反向代理my-domain.com/path-to-dynamic-route
到 NextJS 应用程序中的同一动态路由,例如localhost:3000/path-to-dynamic-route
.
return 301
您可以使用和的组合从 NGINX 手动执行此操作proxy_pass
,也可以通过在proxy_pass
指令中传递带有尾部斜杠的动态路由来让 NextJS 自动执行此操作。
nginx.conf
server {
listen 80;
server_name www.cafe-one.local;
location = / {
# When a url to a route has a trailing slash, NextJS responds with a "308 Permanent redirect" to the path without the slash.
# In this case from /cafes/cafe_id_1/ to /cafes/cafe_id_1
proxy_pass http://localhost:3000/cafes/cafe_id_1/;
# If you also want to be able to pass parameters in the query string, you should use the variable $request_uri instead of "/"
# proxy_pass http://localhost:3000/cafes/cafe_id_1$request_uri;
...
}
location / {
# Any other request to www.cafe-one.local will keep the original route and query string
proxy_pass http://localhost:3000$request_uri;
...
}
}
Run Code Online (Sandbox Code Playgroud)
这应该可行,但现在地址栏中的 url 有问题。任何访问的用户www.cafe-one.local
都会被重定向到www.cafe-one.local/cafes/cafe_id_1
,这看起来不太好。
我发现解决此问题的唯一解决方法是使用 javascript 重写浏览历史记录来删除路径window.history.replaceState()
。
pages/[cafeId]/index.js
...
export default function CafeWebsite(props) {
if (typeof window !== "undefined") {
window.history.replaceState(null, "", "/")
}
...
Run Code Online (Sandbox Code Playgroud)
如果您不想删除所有域的路径,可以使用window.location.hostname
检查当前 url。
...
export default function CafeWebsite(props) {
if (typeof window !== "undefined") {
const hostname = window.location.hostname
const regex = /^(www\.my-domain\.|my-domain\.)/
if (!regex.test(hostname)) {
window.history.replaceState(null, "", "/")
}
}
...
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3280 次 |
最近记录: |