diff --git a/scripts/seo-audit.mjs b/scripts/seo-audit.mjs index 68ab7212..ebcab8fa 100644 --- a/scripts/seo-audit.mjs +++ b/scripts/seo-audit.mjs @@ -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 {