Headers are removed from middleware in production Next js - next.js

I have such a strange thing. Project on next js 12.3. I use middleware to add canonical link to headers. Everything works locally, and the required headers are added, but when deployed in production, the header I need is not added.
if the subdomain is not www, the canonical must be a link to the www version
import { NextRequest, NextResponse } from 'next/server';
export function middleware(req: NextRequest, res: NextResponse) {
const { protocol, href } = req.nextUrl;
const subdomain = href.replace('https://','').split('.')[0];
if (subdomain !== 'www') {
const urlArr = href?.split('//');
const afterSubdomainUrl = urlArr?.slice(1, urlArr.length)?.join('//');
const response = NextResponse.next();
response.headers.append(
'Link',
`<${protocol}//www.${afterSubdomainUrl}>; rel="canonical"`,
);
return response;
}
}

The problem was at DockerFile - need to implement next code at
app/Dockerfile
COPY middleware.ts /app/middleware.ts
RUN true

Related

NextJS middleware does not seem to be triggered

I have the middleware.js file within /myproject/pages/middleware.js:
export function middleware(request) {
console.log(1);
return NextResponse.redirect(new URL('/', request.url));
}
// See "Matching Paths" below to learn more
export const config = {
matcher: ['/test'],
};
Now what I expect is when i go to the page /test then it should redirect me to /. However nothing happens and I see my standard 404 page.
Any ideas why?
NextJs version: 12.2.2
Latest versions of NextJS requires user to have a single middleware on the root folder.
Instead of {root}/pages/_middleware.js, try {root}/middleware.js
For next 13.0.2 / 13.0.1
if you are using appDir: true ( experimental )
if you want to hit middleware:
put middleware.ts in root project:
( as the same hierarchy as "app" folder, not inside app folder... )
make sure tsconfig has include: [..., "middleware.ts"]
make empty "pages" folder. ( based on issue )
will hit every request:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest ) {
console.log('lol!!!')
}
export const config = {
matcher: '/',
}
if your pages and middleware are not on the same level, then it won't work.
here is an example of middleware working with Cookies.
import { NextResponse } from "next/server";
export default function middleware(req){
let verify = req.cookies.get("loggedin");
let url = req.url
if(!verify && url.includes('/dashboard')){
return NextResponse.redirect("http://localhost:3000/");
}
if (verify && url === "http://localhost:3000/") {
return NextResponse.redirect("http://localhost:3000/dashboard");
}
}

NextJS: Run middleware for /pages only

With the recent change regarding middleware https://nextjs.org/docs/messages/middleware-upgrade-guide.
Now the middleware also gets triggered by requests for /_next and /favicon.ico.
What is the best way to only trigger it for requests for /pages folder. There is the matcher but it would be cumbersome to type all of the pages in it.
I'm currently using middleware to check if cookies exist and redirect the user if the user accesses a page. E.g Redirect the user to /login if the user accesses / and the user is not logged in yet.
To avoid the middleware running when "/_next" or "/favicon.ico" routes are requested simply start your middleware function with:
const { pathname } = req.nextUrl
// Allow the requests if the following is true...
// 1) if the request is a system request /_next
// 2) if the request is to get the favicon.ico
if (pathname.startsWith('/_next') || pathname.startsWith('/favicon.ico')) {
return NextResponse.next()
}
Not sure it's the cleanest way but it works fine for me. Don't hesitate to tell me if I can make this better.
Now that middleware is stable (>=12.2), you can use a regex on the matcher to exclude some paths.
export const config = {
// matcher solution for public, api, assets and _next exclusion
matcher: "/((?!api|static|.*\\..*|_next).*)",
};
Ref: https://github.com/vercel/next.js/discussions/36308#discussioncomment-3758041
Here is the snippet of middleware:
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/about-2', request.url))
}
// See "Matching Paths" below to learn more
export const config = {
matcher: '/about/:path*',
}
If you want middleware to run on all paths:
export const config = {
matcher: '/:path*',
}
Have you tried this.

