In the production environment we will use the backend to serve the frontend (in development we used the frontend to serve itself and proxy requests to the backend).
Creating the blueprint
The blueprint itself can be created with the following code in
from quart import Blueprint blueprint = Blueprint("serving", __name__)
and activated by adding the following to
from backend.blueprints.serving import blueprint as serving_blueprint def create_app() -> Quart: ... app.register_blueprint(serving_blueprint)
Serving static assets
Quart will by default serve any assets placed in the
app.root_path / static) directory. So
there is nothing for us to do here other than ensure that static
assets are placed in this directory.
As the frontend routes paths to the displayed page the backend has no
knowledge which paths are valid. For this reason the route handler
should serve any path and let the frontend decide if it is valid or
not. This is done in quart using a
<path:path> url variable.
In addition the served page should be as secure as we can make it
using secure headers, such as a Content Security
various others. The following should be added to
from typing import Optional from quart import make_response, render_template, ResponseReturnValue from quart_rate_limiter import rate_exempt from werkzeug.http import COEP, COOP @blueprint.route("/") @blueprint.route("/<path:path>") @rate_exempt async def index(path: Optional[str] = None) -> ResponseReturnValue: response = await make_response(await render_template("index.html")) response.headers["Content-Security-Policy"] = "" response.content_security_policy.default_src = "'self'" response.content_security_policy.base_uri = "'self'" response.content_security_policy.font_src = "'self' data:" response.content_security_policy.form_action = "'self'" response.content_security_policy.frame_ancestors = "'none'" response.content_security_policy.img_src = "'self' data:" response.content_security_policy.style_src = "'self' 'unsafe-inline'" response.cross_origin_embedder_policy = COEP.REQUIRE_CORP response.cross_origin_opener_policy = COOP.SAME_ORIGIN response.headers["Referrer-Policy"] = "no-referrer, strict-origin-when-cross-origin" response.headers["Strict-Transport-Security"] = "max-age=63072000; includeSubDomains; preload" response.headers["X-Content-Type-Options"] = "nosniff" response.headers["X-Frame-Options"] = "SAMEORIGIN" response.headers["X-XSS-Protection"] = "1; mode=block" return response
The frontend build creates an
index.html which should be placed
backend/src/backend/templates folder and creates static
files that should be placed in the
folder for the above to work. See the Docker
section for more.