diff --git a/src/app/[locale]/(home)/(activities)/comments/page.tsx b/src/app/[locale]/(home)/(activities)/comments/page.tsx index 0e6ef84f..74fa0709 100644 --- a/src/app/[locale]/(home)/(activities)/comments/page.tsx +++ b/src/app/[locale]/(home)/(activities)/comments/page.tsx @@ -1,11 +1,10 @@ -import { Metadata } from "next" - import { HomeFeed } from "~/components/home/HomeFeed" import { APP_NAME } from "~/lib/env" +import { withHrefLang } from "~/lib/with-hreflang" -export const metadata: Metadata = { +export const generateMetadata = withHrefLang(async () => ({ title: `Comments Activities - ${APP_NAME}`, -} +})) export default async function FollowingActivities() { return diff --git a/src/app/[locale]/(home)/(activities)/following/page.tsx b/src/app/[locale]/(home)/(activities)/following/page.tsx index fd2f70cd..8b20413e 100644 --- a/src/app/[locale]/(home)/(activities)/following/page.tsx +++ b/src/app/[locale]/(home)/(activities)/following/page.tsx @@ -1,11 +1,10 @@ -import { Metadata } from "next" - import { HomeFeed } from "~/components/home/HomeFeed" import { APP_NAME } from "~/lib/env" +import { withHrefLang } from "~/lib/with-hreflang" -export const metadata: Metadata = { +export const generateMetadata = withHrefLang(async () => ({ title: `Following Activities - ${APP_NAME}`, -} +})) export default async function FollowingActivities() { return diff --git a/src/app/[locale]/(home)/(activities)/hottest/page.tsx b/src/app/[locale]/(home)/(activities)/hottest/page.tsx index 5de6c79e..6b483c81 100644 --- a/src/app/[locale]/(home)/(activities)/hottest/page.tsx +++ b/src/app/[locale]/(home)/(activities)/hottest/page.tsx @@ -1,4 +1,3 @@ -import { Metadata } from "next" import { getLocale } from "next-intl/server" import { dehydrate, Hydrate } from "@tanstack/react-query" @@ -7,11 +6,12 @@ import { HomeFeed } from "~/components/home/HomeFeed" import { APP_NAME } from "~/lib/env" import getQueryClient from "~/lib/query-client" import { Language } from "~/lib/types" +import { withHrefLang } from "~/lib/with-hreflang" import { prefetchGetFeed } from "~/queries/home.server" -export const metadata: Metadata = { +export const generateMetadata = withHrefLang(async () => ({ title: `Hottest Activities - ${APP_NAME}`, -} +})) export default async function HottestActivities() { const queryClient = getQueryClient() diff --git a/src/app/[locale]/(home)/(activities)/latest/page.tsx b/src/app/[locale]/(home)/(activities)/latest/page.tsx index d925a1eb..2520de4d 100644 --- a/src/app/[locale]/(home)/(activities)/latest/page.tsx +++ b/src/app/[locale]/(home)/(activities)/latest/page.tsx @@ -1,4 +1,3 @@ -import { Metadata } from "next" import { getLocale } from "next-intl/server" import { dehydrate, Hydrate } from "@tanstack/react-query" @@ -7,11 +6,12 @@ import { HomeFeed } from "~/components/home/HomeFeed" import { APP_NAME } from "~/lib/env" import getQueryClient from "~/lib/query-client" import { Language } from "~/lib/types" +import { withHrefLang } from "~/lib/with-hreflang" import { prefetchGetFeed } from "~/queries/home.server" -export const metadata: Metadata = { +export const generateMetadata = withHrefLang(async () => ({ title: `Latest Activities - ${APP_NAME}`, -} +})) export default async function LatestActivities() { const queryClient = getQueryClient() diff --git a/src/app/[locale]/(home)/(activities)/layout.tsx b/src/app/[locale]/(home)/(activities)/layout.tsx index 06837fa0..9f102e4f 100644 --- a/src/app/[locale]/(home)/(activities)/layout.tsx +++ b/src/app/[locale]/(home)/(activities)/layout.tsx @@ -1,12 +1,11 @@ -import { Metadata } from "next" - import { HomeActivitiesTabs } from "~/components/home/HomeActivitiesTabs" import { HomeSidebar } from "~/components/home/HomeSidebar" import { APP_NAME } from "~/lib/env" +import { withHrefLang } from "~/lib/with-hreflang" -export const metadata: Metadata = { +export const generateMetadata = withHrefLang(async () => ({ title: `Activities - ${APP_NAME}`, -} +})) export default async function ActivitiesLayout({ children, diff --git a/src/app/[locale]/(home)/(activities)/page.tsx b/src/app/[locale]/(home)/(activities)/page.tsx index 911969f3..44394036 100644 --- a/src/app/[locale]/(home)/(activities)/page.tsx +++ b/src/app/[locale]/(home)/(activities)/page.tsx @@ -1,4 +1,3 @@ -import { Metadata } from "next" import { getLocale } from "next-intl/server" import { dehydrate, Hydrate } from "@tanstack/react-query" @@ -8,11 +7,12 @@ import ShortPreviewList from "~/components/site/ShortPreviewList" import { APP_NAME, APP_SLOGAN } from "~/lib/env" import getQueryClient from "~/lib/query-client" import { Language } from "~/lib/types" +import { withHrefLang } from "~/lib/with-hreflang" import { getPreviewShort, prefetchGetFeed } from "~/queries/home.server" -export const metadata: Metadata = { +export const generateMetadata = withHrefLang(async () => ({ title: `${APP_NAME} - ${APP_SLOGAN}`, -} +})) export default async function HomeActivities() { const queryClient = getQueryClient() diff --git a/src/app/[locale]/(home)/(activities)/search/page.tsx b/src/app/[locale]/(home)/(activities)/search/page.tsx index cc50219d..1834e4ce 100644 --- a/src/app/[locale]/(home)/(activities)/search/page.tsx +++ b/src/app/[locale]/(home)/(activities)/search/page.tsx @@ -1,4 +1,3 @@ -import { Metadata } from "next" import { getLocale } from "next-intl/server" import { dehydrate, Hydrate } from "@tanstack/react-query" @@ -8,19 +7,12 @@ import { HomeFeed } from "~/components/home/HomeFeed" import { APP_NAME } from "~/lib/env" import getQueryClient from "~/lib/query-client" import { Language } from "~/lib/types" +import { withHrefLang } from "~/lib/with-hreflang" import { prefetchGetFeed } from "~/queries/home.server" -export function generateMetadata({ - searchParams, -}: { - searchParams: { - [key: string]: string | string[] | undefined - } -}): Metadata { - return { - title: `Search: ${searchParams.q} - ${APP_NAME}`, - } -} +export const generateMetadata = withHrefLang(async ({ searchParams }) => ({ + title: `Search: ${searchParams?.q} - ${APP_NAME}`, +})) export default async function Search({ searchParams, diff --git a/src/app/[locale]/(home)/(activities)/shorts/page.tsx b/src/app/[locale]/(home)/(activities)/shorts/page.tsx index 651402f9..20acec20 100644 --- a/src/app/[locale]/(home)/(activities)/shorts/page.tsx +++ b/src/app/[locale]/(home)/(activities)/shorts/page.tsx @@ -1,4 +1,3 @@ -import { Metadata } from "next" import { getLocale } from "next-intl/server" import { dehydrate, Hydrate } from "@tanstack/react-query" @@ -7,11 +6,12 @@ import { HomeFeed } from "~/components/home/HomeFeed" import { APP_NAME } from "~/lib/env" import getQueryClient from "~/lib/query-client" import { Language } from "~/lib/types" +import { withHrefLang } from "~/lib/with-hreflang" import { prefetchGetFeed } from "~/queries/home.server" -export const metadata: Metadata = { +export const generateMetadata = withHrefLang(async () => ({ title: `Shorts - ${APP_NAME}`, -} +})) export default async function LatestActivities() { const queryClient = getQueryClient() diff --git a/src/app/[locale]/(home)/(activities)/tag/[tag]/page.tsx b/src/app/[locale]/(home)/(activities)/tag/[tag]/page.tsx index 498d1ef0..1e068693 100644 --- a/src/app/[locale]/(home)/(activities)/tag/[tag]/page.tsx +++ b/src/app/[locale]/(home)/(activities)/tag/[tag]/page.tsx @@ -1,4 +1,3 @@ -import { Metadata } from "next" import { getLocale } from "next-intl/server" import { dehydrate, Hydrate } from "@tanstack/react-query" @@ -8,19 +7,16 @@ import ParticipateButton from "~/components/home/ParticipateButton" import { APP_NAME } from "~/lib/env" import getQueryClient from "~/lib/query-client" import { Language } from "~/lib/types" +import { withHrefLang } from "~/lib/with-hreflang" import { prefetchGetFeed } from "~/queries/home.server" -export function generateMetadata({ - params, -}: { +export const generateMetadata = withHrefLang<{ params: { tag: string } -}): Metadata { - return { - title: `Tag: ${params.tag} - ${APP_NAME}`, - } -} +}>(async ({ params }) => ({ + title: `Tag: ${params.tag} - ${APP_NAME}`, +})) export default async function Tag({ params, diff --git a/src/app/[locale]/(home)/(activities)/topic/[topic]/page.tsx b/src/app/[locale]/(home)/(activities)/topic/[topic]/page.tsx index 0e91ad57..bdb11a9e 100644 --- a/src/app/[locale]/(home)/(activities)/topic/[topic]/page.tsx +++ b/src/app/[locale]/(home)/(activities)/topic/[topic]/page.tsx @@ -1,4 +1,3 @@ -import { Metadata } from "next" import { getLocale, getTranslations } from "next-intl/server" import { dehydrate, Hydrate } from "@tanstack/react-query" @@ -8,22 +7,21 @@ import ParticipateButton from "~/components/home/ParticipateButton" import { APP_NAME } from "~/lib/env" import getQueryClient from "~/lib/query-client" import { Language } from "~/lib/types" +import { withHrefLang } from "~/lib/with-hreflang" import { prefetchGetFeed } from "~/queries/home.server" import topics from "../../../../../../../data/topics.json" -export function generateMetadata({ - params, -}: { +export const generateMetadata = withHrefLang<{ params: { topic: string } -}): Metadata { +}>(async ({ params }) => { params.topic = decodeURIComponent(params.topic) return { title: `Topic: ${params.topic} - ${APP_NAME}`, } -} +}) export default async function Topic({ params, diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx index c77fa1a8..26e7cb76 100644 --- a/src/app/[locale]/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -1,4 +1,3 @@ -import { Metadata } from "next" import { NextIntlClientProvider } from "next-intl" import { getMessages, unstable_setRequestLocale } from "next-intl/server" import { headers } from "next/headers" @@ -29,7 +28,9 @@ import "remark-github-alerts/styles/github-colors-dark-class.css" import "remark-github-alerts/styles/github-base.css" import "~/css/main.css" -export const metadata: Metadata = { +import { withHrefLang } from "~/lib/with-hreflang" + +export const generateMetadata = withHrefLang(async () => ({ title: `${APP_NAME} - ${APP_SLOGAN}`, description: APP_DESCRIPTION, applicationName: APP_NAME, @@ -94,7 +95,7 @@ export const metadata: Metadata = { site: "@_xLog", creator: "@_xLog", }, -} +})) export default async function RootLayout({ children, diff --git a/src/app/[locale]/site/[site]/[slug]/page.tsx b/src/app/[locale]/site/[site]/[slug]/page.tsx index 43470ea4..74fe1909 100644 --- a/src/app/[locale]/site/[site]/[slug]/page.tsx +++ b/src/app/[locale]/site/[site]/[slug]/page.tsx @@ -1,4 +1,3 @@ -import { Metadata } from "next" import { getLocale, getTranslations } from "next-intl/server" import { notFound } from "next/navigation" import serialize from "serialize-javascript" @@ -18,17 +17,16 @@ import { isInRN } from "~/lib/is-in-rn" import { isOnlyContent } from "~/lib/is-only-content" import getQueryClient from "~/lib/query-client" import { Language } from "~/lib/types" +import { withHrefLang } from "~/lib/with-hreflang" import { fetchGetPage, getSummary } from "~/queries/page.server" import { fetchGetSite } from "~/queries/site.server" -export async function generateMetadata({ - params, -}: { +export const generateMetadata = withHrefLang<{ params: { site: string slug: string } -}): Promise { +}>(async ({ params }) => { const queryClient = getQueryClient() const locale = (await getLocale()) as Language const site = await fetchGetSite(params.site, queryClient) @@ -76,7 +74,7 @@ export async function generateMetadata({ creator: twitterCreator ? `@${twitterCreator}` : undefined, }, } -} +}) export default async function SitePagePage({ params, diff --git a/src/app/[locale]/site/[site]/archives/page.tsx b/src/app/[locale]/site/[site]/archives/page.tsx index d92f2906..766ccca8 100644 --- a/src/app/[locale]/site/[site]/archives/page.tsx +++ b/src/app/[locale]/site/[site]/archives/page.tsx @@ -1,20 +1,17 @@ -import { Metadata } from "next" - import { dehydrate, Hydrate } from "@tanstack/react-query" import { SiteArchives } from "~/components/site/SiteArchives" import getQueryClient from "~/lib/query-client" import { PageVisibilityEnum } from "~/lib/types" +import { withHrefLang } from "~/lib/with-hreflang" import { prefetchGetPagesBySite } from "~/queries/page.server" import { fetchGetSite } from "~/queries/site.server" -export async function generateMetadata({ - params, -}: { +export const generateMetadata = withHrefLang<{ params: { site: string } -}): Promise { +}>(async ({ params }) => { const queryClient = getQueryClient() const site = await fetchGetSite(params.site, queryClient) @@ -24,7 +21,7 @@ export async function generateMetadata({ return { title, } -} +}) export default async function SiteArchivesPage({ params, diff --git a/src/app/[locale]/site/[site]/layout.tsx b/src/app/[locale]/site/[site]/layout.tsx index 683533fd..ad34d933 100644 --- a/src/app/[locale]/site/[site]/layout.tsx +++ b/src/app/[locale]/site/[site]/layout.tsx @@ -1,4 +1,3 @@ -import { Metadata } from "next" import { headers } from "next/headers" import { notFound, redirect } from "next/navigation" @@ -18,6 +17,7 @@ import { isOnlyContent, searchParser } from "~/lib/is-only-content" import getQueryClient from "~/lib/query-client" import { ExpandedNote } from "~/lib/types" import { cn } from "~/lib/utils" +import { withHrefLang } from "~/lib/with-hreflang" import { fetchGetPage } from "~/queries/page.server" import { fetchGetSite, @@ -26,13 +26,11 @@ import { prefetchGetSiteToSubscriptions, } from "~/queries/site.server" -export async function generateMetadata({ - params, -}: { +export const generateMetadata = withHrefLang<{ params: { site: string } -}): Promise { +}>(async ({ params }) => { const queryClient = getQueryClient() const site = await fetchGetSite(params.site, queryClient) @@ -80,7 +78,7 @@ export async function generateMetadata({ creator: twitterCreator, }, } -} +}) export default async function SiteLayout({ children, diff --git a/src/app/[locale]/site/[site]/nft/page.tsx b/src/app/[locale]/site/[site]/nft/page.tsx index 81746adc..7eba65c9 100644 --- a/src/app/[locale]/site/[site]/nft/page.tsx +++ b/src/app/[locale]/site/[site]/nft/page.tsx @@ -1,18 +1,16 @@ -import { Metadata } from "next" import { getTranslations } from "next-intl/server" import { Image } from "~/components/ui/Image" import { UniLink } from "~/components/ui/UniLink" import getQueryClient from "~/lib/query-client" +import { withHrefLang } from "~/lib/with-hreflang" import { fetchGetSite, getNFTs } from "~/queries/site.server" -export async function generateMetadata({ - params, -}: { +export const generateMetadata = withHrefLang<{ params: { site: string } -}): Promise { +}>(async ({ params }) => { const queryClient = getQueryClient() const site = await fetchGetSite(params.site, queryClient) @@ -22,7 +20,7 @@ export async function generateMetadata({ return { title, } -} +}) export default async function SiteNFTPage({ params, diff --git a/src/app/[locale]/site/[site]/portfolios/page.tsx b/src/app/[locale]/site/[site]/portfolios/page.tsx index 5e900925..23c7e0a4 100644 --- a/src/app/[locale]/site/[site]/portfolios/page.tsx +++ b/src/app/[locale]/site/[site]/portfolios/page.tsx @@ -1,20 +1,17 @@ -import { Metadata } from "next" - import { dehydrate, Hydrate } from "@tanstack/react-query" import SiteHome from "~/components/site/SiteHome" import getQueryClient from "~/lib/query-client" import { PageVisibilityEnum } from "~/lib/types" +import { withHrefLang } from "~/lib/with-hreflang" import { prefetchGetPagesBySite } from "~/queries/page.server" import { fetchGetSite } from "~/queries/site.server" -export async function generateMetadata({ - params, -}: { +export const generateMetadata = withHrefLang<{ params: { site: string } -}): Promise { +}>(async ({ params }) => { const queryClient = getQueryClient() const site = await fetchGetSite(params.site, queryClient) @@ -24,7 +21,7 @@ export async function generateMetadata({ return { title, } -} +}) async function SitePortfoliosPage({ params, diff --git a/src/app/[locale]/site/[site]/search/page.tsx b/src/app/[locale]/site/[site]/search/page.tsx index 99269945..85133502 100644 --- a/src/app/[locale]/site/[site]/search/page.tsx +++ b/src/app/[locale]/site/[site]/search/page.tsx @@ -1,33 +1,29 @@ -import { Metadata } from "next" - import { SearchInput } from "~/components/common/SearchInput" import { SiteSearch } from "~/components/site/SiteSearch" import getQueryClient from "~/lib/query-client" +import { withHrefLang } from "~/lib/with-hreflang" import { fetchGetSite } from "~/queries/site.server" -export async function generateMetadata({ - params, - searchParams, -}: { +export const generateMetadata = withHrefLang<{ params: { site: string } searchParams: { [key: string]: string | string[] | undefined } -}): Promise { +}>(async ({ params, searchParams }) => { const queryClient = getQueryClient() - const site = await fetchGetSite(params.site, queryClient) + const site = await fetchGetSite(params?.site, queryClient) - const title = `Search: ${searchParams.q} - ${ + const title = `Search: ${searchParams?.q} - ${ site?.metadata?.content?.name || site?.handle }` return { title, } -} +}) export default async function SiteSearchPage() { return ( diff --git a/src/app/[locale]/site/[site]/shorts/page.tsx b/src/app/[locale]/site/[site]/shorts/page.tsx index 0fc0156f..6dbecdc6 100644 --- a/src/app/[locale]/site/[site]/shorts/page.tsx +++ b/src/app/[locale]/site/[site]/shorts/page.tsx @@ -1,20 +1,17 @@ -import { Metadata } from "next" - import { dehydrate, Hydrate } from "@tanstack/react-query" import SiteHome from "~/components/site/SiteHome" import getQueryClient from "~/lib/query-client" import { PageVisibilityEnum } from "~/lib/types" +import { withHrefLang } from "~/lib/with-hreflang" import { prefetchGetPagesBySite } from "~/queries/page.server" import { fetchGetSite } from "~/queries/site.server" -export async function generateMetadata({ - params, -}: { +export const generateMetadata = withHrefLang<{ params: { site: string } -}): Promise { +}>(async ({ params }) => { const queryClient = getQueryClient() const site = await fetchGetSite(params.site, queryClient) @@ -24,7 +21,7 @@ export async function generateMetadata({ return { title, } -} +}) async function SiteShortsPage({ params, diff --git a/src/app/[locale]/site/[site]/tag/[tag]/page.tsx b/src/app/[locale]/site/[site]/tag/[tag]/page.tsx index 9e4d4660..33ab60c0 100644 --- a/src/app/[locale]/site/[site]/tag/[tag]/page.tsx +++ b/src/app/[locale]/site/[site]/tag/[tag]/page.tsx @@ -1,21 +1,18 @@ -import { Metadata } from "next" - import { dehydrate, Hydrate } from "@tanstack/react-query" import { SiteArchives } from "~/components/site/SiteArchives" import getQueryClient from "~/lib/query-client" import { PageVisibilityEnum } from "~/lib/types" +import { withHrefLang } from "~/lib/with-hreflang" import { prefetchGetPagesBySite } from "~/queries/page.server" import { fetchGetSite } from "~/queries/site.server" -export async function generateMetadata({ - params, -}: { +export const generateMetadata = withHrefLang<{ params: { site: string tag: string } -}): Promise { +}>(async ({ params }) => { const queryClient = getQueryClient() const site = await fetchGetSite(params.site, queryClient) @@ -28,7 +25,7 @@ export async function generateMetadata({ return { title, } -} +}) export default async function SiteTagPage({ params, diff --git a/src/i18n.ts b/src/i18n.ts index 38a7984a..7266d7bf 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -1,6 +1,8 @@ import { getRequestConfig } from "next-intl/server" -export const locales = ["en", "ja", "zh", "zh-TW"] as const +import { Language } from "./lib/types" + +export const locales: Language[] = ["en", "ja", "zh", "zh-TW"] export const languageNames: Record<(typeof locales)[number], string> = { en: "English", ja: "Japanese", diff --git a/src/lib/types.ts b/src/lib/types.ts index a504c842..93bf479d 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,6 +1,6 @@ import type { CharacterEntity, NoteEntity } from "crossbell" -export type Language = "en" | "zh" | "zh-TW" | "ja" +export type Language = Readonly<"en" | "zh" | "zh-TW" | "ja"> export type Site = { id: string diff --git a/src/lib/with-hreflang.ts b/src/lib/with-hreflang.ts new file mode 100644 index 00000000..bc99ee8a --- /dev/null +++ b/src/lib/with-hreflang.ts @@ -0,0 +1,70 @@ +import { Metadata, ResolvingMetadata } from "next" +import { headers } from "next/headers" + +import { defaultLocale, locales } from "~/i18n" + +type BaseProps = { + params?: Record + searchParams?: Record +} + +type GenerateMetadata = ( + props: T, + parent: ResolvingMetadata, +) => Promise + +export function withHrefLang( + _generateMetadata: GenerateMetadata, +): GenerateMetadata { + return async function generateMetadata( + props: T, + parent: ResolvingMetadata, + ): Promise { + const metadata: Metadata = await _generateMetadata(props, parent) + + if (metadata.alternates) { + const { locale } = props?.params || {} + const protocol = headers().get("x-forwarded-proto") || "" + const host = headers().get("x-forwarded-host") || "" + const metadataBase = new URL(`${protocol}://${host}`) + const path = headers().get("x-xlog-pathname") || "" + + const redirectedPathName = (locale: string) => { + const search = headers().get("x-xlog-search") || "" + const searchParams = new URLSearchParams(search) + searchParams.set("locale", locale) + + return `${path}?${searchParams.toString()}` + } + + metadata.metadataBase = metadataBase + /** + * Generate hreflang. + * e.g. In the home page, the hreflang should be: + * + * ... + * + * + * ... + * + * + * */ + metadata.alternates["canonical"] = redirectedPathName( + locale ?? defaultLocale, + ) + metadata.alternates["languages"] = { + ...locales.reduce( + (acc, locale) => { + const hreflang: string = locale + acc[hreflang] = redirectedPathName(locale) + return acc + }, + {} as Record, + ), + "x-default": redirectedPathName("en"), + } + } + + return metadata + } +} diff --git a/src/middleware.ts b/src/middleware.ts index d5d5a3fa..3e2f2ad0 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -6,6 +6,7 @@ import { getClientIp } from "@supercharge/request-ip" import { IS_PROD } from "~/lib/constants" import { defaultLocale, locales } from "./i18n" +import { Language } from "./lib/types" // HTTPWhiteListPaths: White list of path for plain http request, no HTTPS redirect const HTTPWhitelistPaths = ["/api/healthcheck"] @@ -17,7 +18,7 @@ export const config = { } export async function middleware(req: NextRequest) { - const handleI18nRouting = createMiddleware({ + const handleI18nRouting = createMiddleware({ locales, defaultLocale, localePrefix: "never", @@ -25,6 +26,13 @@ export async function middleware(req: NextRequest) { const { pathname } = req.nextUrl + const searchParams = new URLSearchParams(req.nextUrl.search) + const specifiedLocale = searchParams.get("locale") as Language | undefined + + if (specifiedLocale && locales.includes(specifiedLocale)) { + req.cookies.set("NEXT_LOCALE", specifiedLocale) + } + if ( IS_PROD && req.headers.get("x-forwarded-proto") !== "https" &&