英文:
FastApi (Starlette) + NGINX Proxy: URL scheme in Request Object not correct?
问题
- Starlette 在 Request 对象中如何设置 "url" 属性,特别是在 NGINX 代理后面运行时?
- 如何在 NGINX 层面上操作
request.url
,设置代理头不会修改它吗? - 如何通过 Starlette 中间件操作
request.url
,我做的方式似乎没有任何效果?
设置如下:
我有一个 FastAPI 应用程序在 NGINX 代理后运行。通过浏览器,我通过 HTTPS 发送请求到 NGINX (https://www.example.com
),但无论我做什么,Starlette 侧的 request.url
总是 http://www.example.com
。
我创建了一个小的 FastAPI 端点以进行演示
@app.get("/test")
def some_test(request: Request):
return {"request.url": request.url,
"request['headers']": request["headers"],
"request.client": request.client}
在下面的截图中,我展示了我得到的内容(域名已匿名化; 设置为 xyz
):
- 我在浏览器中调用
https://www.example.com/test
- 在控制台中,我可以看到请求正确发送到
https://www.example.com/test
- 但是当查看 Starlette 的
Request.url
时,它显示为http://www.example.com/test
这种行为是否正确?在截图中,我还打印了 host
、x-forwarded-proto
和 x-forwarded-schema
,我将它们设置为 NGINX 配置的 HTTPS,但是 Request
对象并没有受到任何修改。
我应该在 NGINX 或 FastAPI/Starlette 层面上采取什么措施来纠正这个问题?
- 我已经尝试了设置各种命令,比如
proxy_set_header Upgrade $http_upgrade; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Scheme $scheme;
但是都没有成功。
- 我尝试编写了一个自定义的 Starlette 中间件(我知道这样做有点不雅观,但我想尝试一下,以更好地理解发生了什么)
from fastapi import Request from starlette.datastructures import URL @app.middleware("http") async def some_middleware(request: Request, call_next): if "/test" in str(request.url): print(f"before {request.url}") request._url = URL(str(request.url).replace("http", "https")) print(f"after {request.url}") response = await call_next(request) return response
但是这也没有修改响应中显示的
request.url
。
英文:
I'd like to know the following
- How does Starlette set the "url" property in the Request object - especially when operated behind a NGINX proxy.
- How can
request.url
be manipulated on the NGINX level -- setting proxy headers doesn't modify it at all? - How can
request.url
be manipulated via a Starlette Middleware -- somehow what I did doesn't have any effect?
The setting is like this:
I have a FastAPI app running behind a NGINX proxy. Via the browser, I send requests to NGINX via HTTPS (https://www.example.com
) , but whatever I do, request.url
on Starlette's side is always http://www.example.com
.
I made a small FastAPI endpoint for demonstration purposes
@app.get("/test")
def some_test(request: Request):
return {"request.url": request.url,
"request['headers']": request["headers"],
"request.client": request.client}
and in the following screenshots I show what I get (domain is anonymized; set to xyz
):
- I call in the browser
https://www.example.com/test
- In the console, I see that properly the request is issues to
https://www.example.com/test
- But when looking at Starlette's
Request.url
, it sayshttp://www.example.com/test
Is this behaviour how it should be? In the screenshot I also print the host
and x-forwarded-proto
and x-forwarded-schema
which I set the NGINX config to HTTPS, but the object Request
isn't modified at all by that.
What can I do to correct this on the NGINX or FastAPI/Starlette level?
- I already tried setting various commands like
proxy_set_header Upgrade $http_upgrade; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Scheme $scheme;
with no success at all.
- I tried to write a custom Starlette Middleware (I know that's quite ugly, but I wanted to try this out to better understand, what's going on)
from fastapi import Request from starlette.datastructures import URL @app.middleware("http") async def some_middlware(request: Request, call_next): if "/test" in str(request.url): print(f"before {request.url}") request._url = URL(str(request.url).replace("http", "https")) print(f"after {request.url}") response = await call_next(request) return response
but this also doesn't modify the
request.url
shown in the response.
答案1
得分: 0
现在模式的代理工作了。
问题是:
在我的Dockerfile之前看起来像这样:
ENTRYPOINT ["uvicorn", "main:app", "--proxy-headers", "--forwarded-allow-ips='*'", "--host", "0.0.0.0"]
--forwarded-allow-ips
参数中的引号不起作用。相反,入口点应该是:
ENTRYPOINT ["uvicorn", "main:app", "--proxy-headers", "--forwarded-allow-ips=* ", "--host", "0.0.0.0"]
提示:为了更清晰一些,不应该将*
作为IP地址允许,而是应该指定IP地址。
英文:
Now the proxying of the schema works.
The problem was,the following:
Before my Dockerfile looked like
ENTRYPOINT ["uvicorn", "main:app", "--proxy-headers", "--forwarded-allow-ips='*'", "--host", "0.0.0.0"]
The quotation marks in the --forwarded-allow-ips
parameter do not work. Instead the entrypoint should read
ENTRYPOINT ["uvicorn", "main:app", "--proxy-headers", "--forwarded-allow-ips=*", "--host", "0.0.0.0"]
Hint: To be a bit cleaner, one should not allow *
as an IP address, instead you should specify the IP address.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论