-
-
Notifications
You must be signed in to change notification settings - Fork 931
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question: producing https addresses using url_for #538
Comments
Did you ever get an answer on this? I am having a similar problem using jwilder/nginx-proxy as it redirects automatically to https, but the references are to http://domain.name instead of https://domain.name. The difference is I am using Starlette and not FastAPI. |
Note: If in a hurry, skip to the end. The way this information is passed to the app is via the ASGI spec itself. The server (Uvicorn, Hypercorn, or any other) is the one that tells the app if it is running on HTTPS or HTTP. If you provide the HTTPS certificates to the ASGI server (e.g. Uvicorn) it will know it is running on HTTPS and pass that information to the framework (Starlette, FastAPI, or anything else). But it's very common to have a "TLS Proxy" on top of it. That would be something that has the HTTPS certificates, handles the connection, and passes the pure HTTP to the thing running behind (in this case, Uvicorn, running your Starlette/FastAPI app). Examples of programs that can run as TLS Proxies include Nginx, HAProxy, Traefik (I recommend Traefik 😉 ). The same would be done by Heroku or jwilder's Docker image (based on Nginx). But these TLS Proxies (and actually many other layers and servers) create some HTTP headers to let the thing that runs after them know that they are handling HTTPS for them. But by default, none of the intermediate parts (Nginx, Traefik, Uvicorn) receive and accept those HTTPS headers from outside, as that would be a security risk. But if you know that the specific part (e.g. Uvicorn) is behind a TLS Proxy, you can normally configure/override it to receive those HTTP headers about the HTTPS connection. In Uvicorn, the command parameter is |
@tiangolo - Thank you for the response and really like FastAPI by the way. This solves the problem. I also temporarily solved it by adding to my template until I make the changes in Nginx or switch to Traefik (been meaning to try it). In Flask the URL would be served up relative , but in Starlette it is served up absolute. Would it not be better for it to be relative vs absolute? Maybe it should just be an option in how static files are severed. Something like this. If I am correct in how I think Starlette is operating it would be good to update the static file documentation to include that it is absolute path and not relative. Plus how to get Uvicorn use --proxy-headers. |
@devsetgo if you want to use relative URLs, I think you can just write them as is in your template, you then don't need to use I think the main thing But with relative URLs you don't need those, so I think you can just use the explicit relative URL. |
Using However it might be nice to not have to worry about this, and for us to just serve up relative urls by default, since there's less potential for misconfiguration that way. We could still provide an alternative for absolute URLs, but we could more throughly document how to ensure it's properly configured. Also relevant is encode/uvicorn#369, since that'd mean that we're getting the configuration correct automatically more often. |
@tiangolo why the FastAPI doesn't work correctly, even when |
@i4x Could you make sure to update to the latest version of uvicorn? What platform is the service deployed to? Can you output |
Note that the latest version of |
The version tested in the parent was {"host":"mydomain.net","user-agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36","accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3","accept-encoding":"gzip, deflate, br","accept-language":"en-US,en;q=0.9","cache-control":"max-age=0","cookie":"SESS99def2ca8b51de44904e6acf20ead392=8t5neatlj9ffn43933toggif16","sec-fetch-mode":"navigate","sec-fetch-site":"none","sec-fetch-user":"?1","upgrade-insecure-requests":"1","x-forwarded-for":"192.168.1.254","x-forwarded-proto":"https","x-real-ip":"192.168.1.254"} The {'type': 'http', 'http_version': '1.1', 'server': ('192.168.1.103', 1717), 'client': ('192.168.1.185', 42582), 'scheme': 'http', 'method': 'GET', 'root_path': '', 'path': '/items/1', 'raw_path': b'/items/1', 'query_string': b'', 'headers': [(b'host', b'mydomain.net'), (b'user-agent', b'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'), (b'accept', b'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3'), (b'accept-encoding', b'gzip, deflate, br'), (b'accept-language', b'en-US,en;q=0.9'), (b'cache-control', b'max-age=0'), (b'cookie', b'SESS99def2ca8b51de44904e6acf20ead392=8t5neatlj9ffn43933toggif16'), (b'sec-fetch-mode', b'navigate'), (b'sec-fetch-site', b'none'), (b'sec-fetch-user', b'?1'), (b'upgrade-insecure-requests', b'1'), (b'x-forwarded-for', b'192.168.1.254'), (b'x-forwarded-proto', b'https'), (b'x-real-ip', b'192.168.1.254')], 'fastapi_astack': <contextlib.AsyncExitStack object at 0x7fbdd5f5b6d0>, 'app': <fastapi.applications.FastAPI object at 0x7fbdd8f7a410>, 'router': <fastapi.routing.APIRouter object at 0x7fbdd8f7a610>, 'endpoint': <function read_root at 0x7fbdd5f53cb0>, 'path_params': {'item_id': '1'}} Downgrading the default uvicorn installed by FastAPI to {"host":"mydomain.net","user-agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36","accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3","accept-encoding":"gzip, deflate, br","accept-language":"en-US,en;q=0.9","cache-control":"max-age=0","cookie":"SESS99def2ca8b51de44904e6acf20ead392=8t5neatlj9ffn43933toggif16","sec-fetch-mode":"navigate","sec-fetch-site":"none","sec-fetch-user":"?1","upgrade-insecure-requests":"1","x-forwarded-for":"192.168.1.254","x-forwarded-proto":"https","x-real-ip":"192.168.1.254"} The {'type': 'http', 'http_version': '1.1', 'server': ('192.168.1.103', 1717), 'client': ('192.168.1.254', 0), 'scheme': 'https', 'method': 'GET', 'root_path': '', 'path': '/items/1', 'raw_path': b'/items/1', 'query_string': b'', 'headers': [(b'host', b'mydomain.net'), (b'user-agent', b'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36'), (b'accept', b'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3'), (b'accept-encoding', b'gzip, deflate, br'), (b'accept-language', b'en-US,en;q=0.9'), (b'cache-control', b'max-age=0'), (b'cookie', b'SESS99def2ca8b51de44904e6acf20ead392=8t5neatlj9ffn43933toggif16'), (b'sec-fetch-mode', b'navigate'), (b'sec-fetch-site', b'none'), (b'sec-fetch-user', b'?1'), (b'upgrade-insecure-requests', b'1'), (b'x-forwarded-for', b'192.168.1.254'), (b'x-forwarded-proto', b'https'), (b'x-real-ip', b'192.168.1.254')], 'fastapi_astack': <contextlib.AsyncExitStack object at 0x7fa8cb9c3e90>, 'app': <fastapi.applications.FastAPI object at 0x7fa8cee52250>, 'router': <fastapi.routing.APIRouter object at 0x7fa8ce531550>, 'endpoint': <function read_root at 0x7fa8cb9c6320>, 'path_params': {'item_id': '1'}} |
Wonderful. So, under |
Wonderful! Thanks for the feedback. ✨ |
There is/was an issue in starlette repo about having url_for() do the right thing when behind a reverse proxy: encode/starlette#538 (comment) but really there is no need, we can just point to the static assets directly.
Here's a note for Nginx users: be sure to configure your "server" config section to actually forward the
Lastly, uvicorn uses |
@tiangolo 'url_for' is supposed to be a layer between the routing and the templates/web pages. that's why you reference the function name in the call to url_for instead of the path. It would be nice if url_for worked the same way as it does in flask where it returns a relative link, unless there is something that drives it to an absolute, or at least an argument to return relative instead of absolute. Maybe it would have to be a filter instead so as not to interfere with any key,value pairs that someone would want to use. May the default would be relative with a filter to pass it through to force an absolute (and maybe even a specific protocol: http/https). |
In my case, using traefik and uvicorn, I could set the environment variable in my docker-compose.yml |
fwiw I solved this in my own case by replacing |
Unfortunately, I'm still facing this issue. I tried setting the proxy headers and forwarded-allow-ips in the entrypoint: but unfortunately, while everything is sent, received over https, the URL's produced by Have I missed some traefik required setting or something else ? |
Same here. I can see Am I missing something? Also, I share the same view as others in this thread. |
@wookiesh, @dnshio, you could try this: As parameter for My reasoning for why this may work: The single quotes |
@tiangolo I am using gunicorn with uvicorn workers and the above mentioned solution is not working for me. However, I have achieved this by setting up a middleware and changing the scheme value from
Please advice if this modification could potentially impact other parts of the application that rely on the original |
This worked for me! Thank you, @Chris927! |
FastAPI will use http URL when url_for is used when it is behind a proxy. There are some tricks to pass the headers to FastAPI, but as simple solution, just use relative url. See encode/starlette#538 Change-Id: Ifb10fde3ac3b3050750b911a4cda54bc73191e3a
Worked for me, too! Thanks! |
Resolving the FastAPI with Jinja2 Integration IssueUnderstanding the ProblemWhen you don't specify the following settings in your FastAPI application:
The application will redirect the URL to Resolving the IssueTo fix this issue, you need to make the following changes:
Here's an example of the custom Uvicorn worker implementation: # custom_worker.py
from uvicorn.workers import UvicornWorker
class CustomUvicornWorker(UvicornWorker):
CONFIG_KWARGS = {
"loop": "auto", # Use 'auto' to automatically choose the best loop, 'uvloop' can be specified for performance.
"http": "auto", # Use 'auto' to automatically choose the best HTTP protocol support, 'httptools' can be specified.
"lifespan": "on", # 'on' to enable lifespan support.
"proxy_headers": True, # Corresponding to `--proxy-headers`
"forwarded_allow_ips": "*", # Corresponding to `--forwarded-allow-ips=*`
}
Here's an example Systemd service file:
By implementing these changes, your FastAPI application with Jinja2 integration should now work correctly, both locally and when deployed on an Ubuntu server with Nginx. Happy Coding 🤗 |
Currently, the `request.url_for` and `URLPath.make_absolute_url` methods always build URLs with "http" scheme, even when the original requested URL is using "https". The reason for this is that Gunicorn does not allow IPs other than 127.0.0.1 to set secure headers by default. As regular RomM installations don't know which frontend IPs will try to set security headers in advance, we can disable this validation, and fix URL building. A simple way to test this change is to access any of the `feed` endpoints, which generate URLs using the mentioned methods. Accessing the endpoint using "https" scheme must generate "https" URLs. Reference: * encode/starlette#538 (comment) * https://docs.gunicorn.org/en/stable/settings.html#forwarded-allow-ips
Alas this did not help me with an Apache reverse proxy. What helped are those two lines in the virtual host definition, along the ProxyPass:
The latter requires "headers" module enabled. |
I'm looking for a way to produce static file routes with
https
prefixes instead of the defaulthttp
. For example, say I have the following template:I would like it to produce:
I don't find information about this on the documentation, so I was wondering if it was possible? I can't use the
<script src="/static/file.js"></script>
form because I want to use the generated html on a different domain.In case it makes a difference: I'm using fastAPI and the app is running on heroku.
The text was updated successfully, but these errors were encountered: