diff --git a/services/web/app/src/Features/Helpers/UrlHelper.js b/services/web/app/src/Features/Helpers/UrlHelper.js index d75eaccfc6..38d825e1e0 100644 --- a/services/web/app/src/Features/Helpers/UrlHelper.js +++ b/services/web/app/src/Features/Helpers/UrlHelper.js @@ -1,6 +1,19 @@ const Settings = require('@overleaf/settings') const { URL } = require('url') +const PROTO = new URL(Settings.siteUrl).protocol + +function getCanonicalURL(req, url) { + const origin = `${PROTO}//${req.headers.host}` + url = new URL(url || req.originalUrl, origin) + if (url.pathname.endsWith('/')) { + url.pathname = url.pathname.replace(/\/+$/, '') + } + url.search = '' + url.hash = '' + return url.href +} + function getSafeRedirectPath(value) { const baseURL = Settings.siteUrl // base URL is required to construct URL from path const url = new URL(value, baseURL) @@ -12,6 +25,7 @@ function getSafeRedirectPath(value) { } const UrlHelper = { + getCanonicalURL, getSafeRedirectPath, wrapUrlWithProxy(url) { // TODO: Consider what to do for Community and Enterprise edition? diff --git a/services/web/app/views/_metadata.pug b/services/web/app/views/_metadata.pug index 14f84c85d1..c203c84e4f 100644 --- a/services/web/app/views/_metadata.pug +++ b/services/web/app/views/_metadata.pug @@ -113,3 +113,7 @@ link(rel="icon", href="/favicon.ico") link(rel="icon", sizes="192x192", href="/touch-icon-192x192.png") link(rel="apple-touch-icon-precomposed", href="/apple-touch-icon-precomposed.png") link(rel="mask-icon", href="/mask-favicon.svg", color="#138A07") + +//- Canonical Tag for SEO +if (metadata && metadata.canonicalURL) + link(rel="canonical" href=metadata.canonicalURL)