diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
index 3309cd385..c434c56a7 100755
--- a/docker/entrypoint.sh
+++ b/docker/entrypoint.sh
@@ -17,6 +17,19 @@ is_alpine() {
[ -f /etc/alpine-release ]
}
+html_escape() {
+ printf '%s' "$1" | sed \
+ -e 's/&/\&/g' \
+ -e 's/\</g' \
+ -e 's/>/\>/g' \
+ -e 's/"/\"/g' \
+ -e "s/'/\'/g"
+}
+
+sed_escape() {
+ printf '%s\n' "$1" | sed 's/[&/\\]/\\&/g; s/\$/\\$/g'
+}
+
# Cleanup
rm -rf /var/www/html/*
@@ -84,6 +97,17 @@ if [[ "$MODE" == "frontend" || "$MODE" == "dual" || "$MODE" == "standalone" ]];
sed -i "s/var SPEEDTEST_SERVERS = \"server-list.json\";/var SPEEDTEST_SERVERS = \"$SERVER_LIST_URL_ESCAPED\";/" /var/www/html/index-modern.html
sed -i "s/var SPEEDTEST_SERVERS = \\[/var SPEEDTEST_SERVERS = \"$SERVER_LIST_URL_ESCAPED\";\\n\\t\\t\\/\\*/" /var/www/html/index-classic.html
fi
+
+ # Replace title placeholders if TITLE is set
+ if [ ! -z "$TITLE" ]; then
+ TITLE_ONE_LINE=${TITLE//$'\r'/}
+ TITLE_ONE_LINE=${TITLE_ONE_LINE//$'\n'/ }
+ TITLE_HTML_ESCAPED=$(html_escape "$TITLE_ONE_LINE")
+ TITLE_ESCAPED=$(sed_escape "$TITLE_HTML_ESCAPED")
+ sed -i "s/
LibreSpeed<\\/title>/$TITLE_ESCAPED<\\/title>/g; s/LibreSpeed<\\/h1>/$TITLE_ESCAPED<\\/h1>/g" /var/www/html/index-classic.html
+ sed -i "s/LibreSpeed<\\/title>/$TITLE_ESCAPED<\\/title>/g" /var/www/html/index.html
+ sed -i "s/LibreSpeed - Free and Open Source Speedtest<\\/title>/$TITLE_ESCAPED - Free and Open Source Speedtest<\\/title>/g" /var/www/html/index-modern.html
+ fi
# Support legacy EMAIL env var as fallback for GDPR_EMAIL
if [ -z "$GDPR_EMAIL" ] && [ ! -z "$EMAIL" ]; then
diff --git a/tests/docker-compose-playwright.yml b/tests/docker-compose-playwright.yml
index d84c583ab..2575d26ed 100644
--- a/tests/docker-compose-playwright.yml
+++ b/tests/docker-compose-playwright.yml
@@ -26,6 +26,7 @@ services:
- MODE=standalone
- WEBPORT=8080
- USE_NEW_DESIGN=true
+ - 'TITLE=Grüße "Tempo" ''Österreich'''
ports:
- "18185:8080"
diff --git a/tests/e2e/title-special-chars.spec.js b/tests/e2e/title-special-chars.spec.js
new file mode 100644
index 000000000..4fe942555
--- /dev/null
+++ b/tests/e2e/title-special-chars.spec.js
@@ -0,0 +1,16 @@
+const { test, expect } = require('@playwright/test');
+const { baseUrls } = require('./helpers/env');
+
+const specialTitle = 'Grüße "Tempo" \'Österreich\'';
+
+test.describe('TITLE special characters', () => {
+ test('modern page title supports umlauts and quotes', async ({ page }) => {
+ await page.goto(`${baseUrls.standaloneNew}/index-modern.html`);
+ await expect(page).toHaveTitle(`${specialTitle} - Free and Open Source Speedtest`);
+ });
+
+ test('classic heading supports umlauts and quotes', async ({ page }) => {
+ await page.goto(`${baseUrls.standaloneNew}/index-classic.html`);
+ await expect(page.locator('h1').first()).toHaveText(specialTitle);
+ });
+});