diff --git a/CHANGELOG.md b/CHANGELOG.md
index f421d20..b32c6e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
- Remove `libera.chat` as a default since their rooms are not accessible in the archive, https://github.com/matrix-org/matrix-public-archive/pull/263
- Add reason why the archive bot is joining the room, https://github.com/matrix-org/matrix-public-archive/pull/262
- Add `/faq` redirect, https://github.com/matrix-org/matrix-public-archive/pull/265
+- Use `rel=canonical` link to de-duplicate event permalinks, https://github.com/matrix-org/matrix-public-archive/pull/266
# 0.1.0 - 2023-05-11
diff --git a/server/hydrogen-render/render-hydrogen-to-string-unsafe.js b/server/hydrogen-render/render-hydrogen-to-string-unsafe.js
index 6930446..a34c384 100644
--- a/server/hydrogen-render/render-hydrogen-to-string-unsafe.js
+++ b/server/hydrogen-render/render-hydrogen-to-string-unsafe.js
@@ -69,14 +69,14 @@ async function _renderHydrogenToStringUnsafe(renderOptions) {
assert(renderOptions.vmRenderScriptFilePath);
assert(renderOptions.vmRenderContext);
assert(renderOptions.pageOptions);
- assert(renderOptions.pageOptions.locationHref);
+ assert(renderOptions.pageOptions.locationUrl);
assert(renderOptions.pageOptions.cspNonce);
const { dom, vmContext } = createDomAndSetupVmContext();
// A small `window.location` stub
if (!dom.window.location) {
- const locationUrl = new URL(renderOptions.pageOptions.locationHref);
+ const locationUrl = new URL(renderOptions.pageOptions.locationUrl);
dom.window.location = {};
[
'hash',
diff --git a/server/hydrogen-render/render-page-html.js b/server/hydrogen-render/render-page-html.js
index d209aa3..af1d60a 100644
--- a/server/hydrogen-render/render-page-html.js
+++ b/server/hydrogen-render/render-page-html.js
@@ -71,6 +71,11 @@ function renderPageHtml({
metaImageUrl = pageOptions.imageUrl;
}
+ let maybeRelCanonical = '';
+ if (pageOptions.canonicalUrl) {
+ maybeRelCanonical = sanitizeHtml(``);
+ }
+
const pageHtml = `
@@ -83,6 +88,7 @@ function renderPageHtml({
${sanitizeHtml(``)}
+ ${maybeRelCanonical}
${styles
.map(
(styleUrl) =>
diff --git a/server/middleware/timeout-middleware.js b/server/middleware/timeout-middleware.js
index f7fb2a5..6a592e8 100644
--- a/server/middleware/timeout-middleware.js
+++ b/server/middleware/timeout-middleware.js
@@ -86,7 +86,7 @@ async function timeoutMiddleware(req, res, next) {
title: `Server timeout - Matrix Public Archive`,
description: `Unable to respond in time (${requestTimeoutMs / 1000}s)`,
entryPoint: 'client/js/entry-client-timeout.js',
- locationHref: urlJoin(basePath, req.originalUrl),
+ locationUrl: urlJoin(basePath, req.originalUrl),
// We don't have a Matrix room so we don't know whether or not to index. Just choose
// a safe-default of false.
shouldIndex: false,
diff --git a/server/routes/client-side-room-alias-hash-redirect-route.js b/server/routes/client-side-room-alias-hash-redirect-route.js
index 6ab2443..d28152c 100644
--- a/server/routes/client-side-room-alias-hash-redirect-route.js
+++ b/server/routes/client-side-room-alias-hash-redirect-route.js
@@ -17,7 +17,7 @@ function clientSideRoomAliasHashRedirectRoute(req, res) {
title: `Page not found - Matrix Public Archive`,
description: `This page does not exist but we may be able to redirect you to the right place.`,
entryPoint: 'client/js/entry-client-room-alias-hash-redirect.js',
- locationHref: urlJoin(basePath, req.originalUrl),
+ locationUrl: urlJoin(basePath, req.originalUrl),
// We don't have a Matrix room so we don't know whether or not to index. Just choose
// a safe-default of false.
shouldIndex: false,
diff --git a/server/routes/room-directory-routes.js b/server/routes/room-directory-routes.js
index 468d8d6..7b88671 100644
--- a/server/routes/room-directory-routes.js
+++ b/server/routes/room-directory-routes.js
@@ -78,7 +78,7 @@ router.get(
description:
'Browse thousands of rooms using Matrix. The new portal into the Matrix ecosystem.',
entryPoint: 'client/js/entry-client-room-directory.js',
- locationHref: urlJoin(basePath, req.originalUrl),
+ locationUrl: urlJoin(basePath, req.originalUrl),
shouldIndex,
cspNonce: res.locals.cspNonce,
};
diff --git a/server/routes/room-routes.js b/server/routes/room-routes.js
index 2044993..dd383e3 100644
--- a/server/routes/room-routes.js
+++ b/server/routes/room-routes.js
@@ -916,7 +916,19 @@ router.get(
}),
blockedBySafeSearch: isNsfw,
entryPoint: 'client/js/entry-client-hydrogen.js',
- locationHref: urlJoin(basePath, req.originalUrl),
+ locationUrl: urlJoin(basePath, req.originalUrl),
+ canonicalUrl: matrixPublicArchiveURLCreator.archiveUrlForDate(
+ roomIdOrAlias,
+ new Date(toTimestamp),
+ {
+ preferredPrecision: precisionFromUrl,
+ // We purposely omit `scrollStartEventId` here because the canonical location
+ // for any given event ID is the page it resides on.
+ //
+ // We can avoid passing along the `viaServers` because we already joined the
+ // room above (see `ensureRoomJoined`).
+ }
+ ),
shouldIndex,
cspNonce: res.locals.cspNonce,
};