Problem
In v3 (tina4_python 3.11.21), GET / unconditionally renders the framework's built-in "Tina4Python" welcome page whenever the user hasn't registered their own / route. There's no way to turn this off.
In v2 (PHP) this was controlled by a TINA4_APP_INDEX env var which could be set to false to suppress the default landing. The Python v3 equivalent is missing — I grepped the whole package and there is no env var or config knob that disables core/server.py::_render_landing_page().
The only escape today is to register your own @get("/") handler. That works but has two problems:
- Discoverability — new users don't know the default landing is hijacking
/ until they ship a production SPA and see the "Tina4Python" page instead of their own, as I did. (The public/index.html path convention suggests it would auto-serve at /, which it does at /index.html but not at /.)
- Production hygiene — the framework-branded landing page exposes the framework version, a "Dev Admin" link, and a "Gallery" button to users in production unless someone remembers to shadow
/. This is information-disclosure by default.
Security / defence-in-depth
The original reporter pointed out that in v2 PHP there were historical cases where the landing page could be reached via URL variants (trailing slash, etc.) that skipped middleware, so having an explicit opt-out was used as a hardening step.
I tested a handful of variants against v3 Python and couldn't reproduce a genuine middleware bypass — /api/bookings/, /api/bookings//, /%2e%2e, etc. all return correct 4xx responses with middleware running. So this issue is not reporting a current bypass, just asking for the same defence-in-depth knob v2 had. Landing pages that activate on uncovered paths have a track record in other frameworks (Spring Boot's /error, early Rails' /rails/info) of being vectors for leakage or probing, so an opt-out is a sensible hardening lever even when the current implementation is correct.
Proposed fix
Add an env var (names to bikeshed — matching v2's convention would be nicest):
TINA4_APP_INDEX=false — v2 parity
- or
TINA4_LANDING_PAGE=false
- or
TINA4_DISABLE_WELCOME=true
When set to the disabling value, _render_landing_page() should be skipped and GET / with no matching route should 404 like any other unknown path.
Bonus: consider having the framework auto-serve src/public/index.html at / when it exists, as many frameworks do. That would cover the SPA hosting case without requiring a custom route, and would make the existing public/ convention symmetric (today public/index.html serves at /index.html but not at /).
Adjacent bug worth mentioning
While testing I noticed tina4 is emitting HTTP/1.1 404 OK as the status line on missing paths — that's status code 404 with reason phrase literally "OK". It's malformed HTTP; should be HTTP/1.1 404 Not Found. Happy to file separately if preferred.
Current workaround
# src/routes/anon/spa_root.py
import os
from tina4_python import get, noauth
_INDEX = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", "..", "public", "index.html"),
)
@get("/")
@noauth()
async def spa_root(request, response):
with open(_INDEX, "r", encoding="utf-8") as f:
body = f.read()
return response(body, 200, "text/html; charset=utf-8")
Environment
tina4_python 3.11.21
- Python 3.12
- Running under
tina4python serve inside Docker with TINA4_OVERRIDE_CLIENT=true
- Repo: single-container production-style setup (multi-stage Docker build bakes a Vite-built SPA into
src/public/)
Problem
In v3 (
tina4_python3.11.21),GET /unconditionally renders the framework's built-in "Tina4Python" welcome page whenever the user hasn't registered their own/route. There's no way to turn this off.In v2 (PHP) this was controlled by a
TINA4_APP_INDEXenv var which could be set tofalseto suppress the default landing. The Python v3 equivalent is missing — I grepped the whole package and there is no env var or config knob that disablescore/server.py::_render_landing_page().The only escape today is to register your own
@get("/")handler. That works but has two problems:/until they ship a production SPA and see the "Tina4Python" page instead of their own, as I did. (Thepublic/index.htmlpath convention suggests it would auto-serve at/, which it does at/index.htmlbut not at/.)/. This is information-disclosure by default.Security / defence-in-depth
The original reporter pointed out that in v2 PHP there were historical cases where the landing page could be reached via URL variants (trailing slash, etc.) that skipped middleware, so having an explicit opt-out was used as a hardening step.
I tested a handful of variants against v3 Python and couldn't reproduce a genuine middleware bypass —
/api/bookings/,/api/bookings//,/%2e%2e, etc. all return correct 4xx responses with middleware running. So this issue is not reporting a current bypass, just asking for the same defence-in-depth knob v2 had. Landing pages that activate on uncovered paths have a track record in other frameworks (Spring Boot's/error, early Rails'/rails/info) of being vectors for leakage or probing, so an opt-out is a sensible hardening lever even when the current implementation is correct.Proposed fix
Add an env var (names to bikeshed — matching v2's convention would be nicest):
TINA4_APP_INDEX=false— v2 parityTINA4_LANDING_PAGE=falseTINA4_DISABLE_WELCOME=trueWhen set to the disabling value,
_render_landing_page()should be skipped andGET /with no matching route should 404 like any other unknown path.Bonus: consider having the framework auto-serve
src/public/index.htmlat/when it exists, as many frameworks do. That would cover the SPA hosting case without requiring a custom route, and would make the existingpublic/convention symmetric (todaypublic/index.htmlserves at/index.htmlbut not at/).Adjacent bug worth mentioning
While testing I noticed tina4 is emitting
HTTP/1.1 404 OKas the status line on missing paths — that's status code404with reason phrase literally"OK". It's malformed HTTP; should beHTTP/1.1 404 Not Found. Happy to file separately if preferred.Current workaround
Environment
tina4_python3.11.21tina4python serveinside Docker withTINA4_OVERRIDE_CLIENT=truesrc/public/)