How to set api routes if you want your NEXTJS app to follow a subdomain?

I have set up a NEXTJS app that is under a subdomain and basically the structure is the following:
/Pages->
--/Sites
--/api
--/home
--/subdomain_logic
--_app.tsx
...config files...
As of this moment, if you go to domain.com you will be landing into another app that I developed so there is nothing configured outside of subdomain logic. If you go to subdomain.domain.com then you get all the logic ocurring into subdomain_logic. I want to set api routes but nextjs doesn't allow to set them outside of your api folder and if I leave them there those routes actually belong the domain.com app that I have in isolation. How would you create api routes on my situation?
Here is my middleware.ts file:
import { NextRequest, NextResponse } from "next/server";
export const config = {
matcher: [
"/",
"/([^/.]*)", // exclude `/public` files by matching all paths except for paths containing `.` (e.g. /logo.png)
"/site/:path*",
"/post/:path*",
"/_sites/:path*"
]
};
export default function middleware(req: NextRequest) {
const url = req.nextUrl;
const pathname = req.nextUrl.pathname.toString();
const hostname = req.headers.get("host");
if (!hostname)
return new Response(null, {
status: 400,
statusText: "No hostname found in request headers"
});
const currentHost =
process.env.VERCEL_ENV === `production` ||
process.env.VERCEL_ENV === `preview`
?
hostname
.replace(`.domain.com`, "")
.replace(`${process.env.VERCEL_URL}`, "")
.replace(`${process.env.NEXT_PUBLIC_VERCEL_URL}`, "")
: hostname.replace(`.localhost:3000`, "");
if (pathname.startsWith(`/_sites`))
return new Response(null, {
status: 404
});
if (
!pathname.includes(".")
) {
if (currentHost === "subdomain") {
if (
pathname === "/login" &&
(req.cookies["next-auth.session-token"] ||
req.cookies["__Secure-next-auth.session-token"])
) {
url.pathname = "/";
return NextResponse.redirect(url);
}
url.pathname = `/subdomain${url.pathname}`;
console.log(url);
return NextResponse.rewrite(url);
}
url.pathname = `${pathname}`;
return NextResponse.rewrite(url);
}
}
I would like to set up properly NextAuth if that give more clues into what could be the solution for my problem. Thanks for the help!
So where you are doing your redirect you basically want to do a check if its the api directory and not run those rewrites. Not sure what some of your code is doing but here is something that i am doing in a project with a similar setup.
If you reference your api endpoints via either the main or the subdomain it should still route correctly.
I am a bit confused by your question and how you are looking to integrate next auth, feel free to drop a comment if i've misunderstood anything.
import { NextRequest, NextResponse } from "next/server";
export default function middleware(req: NextRequest) {
const { pathname } = req.nextUrl;
const url = req.nextUrl.clone();
let hostname = req.headers.get("host");
hostname = hostname?.split(":")[0] ?? null;
const rootUrls = `${process.env.ROOT_URLS}`.split(",");
// only continue if its not a custom domain happening.
if (!rootUrls?.includes(hostname!)) {
// get the current subdomain or custom domain
const currentHost = hostname!.replace(`.${process.env.ROOT_URL}`, "");
if (pathname.endsWith("sitemap.xml")) {
url.pathname = `/_sites/${currentHost}/sitemap`;
return NextResponse.rewrite(url);
}
if (pathname.endsWith("robots.txt")) {
url.pathname = `/_sites/${currentHost}/robots`;
return NextResponse.rewrite(url);
}
// fail if trying to access the site directory
if (pathname.startsWith(`/_sites`)) {
return new Response(null, { status: 404 });
}
if (pathname.startsWith(`/_sites_previews`)) {
return new Response(null, { status: 404 });
}
if (
!pathname.includes(".") && // exclude all files in the public folder
!pathname.startsWith("/api") // exclude all API routes
) {
if (req.nextUrl.searchParams.get("preview")) {
url.pathname = `/_sites_previews/${currentHost}${pathname}`;
} else {
url.pathname = `/_sites/${currentHost}${pathname}`;
}
// rewrite to the current hostname under the pages/sites folder
// the main logic component will happen in pages/sites/[site]/index.tsx
return NextResponse.rewrite(url);
}
}
}

