diff --git a/public/robots.txt b/public/robots.txt
index ed7d173c3a..4603063510 100644
--- a/public/robots.txt
+++ b/public/robots.txt
@@ -1,4 +1,3 @@
-User-agent: *
-Crawl-delay: 10
-Disallow:
-Sitemap: sitemap.xml
+User-agent: *
+Disallow:
+Sitemap: https://bewelcome.org/sitemap.xml
diff --git a/public/sitemap.xml b/public/sitemap.xml
index f0068f61b1..476e64ae75 100644
--- a/public/sitemap.xml
+++ b/public/sitemap.xml
@@ -1,133 +1,30 @@
-
-
-
- https://www.bewelcome.org/
- 2020-05-17T21:18:24+01:00
- 1.0
-
-
- https://www.bewelcome.org/signup
- 2020-05-17T21:18:24+01:00
- 1.0
-
-
- https://www.bewelcome.org/about
- 2020-05-17T21:18:27+01:00
- 1.0
-
-
- https://www.bewelcome.org/terms
- 2020-05-17T21:18:30+01:00
- 1.0
-
-
- https://www.bewelcome.org/privacy
- 2020-05-17T21:18:28+01:00
- 1.0
-
-
- https://www.bewelcome.org/impressum
- 2020-05-17T21:18:29+01:00
- 1.0
-
-
- https://www.bewelcome.org/safety
- 2020-05-17T21:18:30+01:00
- 1.0
-
-
- https://www.bewelcome.org/volunteer
- 2020-05-17T21:18:31+01:00
- 1.0
-
-
- https://www.bewelcome.org/about/faq
- 2020-05-17T21:18:31+01:00
- 0.8
-
-
- https://www.bewelcome.org/about/getactive
- 2020-05-17T21:18:31+01:00
- 0.8
-
-
- https://www.bewelcome.org/about/thepeople
- 2020-05-17T21:18:32+01:00
- 0.8
-
-
- https://www.bewelcome.org/stats
- 2020-05-17T21:18:32+01:00
- 1.0
-
-
- https://www.bewelcome.org/about/credits
- 2020-05-17T21:18:33+01:00
- 0.8
-
-
- https://www.bewelcome.org/login
- 2020-05-17T21:18:33+01:00
- 1.0
-
-
- https://www.bewelcome.org/safety/basics
- 2020-05-17T21:18:33+01:00
- 0.8
-
-
- https://www.bewelcome.org/safety/whattodo
- 2020-05-17T21:18:33+01:00
- 0.8
-
-
- https://www.bewelcome.org/safety/team
- 2020-05-17T21:18:34+01:00
- 0.8
-
-
- https://www.bewelcome.org/safety/faq
- 2020-05-17T21:18:34+01:00
- 0.8
-
-
- https://www.bewelcome.org/safety/tips
- 2020-05-17T21:18:34+01:00
- 0.8
-
-
- https://www.bewelcome.org/about/faq/2
- 2020-05-17T21:18:36+01:00
- 0.6
-
-
- https://www.bewelcome.org/about/faq/5
- 2020-05-17T21:18:35+01:00
- 0.6
-
-
- https://www.bewelcome.org/about/faq/0
- 2020-05-17T21:18:36+01:00
- 0.6
-
-
- https://www.bewelcome.org/about/faq/1
- 2020-05-17T21:18:36+01:00
- 0.6
-
-
- https://www.bewelcome.org/about/faq/3
- 2020-05-17T21:18:37+01:00
- 0.6
-
-
- https://www.bewelcome.org/about/faq/4
- 2020-05-17T21:18:36+01:00
- 0.6
-
-
- https://www.bewelcome.org/about/faq/6
- 2020-05-17T21:18:37+01:00
- 0.6
-
-
\ No newline at end of file
+
+
+ https://bewelcome.org/
+ https://bewelcome.org/signup
+ https://bewelcome.org/login
+ https://bewelcome.org/about
+ https://bewelcome.org/about/getactive
+ https://bewelcome.org/about/thepeople
+ https://bewelcome.org/about/credits
+ https://bewelcome.org/about/statistics
+ https://bewelcome.org/about/commentguidelines
+ https://bewelcome.org/press-information
+ https://bewelcome.org/feedback
+ https://bewelcome.org/faq
+ https://bewelcome.org/faq/2
+ https://bewelcome.org/faq/4
+ https://bewelcome.org/faq/6
+ https://bewelcome.org/faq/7
+ https://bewelcome.org/faq/9
+ https://bewelcome.org/safety
+ https://bewelcome.org/safety/basics
+ https://bewelcome.org/safety/whattodo
+ https://bewelcome.org/safety/team
+ https://bewelcome.org/safety/faq
+ https://bewelcome.org/safety/tips
+ https://bewelcome.org/volunteer
+ https://bewelcome.org/terms
+ https://bewelcome.org/privacy
+ https://bewelcome.org/impressum
+
diff --git a/templates/_canonical.html.twig b/templates/_canonical.html.twig
new file mode 100644
index 0000000000..c2f5adc52a
--- /dev/null
+++ b/templates/_canonical.html.twig
@@ -0,0 +1,7 @@
+{# Self-referencing canonical without query string (BeWelcome/sysadmins-infra#654).
+ Built from the route so defaults normalise (/faq/1 and /faq share one URL).
+ Error pages are rendered without a route, so they get no canonical. #}
+{% set canonical_route = app.request.attributes.get('_route') %}
+{% if canonical_route %}
+
+{% endif %}
diff --git a/templates/base-lite.html.twig b/templates/base-lite.html.twig
index d2c167b9da..9d803e1d6f 100644
--- a/templates/base-lite.html.twig
+++ b/templates/base-lite.html.twig
@@ -20,6 +20,7 @@
+ {% include '_canonical.html.twig' %}
diff --git a/templates/base.html.twig b/templates/base.html.twig
index 7f9a29f165..6bafebf0b2 100644
--- a/templates/base.html.twig
+++ b/templates/base.html.twig
@@ -19,6 +19,7 @@
+ {% include '_canonical.html.twig' %}
{% block redirect %}{% endblock %}
diff --git a/templates/home/home.html.twig b/templates/home/home.html.twig
index c8c3687b09..3c5f32dd78 100644
--- a/templates/home/home.html.twig
+++ b/templates/home/home.html.twig
@@ -11,6 +11,7 @@
BeWelcome - {{ 'home.headline.bewelcome.cultural' | trans }}
+ {% include '_canonical.html.twig' %}
{% include 'webpreview.html.twig' %}
diff --git a/templates/profile/profile.base.html.twig b/templates/profile/profile.base.html.twig
index aac78ffe9a..70c30c2cb3 100644
--- a/templates/profile/profile.base.html.twig
+++ b/templates/profile/profile.base.html.twig
@@ -19,6 +19,7 @@
+ {% include '_canonical.html.twig' %}
{% block redirect %}{% endblock %}
diff --git a/templates/search/searchmap.html.twig b/templates/search/searchmap.html.twig
index 660322c649..cfe39bfad8 100644
--- a/templates/search/searchmap.html.twig
+++ b/templates/search/searchmap.html.twig
@@ -9,6 +9,7 @@
{{ title }} - BeWelcome
+ {% include '_canonical.html.twig' %}
{{ encore_entry_link_tags('bewelcome') }}
diff --git a/templates/security/login.html.twig b/templates/security/login.html.twig
index 4d80528776..8ffaa13b9d 100644
--- a/templates/security/login.html.twig
+++ b/templates/security/login.html.twig
@@ -14,6 +14,7 @@
{{ 'login' | trans }} | BeWelcome
+ {% include '_canonical.html.twig' %}
diff --git a/tools/page/html.page.php b/tools/page/html.page.php
index 9a4147a3f0..9f8f11ea81 100644
--- a/tools/page/html.page.php
+++ b/tools/page/html.page.php
@@ -333,12 +333,28 @@ protected function head()
+
includeStylesheets();
$this->includeCustomElements();
$this->_tr_buffer_header = $this->getWords()->flushBuffer();
}
+ /**
+ * Self-referencing canonical (BeWelcome/sysadmins-infra#654). Legacy pages may rely on the
+ * query string, so only tracking parameters are dropped.
+ */
+ protected function getCanonicalUrl($baseuri)
+ {
+ $parts = parse_url($_SERVER['REQUEST_URI'] ?? '/');
+ parse_str($parts['query'] ?? '', $query);
+ $query = array_filter($query, function ($key) {
+ return !preg_match('/^(utm_.*|gclid|gbraid|wbraid|fbclid|msclkid)$/', $key);
+ }, ARRAY_FILTER_USE_KEY);
+
+ return rtrim($baseuri, '/') . ($parts['path'] ?? '/') . ($query ? '?' . http_build_query($query) : '');
+ }
+
protected function getPagePermalink() {
return 'index';
}