From aa6de883be61880dd65c32f190262d72e72fef88 Mon Sep 17 00:00:00 2001 From: Adam French Date: Thu, 30 Apr 2026 17:29:29 +0100 Subject: [PATCH] Move Open-WebUI to chat.${DOMAIN} subdomain Open-WebUI's SvelteKit frontend hardcodes asset/API paths at build time, so subpath hosting under /openwebui/ produced 404s on /_app/... assets. Move it to its own subdomain so it can run at root. - certbot: request cert with chat.${DOMAIN} as a third SAN via --expand - nginx (prod): drop /openwebui blocks; add chat.${DOMAIN} HTTP redirect + HTTPS server with the existing admin auth gate - nginx (dev): drop /openwebui blocks (no chat.localhost in dev) - compose: WEBUI_URL points to https://chat.${DOMAIN} Co-Authored-By: Claude Opus 4.7 --- certbot/entrypoint.sh | 7 +++-- docker-compose.yml | 2 +- nginx/nginx.conf.template | 53 ++++++++++++++++++++++++++++++----- nginx/nginx_dev.conf.template | 46 ------------------------------ 4 files changed, 51 insertions(+), 57 deletions(-) diff --git a/certbot/entrypoint.sh b/certbot/entrypoint.sh index 9666c42..944c788 100755 --- a/certbot/entrypoint.sh +++ b/certbot/entrypoint.sh @@ -1,7 +1,8 @@ #!/bin/sh -if [ ! -d /etc/letsencrypt/live/${DOMAIN} ]; then - certbot certonly --webroot -w /var/www/certbot --email ${EMAIL} -d ${DOMAIN} -d www.${DOMAIN} --agree-tos --non-interactive; -fi; +certbot certonly --webroot -w /var/www/certbot \ + --email ${EMAIL} \ + -d ${DOMAIN} -d www.${DOMAIN} -d chat.${DOMAIN} \ + --agree-tos --non-interactive --expand; trap exit TERM; diff --git a/docker-compose.yml b/docker-compose.yml index 9b392f6..8389b72 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -184,7 +184,7 @@ services: environment: - OLLAMA_BASE_URL=${OLLAMA_BASE_URL} - WEBUI_AUTH=False - - WEBUI_URL=https://www.${DOMAIN}/openwebui + - WEBUI_URL=https://chat.${DOMAIN} volumes: - openwebui_data:/app/backend/data diff --git a/nginx/nginx.conf.template b/nginx/nginx.conf.template index f961a1f..bf8f6f0 100644 --- a/nginx/nginx.conf.template +++ b/nginx/nginx.conf.template @@ -79,7 +79,6 @@ http { set $upstream_hasura http://$HASURA_HOST:$HASURA_PORT; set $upstream_quartz http://$QUARTZ_HOST:$QUARTZ_PORT; set $upstream_searxng http://$SEARXNG_HOST:$SEARXNG_PORT; - set $upstream_openwebui http://$OPENWEBUI_HOST:$OPENWEBUI_PORT; root /etc/nginx/html; index index.html; @@ -289,14 +288,56 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } - location /openwebui { - return 301 /openwebui/; + + + } + + server { + listen 80; + server_name chat.$DOMAIN; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; } - location /openwebui/ { + location / { + return 301 https://chat.$DOMAIN$request_uri; + } + } + + server { + listen 443 ssl; + http2 on; + server_name chat.$DOMAIN; + + ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem; + + set $upstream_openwebui http://$OPENWEBUI_HOST:$OPENWEBUI_PORT; + + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always; + + client_max_body_size 50M; + + location = /internal/auth/admin-validate { + internal; + rewrite ^ /auth/validate-admin break; + proxy_pass http://$BACKEND_HOST:$BACKEND_PORT; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + proxy_set_header Cookie $http_cookie; + } + + location @auth_denied { + return 302 https://www.$DOMAIN/admin/login?redirect=https://chat.$DOMAIN$request_uri; + } + + location / { auth_request /internal/auth/admin-validate; error_page 401 403 = @auth_denied; - rewrite ^/openwebui/(.*)$ /$1 break; proxy_pass $upstream_openwebui; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; @@ -309,8 +350,6 @@ http { proxy_read_timeout 300s; proxy_send_timeout 300s; } - - } } diff --git a/nginx/nginx_dev.conf.template b/nginx/nginx_dev.conf.template index 88539fb..aec5c62 100644 --- a/nginx/nginx_dev.conf.template +++ b/nginx/nginx_dev.conf.template @@ -52,7 +52,6 @@ http { set $upstream_hasura http://$HASURA_HOST:$HASURA_PORT; set $upstream_quartz http://$QUARTZ_HOST:$QUARTZ_PORT; set $upstream_searxng http://$SEARXNG_HOST:$SEARXNG_PORT; - set $upstream_openwebui http://$OPENWEBUI_HOST:$OPENWEBUI_PORT; location /uploads/ { alias /uploads/; @@ -206,28 +205,6 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } - location /openwebui { - return 301 /openwebui/; - } - - location /openwebui/ { - auth_request /internal/auth/admin-validate; - error_page 401 403 = @auth_denied; - rewrite ^/openwebui/(.*)$ /$1 break; - proxy_pass $upstream_openwebui; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - 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_buffering off; - proxy_read_timeout 300s; - proxy_send_timeout 300s; - } - - } server { @@ -243,7 +220,6 @@ http { set $upstream_hasura http://$HASURA_HOST:$HASURA_PORT; set $upstream_quartz http://$QUARTZ_HOST:$QUARTZ_PORT; set $upstream_searxng http://$SEARXNG_HOST:$SEARXNG_PORT; - set $upstream_openwebui http://$OPENWEBUI_HOST:$OPENWEBUI_PORT; location /uploads/ { alias /uploads/; @@ -397,28 +373,6 @@ http { proxy_set_header X-Forwarded-Proto $scheme; } - location /openwebui { - return 301 /openwebui/; - } - - location /openwebui/ { - auth_request /internal/auth/admin-validate; - error_page 401 403 = @auth_denied; - rewrite ^/openwebui/(.*)$ /$1 break; - proxy_pass $upstream_openwebui; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - 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_buffering off; - proxy_read_timeout 300s; - proxy_send_timeout 300s; - } - - } }