2

I migrated an application in Flask served by waitress to FastAPI served by uvicorn, but I can't force the links (generated by url_for inside the index.html template) to use HTTPS instead of HTTP.

With waitress I used:

from waitress import serve
import flask_app

PORT=5000
HOST_IP_ADDRESS='0.0.0.0'

serve(flask_app.app, host=HOST_IP_ADDRESS, port=PORT, url_scheme="https")

with uvicorn I tried to use proxy_headers, but that didn't work. I used a workaround in the index.html

<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">

which correctly loaded the style.css from static files, but the links to another endpoint still use HTTP.

Is there an easy way to force all links created by url_for to use HTTPS?

halal
  • 25
  • 1
  • 9

2 Answers2

4

I have also run into this issue before. One possible solution is to create a custom url_for function which changes the protocol, then add it to the Jinja environment. One possible implementation may look something like this:

template = Jinja2Templates("/path/to/templates")

def https_url_for(request: Request, name: str, **path_params: Any) -> str:

    http_url = request.url_for(name, **path_params)

    # Replace 'http' with 'https'
    return http_url.replace("http", "https", 1)

template.env.globals["https_url_for"] = https_url_for

You will have to pass the request to the function so it knows how to generate the url_for, but the request should be passed in to your Jinja2 template either way.

You can then use it in your Jinja2 template like this:

https_url_for(request, "/https/path", search="hi")

The resulting url should look like https://<domain>/https/path?search=hi.

  • Perfect, this workaround worked for me. Just to complete the final code: I had links in jinja in format `url_for('fastapi_function_name', kwarg1='something', kwarg2='somethingelse')`. I modified it to your https_url_for function and added the request as first argument: `https_url_for(request, 'fastapi_function_name', kwarg1='something', kwarg2='somethingelse')`. The stylesheet and images inside the html still stay the same: `{{ url_for('static', path='css/style.css') }}` if `app.mount("/static", StaticFiles(directory="static"), name="static")` and the same for images – halal Jan 03 '22 at 13:52
0

I had the same problems. On develop environment all links were with http. I solved it this way.

from starlette.templating import Jinja2Templates
from sqladmin import Admin
from jinja2 import ChoiceLoader, FileSystemLoader, PackageLoader
import jinja2
if hasattr(jinja2, "pass_context"):
    pass_context = jinja2.pass_context
else:
    pass_context = jinja2.contextfunction


@pass_context
def https_url_for(context: dict, name: str, **path_params) -> str:
    request = context["request"]
    http_url = request.url_for(name, **path_params)
    return http_url.replace("http", "https", 1)


class CustomAdmin(Admin):
    def init_templating_engine(self) -> Jinja2Templates:
        templates = Jinja2Templates("templates")
        loaders = [
            FileSystemLoader(self.templates_dir),
            PackageLoader("sqladmin", "templates"),
        ]

        templates.env.loader = ChoiceLoader(loaders)
        templates.env.globals["min"] = min
        templates.env.globals["zip"] = zip
        templates.env.globals["admin"] = self
        templates.env.globals["is_list"] = lambda x: isinstance(x, list)
        templates.env.globals["url_for"] = https_url_for
        return templates

After all just import this class in main file and init the admin class