openvibers/openvibe.ai Commits

AI: unknown GET paths get the app's own 404 page and favicon instead of nginx's bare page (#39)

OP OpenVibers committed 10 Oct 2026 · 8ad6429c99 · 2 files, +23 −1

The release UX check flagged ai.openvibe.services: `/favicon.ico` answered 404 (on every page, including /stats), and the 404 page had no `<html lang>` (axe `html-has-lang`, serious). The app already serves both: the SVG app icon, and its own 404 page with lang and the Frame (a problem+json for API clients). The vhost's catch-all `location / { return 404; }` answered first with nginx's bare page.

The catch-all now passes GET and HEAD to the app. Other methods are refused at nginx, and `/metrics` and the API blocks are unchanged. The new `test/vhost-catchall.test.js` asserts it. nginx -t is clean on the host; the vhost is installed with ov-vhost-install after the deploy.

Changes

ddiff --git a/deploy/nginx/ai.openvibe.services.conf b/deploy/nginx/ai.openvibe.services.conf
iindex 3185cd9..5fc1ee5 100644
---- a/deploy/nginx/ai.openvibe.services.conf
++++ b/deploy/nginx/ai.openvibe.services.conf
@@@ -135,7 +135,15 @@ server {
proxy_set_header CF-Connecting-IP $remote_addr;
}
++ # Everything else: the app answers GET/HEAD (its /favicon.ico, and its own 404 page with lang and the Frame for
++ # browsers, a problem+json for API clients); other methods are refused here.
location / {
-- return 404;
++ limit_except GET HEAD { deny all; }
++ proxy_pass http://127.0.0.1:4700;
++ proxy_set_header Host $host;
++ proxy_set_header X-Real-IP $remote_addr;
++ proxy_set_header X-Forwarded-For $remote_addr;
++ proxy_set_header CF-Connecting-IP $remote_addr;
++ proxy_set_header X-Forwarded-Proto $scheme;
}
}
ddiff --git a/test/vhost-catchall.test.js b/test/vhost-catchall.test.js
nnew file mode 100644
iindex 0000000..a56a269
---- /dev/null
++++ b/test/vhost-catchall.test.js
@@@ -0,0 +1,14 @@
++'use strict';
++// deploy/nginx/ai.openvibe.services.conf: the catch-all hands GET/HEAD to the app, so browsers get its /favicon.ico and
++// its own 404 page (with lang and the Frame) instead of nginx's bare page; every other method is refused at nginx.
++const assert = require('assert');
++const fs = require('fs');
++const path = require('path');
++
++const conf = fs.readFileSync(path.join(__dirname, '..', 'deploy', 'nginx', 'ai.openvibe.services.conf'), 'utf8');
++const catchAll = conf.slice(conf.lastIndexOf(' location / {'));
++assert.match(catchAll, /limit_except GET HEAD \{ deny all; \}/, 'the catch-all only passes GET and HEAD');
++assert.match(catchAll, /proxy_pass http:\/\/127\.0\.0\.1:4700;/, 'and lets the app answer (favicon, 404 page)');
++assert.ok(!/location \/ \{\s*return 404;\s*\}/.test(conf), "nginx's bare 404 page (no lang) is gone");
++assert.match(conf, /location ~\* \^\/metrics\(\/\|\$\) \{ return 404; \}/, '/metrics stays refused');
++console.log('vhost catch-all: GET/HEAD reach the app (favicon, 404 page); other methods refused; /metrics refused');