Switch GrampsWeb from subpath to subdomain ahnenforschung.vhtv-stiftung.de (STI-91)
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy (push) Has been cancelled
Code Quality / quality (push) Has been cancelled

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 <noreply@paperclip.ing>
This commit is contained in:
SysAdmin Agent
2026-04-05 16:37:19 +00:00
parent a9201138c2
commit 8de3106596
4 changed files with 55 additions and 75 deletions

View File

@@ -689,7 +689,7 @@
<i class="fas fa-book-open"></i> <i class="fas fa-book-open"></i>
<span>Geschichte</span> <span>Geschichte</span>
</a> </a>
<a class="sidebar-link" href="/ahnenforschung/" target="_blank"> <a class="sidebar-link" href="https://ahnenforschung.vhtv-stiftung.de" target="_blank">
<i class="fas fa-tree"></i> <i class="fas fa-tree"></i>
<span>Ahnenforschung</span> <span>Ahnenforschung</span>
</a> </a>

View File

@@ -198,7 +198,7 @@ services:
- GRAMPSWEB_ADMIN_EMAIL=${GRAMPSWEB_ADMIN_EMAIL:-admin@localhost} - GRAMPSWEB_ADMIN_EMAIL=${GRAMPSWEB_ADMIN_EMAIL:-admin@localhost}
- GRAMPSWEB_ADMIN_PASSWORD=${GRAMPSWEB_ADMIN_PASSWORD:-gramps_dev_password} - GRAMPSWEB_ADMIN_PASSWORD=${GRAMPSWEB_ADMIN_PASSWORD:-gramps_dev_password}
- GRAMPSWEB_TREE=${GRAMPSWEB_TREE:-Stiftung} - 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__broker_url=redis://redis:6379/0
- GRAMPSWEB_CELERY_CONFIG__result_backend=redis://redis:6379/0 - GRAMPSWEB_CELERY_CONFIG__result_backend=redis://redis:6379/0
- GRAMPSWEB_RATELIMIT_STORAGE_URI=redis://redis:6379/1 - GRAMPSWEB_RATELIMIT_STORAGE_URI=redis://redis:6379/1
@@ -207,7 +207,7 @@ services:
- sh - sh
- -c - -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 ..." echo "[grampsweb] Ensuring admin user exists ..."
python3 << 'PYEOF' 2>&1 | grep -v Gtk python3 << 'PYEOF' 2>&1 | grep -v Gtk
from gramps_webapi.app import create_app from gramps_webapi.app import create_app

View File

@@ -1,13 +1,13 @@
# HTTP server block - redirect to HTTPS # HTTP server block - redirect to HTTPS
server { server {
listen 80; 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 # 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 { server {
listen 443 ssl http2; listen 443 ssl http2;
server_name vhtv-stiftung.de www.vhtv-stiftung.de; server_name vhtv-stiftung.de www.vhtv-stiftung.de;
@@ -82,73 +82,6 @@ server {
proxy_send_timeout 300s; 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 <base href> 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 <base href> so the SPA router knows its prefix
sub_filter '<base href="/">' '<base href="/ahnenforschung/">';
# HTML: inject History API interceptor — ensures pushState/replaceState
# always include the /ahnenforschung/ prefix for correct SPA routing on reload
sub_filter '</head>' '<script>!function(){var p="/ahnenforschung",o=history.pushState,r=history.replaceState;function w(u){return"string"==typeof u&&"/"===u[0]&&0!==u.indexOf(p)?p+u:u}history.pushState=function(s,t,u){return o.call(this,s,t,w(u))},history.replaceState=function(s,t,u){return r.call(this,s,t,w(u))}}();</script></head>';
# 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 # Health check endpoint
location /health/ { location /health/ {
access_log off; access_log off;
@@ -165,3 +98,50 @@ server {
deny all; 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;
}
}

View File

@@ -64,7 +64,7 @@ GRAMPSWEB_SECRET_KEY=your-grampsweb-secret-key-here
GRAMPSWEB_ADMIN_EMAIL=admin@vhtv-stiftung.de GRAMPSWEB_ADMIN_EMAIL=admin@vhtv-stiftung.de
GRAMPSWEB_ADMIN_PASSWORD=your-grampsweb-admin-password-here GRAMPSWEB_ADMIN_PASSWORD=your-grampsweb-admin-password-here
GRAMPSWEB_TREE=Stiftung GRAMPSWEB_TREE=Stiftung
# Full external URL including subpath (used by GrampsWeb for email links, OIDC, etc.) # Full external URL (used by GrampsWeb for email links, OIDC, etc.)
GRAMPSWEB_BASE_URL=https://vhtv-stiftung.de/ahnenforschung GRAMPSWEB_BASE_URL=https://ahnenforschung.vhtv-stiftung.de