From 8de3106596fb83dea3c5c4a9f8dc7814e6d144f5 Mon Sep 17 00:00:00 2001 From: SysAdmin Agent Date: Sun, 5 Apr 2026 16:37:19 +0000 Subject: [PATCH] Switch GrampsWeb from subpath to subdomain ahnenforschung.vhtv-stiftung.de (STI-91) The subpath approach (/ahnenforschung/) required ~15 nginx sub_filter rules to rewrite JS/CSS/HTML paths, causing persistent issues with font loading, Service Worker, and API routes. GrampsWeb is not designed for subpath deployment. Switch to dedicated subdomain where GrampsWeb runs on root /: - nginx.conf: remove all sub_filter rules, add clean subdomain server block - base.html: update sidebar link to subdomain URL - compose.yml: update GRAMPSWEB_BASE_URL default - env-template.txt: update BASE_URL reference After deploy: run certbot for ahnenforschung.vhtv-stiftung.de SSL cert, update .env GRAMPSWEB_BASE_URL, restart containers. Co-Authored-By: Paperclip --- app/templates/base.html | 2 +- compose.yml | 4 +- deploy-production/nginx.conf | 120 +++++++++++++++-------------------- env-template.txt | 4 +- 4 files changed, 55 insertions(+), 75 deletions(-) diff --git a/app/templates/base.html b/app/templates/base.html index 668e50f..b682e51 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -689,7 +689,7 @@ Geschichte - + Ahnenforschung diff --git a/compose.yml b/compose.yml index 9a59b86..4f398fc 100644 --- a/compose.yml +++ b/compose.yml @@ -198,7 +198,7 @@ services: - GRAMPSWEB_ADMIN_EMAIL=${GRAMPSWEB_ADMIN_EMAIL:-admin@localhost} - GRAMPSWEB_ADMIN_PASSWORD=${GRAMPSWEB_ADMIN_PASSWORD:-gramps_dev_password} - GRAMPSWEB_TREE=${GRAMPSWEB_TREE:-Stiftung} - - GRAMPSWEB_BASE_URL=${GRAMPSWEB_BASE_URL:-https://vhtv-stiftung.de/ahnenforschung} + - GRAMPSWEB_BASE_URL=${GRAMPSWEB_BASE_URL:-https://ahnenforschung.vhtv-stiftung.de} - GRAMPSWEB_CELERY_CONFIG__broker_url=redis://redis:6379/0 - GRAMPSWEB_CELERY_CONFIG__result_backend=redis://redis:6379/0 - GRAMPSWEB_RATELIMIT_STORAGE_URI=redis://redis:6379/1 @@ -207,7 +207,7 @@ services: - sh - -c - | - # All subpath rewriting is handled by nginx sub_filter — no container patching needed. + # GrampsWeb runs on its own subdomain — no subpath rewriting needed. echo "[grampsweb] Ensuring admin user exists ..." python3 << 'PYEOF' 2>&1 | grep -v Gtk from gramps_webapi.app import create_app diff --git a/deploy-production/nginx.conf b/deploy-production/nginx.conf index ccaf93f..00827f3 100644 --- a/deploy-production/nginx.conf +++ b/deploy-production/nginx.conf @@ -1,13 +1,13 @@ # HTTP server block - redirect to HTTPS server { listen 80; - server_name vhtv-stiftung.de www.vhtv-stiftung.de; + server_name vhtv-stiftung.de www.vhtv-stiftung.de ahnenforschung.vhtv-stiftung.de; # Redirect all HTTP traffic to HTTPS - return 301 https://$server_name$request_uri; + return 301 https://$host$request_uri; } -# HTTPS server block +# HTTPS server block — Stiftung Django app server { listen 443 ssl http2; server_name vhtv-stiftung.de www.vhtv-stiftung.de; @@ -82,73 +82,6 @@ server { proxy_send_timeout 300s; } - # GrampsWeb: fix double fonts/ path from CSS relative URL resolution - # fonts.css at /ahnenforschung/fonts/ references url('fonts/file.woff2') - # which resolves to /ahnenforschung/fonts/fonts/file.woff2 - location /ahnenforschung/fonts/fonts/ { - proxy_pass http://127.0.0.1:8090/fonts/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # GrampsWeb Ahnenforschung - # All path rewriting happens here via sub_filter — no container-side patching needed. - # GrampsWeb SPA uses Vaadin Router with absolute paths; the injected History API - # interceptor ensures pushState/replaceState calls get the /ahnenforschung/ prefix, - # while lets the router strip it back for route matching. - location /ahnenforschung/ { - proxy_pass http://127.0.0.1:8090/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Script-Name /ahnenforschung; - - # Disable upstream compression so sub_filter can operate on response bodies - proxy_set_header Accept-Encoding ""; - - # Apply sub_filter to HTML, JS, and CSS responses - sub_filter_types application/javascript text/javascript text/css; - sub_filter_once off; - - # HTML: set so the SPA router knows its prefix - sub_filter '' ''; - - # HTML: inject History API interceptor — ensures pushState/replaceState - # always include the /ahnenforschung/ prefix for correct SPA routing on reload - sub_filter '' ''; - - # JS: rewrite hardcoded absolute API/resource paths - sub_filter '"/api/' '"/ahnenforschung/api/'; - sub_filter '`/api/' '`/ahnenforschung/api/'; - sub_filter '"/lang/' '"/ahnenforschung/lang/'; - sub_filter '`/lang/' '`/ahnenforschung/lang/'; - sub_filter '"/fonts/' '"/ahnenforschung/fonts/'; - sub_filter '`/fonts/' '`/ahnenforschung/fonts/'; - sub_filter '"/assets/' '"/ahnenforschung/assets/'; - sub_filter '`/assets/' '`/ahnenforschung/assets/'; - - # JS: rewrite root redirects - sub_filter 'location.href="/"' 'location.href="/ahnenforschung/"'; - sub_filter 'document.location.href="/"' 'document.location.href="/ahnenforschung/"'; - - # JS: service worker route handling - sub_filter 'createHandlerBoundToURL("/index.html")' 'createHandlerBoundToURL("/ahnenforschung/index.html")'; - - # CSS font paths: handled by separate location block for /ahnenforschung/fonts/fonts/ - - # WebSocket support - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - - proxy_read_timeout 300s; - proxy_connect_timeout 60s; - proxy_send_timeout 300s; - } - # Health check endpoint location /health/ { access_log off; @@ -165,3 +98,50 @@ server { deny all; } } + +# HTTPS server block — GrampsWeb Ahnenforschung (subdomain) +# No sub_filter or path rewriting needed — GrampsWeb runs on root / +server { + listen 443 ssl http2; + server_name ahnenforschung.vhtv-stiftung.de; + + # SSL — will be updated by certbot after first run + ssl_certificate /etc/letsencrypt/live/vhtv-stiftung.de/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/vhtv-stiftung.de/privkey.pem; + + # SSL Security Settings + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # HSTS + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + + # Large file uploads (GEDCOM imports) + client_max_body_size 100M; + + location / { + proxy_pass http://127.0.0.1:8090; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # WebSocket support + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + proxy_read_timeout 300s; + proxy_connect_timeout 60s; + proxy_send_timeout 300s; + } +} diff --git a/env-template.txt b/env-template.txt index 75e4b88..0a62fe6 100644 --- a/env-template.txt +++ b/env-template.txt @@ -64,7 +64,7 @@ GRAMPSWEB_SECRET_KEY=your-grampsweb-secret-key-here GRAMPSWEB_ADMIN_EMAIL=admin@vhtv-stiftung.de GRAMPSWEB_ADMIN_PASSWORD=your-grampsweb-admin-password-here GRAMPSWEB_TREE=Stiftung -# Full external URL including subpath (used by GrampsWeb for email links, OIDC, etc.) -GRAMPSWEB_BASE_URL=https://vhtv-stiftung.de/ahnenforschung +# Full external URL (used by GrampsWeb for email links, OIDC, etc.) +GRAMPSWEB_BASE_URL=https://ahnenforschung.vhtv-stiftung.de \ No newline at end of file