NextJs - Apollo - First render is not SSR

I am using NextJs(9.3.0) for SSR and graphQL (Apollo).
My app is working well, but when I check what google is seeing, the data from Apollo are not available
If I am doing a curl https://myWebsite.com randomly, I have sometime the content (like google) without the data from Apollo, and sometimes with the data from Apollo.
For SEO purpose, I need to always have the first render (after a refresh) with the data given buy my Backend (Apollo)
Here is my file: apolloClient.tsx
import { ApolloClient } from "apollo-client";
import { AUTH_TOKEN } from "./config";
import { InMemoryCache } from "apollo-cache-inmemory";
import { HttpLink } from "apollo-link-http";
import Cookies from "js-cookie";
import fetch from "isomorphic-unfetch";
import nextCookies from "next-cookies";
import { uriBackend } from "./config";
let token = null;
export default function createApolloClient(initialState, ctx) {
// The `ctx` (NextPageContext) will only be present on the server.
// use it to extract auth headers (ctx.req) or similar.
// on server
if (ctx && ctx.req && ctx.req.headers["cookie"]) {
token = nextCookies(ctx)[AUTH_TOKEN];
// on client
} else {
// console.log("with data get client cookie");
token = Cookies.get(AUTH_TOKEN);
}
const headers = token ? { Authorization: `Bearer ${token}` } : {};
return new ApolloClient({
ssrMode: Boolean(ctx),
link: new HttpLink({
uri: uriBackend, // Server URL (must be absolute)
fetch,
headers
}),
cache: new InMemoryCache().restore(initialState)
});
}
Looks to me like your SSR doesn't wait for the data fetching.
One solution for you could be, if your data changes rarely, to staticelly generate you pages with data:
https://nextjs.org/docs/basic-features/pages#scenario-1-your-page-content-depends-on-external-data
If you use SSR, make sure you use an async getServerSideProps that awaits your data requests:
https://nextjs.org/docs/basic-features/pages#server-side-rendering

Next.js returns 500: internal server error in Production

Created a next.js full stack application. After production build when I run next start it returns 500 : internal server. I'm using environment varibles for hitting api.
env.development file
BASE_URL=http://localhost:3000
It was working fine in development
service.ts
import axios from 'axios';
const axiosDefaultConfig = {
baseURL: process.env.BASE_URL, // is this line reason for error?
headers: {
'Access-Control-Allow-Origin': '*'
}
};
const axio = axios.create(axiosDefaultConfig);
export class Steam {
static getGames = async () => {
return await axio.get('/api/getAppList');
};
}
Do you have a next.config.js file?
To add runtime configuration to your app open next.config.js and add the publicRuntimeConfig and serverRuntimeConfig configs:
module.exports = {
serverRuntimeConfig: {
// Will only be available on the server side
mySecret: 'secret',
secondSecret: process.env.SECOND_SECRET, // Pass through env variables
},
publicRuntimeConfig: {
// Will be available on both server and client
staticFolder: '/static',
},
}
To get access to the runtime configs in your app use next/config, like so:
import getConfig from 'next/config'
// Only holds serverRuntimeConfig and publicRuntimeConfig
const { serverRuntimeConfig, publicRuntimeConfig } = getConfig()
// Will only be available on the server-side
console.log(serverRuntimeConfig.mySecret)
// Will be available on both server-side and client-side
console.log(publicRuntimeConfig.staticFolder)
function MyImage() {
return (
<div>
<img src={`${publicRuntimeConfig.staticFolder}/logo.png`} alt="logo" />
</div>
)
}
export default MyImage
I hope this helps.
I dont think you have setup env.
You need to configure it for it to work. Try it without it and it should work fine!

Resources