fix(seo): accept official blog canonicals under a custom SITE_URL (#933)

The blog is BentoPDF's own content, so generate-blog.mjs and the
hand-written blog pages keep their canonical on www.bentopdf.com in
self-hosted builds too. seo-audit.mjs now expects that official canonical
(and host) for blog/ pages instead of failing every custom-SITE_URL build.
This commit is contained in:
David Cumps
2026-10-23 13:58:14 +00:00
parent af92a591b9
commit 4d3468c06f
+17 -6
View File
@@ -7,12 +7,17 @@ const __dirname = path.dirname(__filename);
const DIST_DIR = path.resolve(__dirname, '../dist');
const LOCALES_DIR = path.resolve(__dirname, '../public/locales');
const SITE_URL = (process.env.SITE_URL || 'https://www.bentopdf.com').replace(
// The blog is BentoPDF's own content, so generate-blog.mjs and the hand-written
// blog pages keep their canonicals on the official site, also in self-hosted
// builds with a custom SITE_URL or BASE_URL.
const OFFICIAL_SITE_URL = 'https://www.bentopdf.com';
const SITE_URL = (process.env.SITE_URL || OFFICIAL_SITE_URL).replace(
/\/+$/,
''
);
const BASE_PATH = (process.env.BASE_URL || '/').replace(/\/$/, '');
const HOST = new URL(SITE_URL).hostname;
const OFFICIAL_HOST = new URL(OFFICIAL_SITE_URL).hostname;
const NOINDEX_ALLOWLIST = new Set(['404.html', 'wasm-settings.html']);
const SKIP_DIRS = new Set([
@@ -90,18 +95,23 @@ function hasTrailingSlash(href) {
return pathname !== '/' && pathname.endsWith('/');
}
function isBlogPage(rel) {
return rel.startsWith('blog/');
}
function expectedCanonicalForFile(rel) {
const parts = rel.split('/');
const fileName = parts.pop();
const baseName = fileName.replace(/\.html$/, '');
const slug = baseName === 'index' ? '' : baseName;
if (isBlogPage(rel)) {
return `${OFFICIAL_SITE_URL}/blog${slug ? `/${slug}` : ''}`;
}
const translationKey = KEY_MAPPING[baseName] || toCamelCase(baseName);
const segments = [SITE_URL];
if (BASE_PATH) segments.push(BASE_PATH.replace(/^\//, ''));
for (const dir of parts) {
if (dir === 'blog') {
segments.push(dir);
} else if (LOCALE_DIRS.has(dir)) {
if (LOCALE_DIRS.has(dir)) {
const translated =
baseName === 'index' ||
Boolean(toolsByLang[dir] && toolsByLang[dir][translationKey]);
@@ -177,10 +187,11 @@ function auditHtml(file, knownSlugs) {
`${file.rel}: canonical "${actual}" ends with "/" (the server redirects it to the slashless form)`
);
}
if (url.hostname !== HOST) {
const expectedHost = isBlogPage(file.rel) ? OFFICIAL_HOST : HOST;
if (url.hostname !== expectedHost) {
warn(
'canonical',
`${file.rel}: canonical host "${url.hostname}" != expected "${HOST}"`
`${file.rel}: canonical host "${url.hostname}" != expected "${expectedHost}"`
);
}
} catch {