Fix metadata base URL generation

This commit is contained in:
cryst
2026-03-09 19:51:20 +00:00
parent 0a838649b7
commit 1c0ad6e42b
3 changed files with 101 additions and 6 deletions
+13 -6
View File
@@ -1,4 +1,5 @@
import type { Metadata } from 'next';
import { headers } from 'next/headers';
import { Suspense } from 'react';
import { Providers } from './Providers';
import '@fontsource/inter/300.css';
@@ -6,6 +7,7 @@ import '@fontsource/inter/400.css';
import '@fontsource/inter/500.css';
import '@fontsource/inter/700.css';
import '@umami/react-zen/styles.css';
import { getBaseUrl } from '@/lib/get-base-url';
import '@/styles/global.css';
import '@/styles/variables.css';
@@ -41,9 +43,14 @@ export default function ({ children }) {
);
}
export const metadata: Metadata = {
title: {
template: '%s | Umami',
default: 'Umami',
},
};
export async function generateMetadata(): Promise<Metadata> {
const headerStore = await headers();
return {
metadataBase: getBaseUrl(headerStore),
title: {
template: '%s | Umami',
default: 'Umami',
},
};
}
+48
View File
@@ -0,0 +1,48 @@
import { HOMEPAGE_URL } from '../constants';
import { getBaseUrl } from '../get-base-url';
function createHeaders(entries: Record<string, string>) {
return {
get(name: string) {
return entries[name.toLowerCase()] ?? null;
},
};
}
test('prefers forwarded host and protocol', () => {
const url = getBaseUrl(
createHeaders({
'x-forwarded-host': 'umami.is',
'x-forwarded-proto': 'https',
host: 'localhost:3000',
}),
);
expect(url.toString()).toBe('https://umami.is/');
});
test('falls back to host header', () => {
const url = getBaseUrl(
createHeaders({
host: 'analytics.example.com',
}),
);
expect(url.toString()).toBe('https://analytics.example.com/');
});
test('uses http for localhost hosts', () => {
const url = getBaseUrl(
createHeaders({
host: 'localhost:3000',
}),
);
expect(url.toString()).toBe('http://localhost:3000/');
});
test('falls back to homepage when host is missing', () => {
const url = getBaseUrl(createHeaders({}));
expect(url.toString()).toBe(`${HOMEPAGE_URL}/`);
});
+40
View File
@@ -0,0 +1,40 @@
import { HOMEPAGE_URL } from './constants';
type HeaderStore = Pick<Headers, 'get'>;
function getFirstHeaderValue(value?: string | null) {
return value?.split(',')[0]?.trim();
}
function getDefaultProtocol(host?: string) {
if (!host) {
return 'https';
}
if (host.startsWith('localhost') || host.startsWith('127.0.0.1') || host.startsWith('[::1]')) {
return 'http';
}
return 'https';
}
export function getBaseUrl(headers?: HeaderStore) {
const host =
getFirstHeaderValue(headers?.get('x-forwarded-host')) ||
getFirstHeaderValue(headers?.get('host'));
if (!host) {
return new URL(HOMEPAGE_URL);
}
const protocol =
getFirstHeaderValue(headers?.get('x-forwarded-proto')) ||
getFirstHeaderValue(headers?.get('x-forwarded-protocol')) ||
getDefaultProtocol(host);
try {
return new URL(`${protocol}://${host}`);
} catch {
return new URL(HOMEPAGE_URL);
}
}