How to Access FastAPI SwaggerUI Docs Behind an NGINX proxy? - nginx

Hope you can help me, here is the issue I have:
Both of my frontend and backend servers runs on the same AWS EC2 instance. Because of this I have created a NGINX config like this:
server {
server_name NAME;
listen 80 default_server;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
}
location /api/ {
proxy_pass http://127.0.0.1:8000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_http_version 1.1;
}
}
So any request to the "http://public_ip/api/" routed to the FastAPI backend server while every other request to another endpoint routed to the frontend SPA.
This works quite good mostly. However there is an issue if I try to access FastAPI "/api/docs" or "/api/redoc" routes. When I call the "/api/docs" endpoint for instance, there is a request to the "http://public_ip/openapi.json" address. And this isn't an endpoint starting with "/api" obviously. So NGINX blocks it and raises a bad request.
https://fastapi.tiangolo.com/advanced/behind-a-proxy/#about-proxies-with-a-stripped-path-prefix
I found this guide but it seems like this isn't related to my problem at all. At least I understand it that way.
Any help is appreciated. Thanks in advance.

Passing 'openapi_url' argument to the FastAPI() seems like good solution. Passed openapi_url= '/api/openapi.json' and it's fixed for both docs and redoc. Any other/better solution to handle all redirects that may occur is appreciated.
api = FastAPI(title="API_NAME",
description="API_DESC",
version="0.2.0",
docs_url='/api/docs',
redoc_url='/api/redoc',
openapi_url='/api/openapi.json')

Related

How to configure Nginx so the proxy_pass also aply to in-site requests?

So let me introduce you to my context: I have a several containers deployed and I'd like to use nginx as reverse proxy so I can target my other containers (which are API's) through something like this: https://example.mysite.com/services/whatever_the_api.
I managed to get to a point where the proxy_pass works like a charm but now I have a problem: when my API admin page loads, it tries to find the assets like this : https://example.mysite.com/assets/... ditching the /services/api/ part.
Here is my /etc/nginx/config.d/sample.conf file :
server {
listen myhost:80;
server_name myhost;
location ^~ /services/apiName/ {
proxy_pass http://myapp:1338/;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
If anyone knows what is the configuration material I'm missing, I'll be grateful to hear about it.

Wildfly request.getRemoteUser() returns null when accessed through nginx basic authentication

I have Wildfly 24 behind a nginx webserver, acting as a reverse proxy with Basic Authentication. When I access my services with Insomnia I can send a POST (and that works) but the problem is that request.getRemoteUser() returns NULL, instead of the authenticated user from Basic Authentication, and I need that value in my application.
This is my nginx configuration
location / {
auth_basic "Application auth";
auth_basic_user_file /etc/nginx/htpasswd;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://wildfly/;
include snippets/services.conf;
return 404;
}
where snippets/services.conf contains just a lot of rewrites for legacy reasons.
I have experimented with adding proxy_set_header Authorization $http_authorization; and proxy_pass_header Authorization; but that has so far not yielded any results. Any ideas what I am missing?
It is possible to add a request logger to Wildfly and thus log requests with headers directly into server.log.
Create a custom filter for Undertow, using the RequestDumpingHandler
Add this filter to the default server
Start the CLI (bin/jboss-cli.sh -c) and enter the following commands
/subsystem=undertow/configuration=filter/custom-filter=request-logger:add(module=io.undertow.core, class-name=io.undertow.server.handlers.RequestDumpingHandler)
/subsystem=undertow/server=default-server/host=default-host/filter-ref=request-logger:add()
reload
Remove the logger with these commands:
/subsystem=undertow/server=default-server/host=default-host/filter-ref=request-logger:remove
/subsystem=undertow/configuration=filter/custom-filter=request-logger:remove
reload

Nginx reverse proxy root URI issue

I have an application running in Kubernetes with the following topology:
Some-ingress-controller--> nginx reverse proxy -->dynamically generated services.
I have set the NGINX reverse proxy with the following test configuration
location /mysite1/ {
proxy_set_header Host $host;
proxy_set_header Referer $http_referer;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto http;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $remote_addr;
proxy_pass http://myservice1.default.svc:9000/;
}
So far everything works fine - when I go to my website http://example.com/mysite1/ I see what I expect from the myservice1 application hosted at http://myservice1.default.svc:9000/. However, the application myservice1 issues requests to various internal (internal meaning they are part of the same container) resources on /get_resourceX. When the myservice1 application tries to access these resources they will be accessed at http://example.com/get_resourceX/ and not at http://example.com/mysite1/get_resourceX as they should - and that is my problem.
What could work is to simply reverse proxy all the relevant resource names as well. However, then I would need to do the same for http://example.com/mysite2, http://example.com/mysite3 etc. which is impractical since these are generated dynamically.
Another possible solution is to check the http Referrer header and see whether it originates from mysite1 - but that seems awfully hackish.
How can I easily have myservice1 requests issued to /get_resourceX served by itself? Is there a generic way to set the root path for the myservice1 application to myservice1?

Combination of using nginx as a reverse proxy with keycloak as upstream server fails

We are nginx newbies and trying to replace httpd with it.
We have the following nginx configuration:
location /auth {
proxy_pass http://keycloak_server$request_uri;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
This works in providing access to the administrator portal. However we use also keycloak for authentication for our applications, and the problem is that keycloak responds with a 302 redirect however nginx treats it as a 502 bad gateway error.
The apache httpd works without any problems.
What are we doing wrong ? Any pointers or specific configuration guidance would be appreciated.
The issue was resolved. It was because the upstream was sending too big a header. Modifying the buffer size for proxy worked.

Change Host header in nginx reverse proxy

I am running nginx as reverse proxy for the site example.com to loadbalance a ruby application running in backend server. I have the following proxy_set_header field in nginx which will pass host headers to backend ruby. This is required by ruby app to identify the subdomain names.
location / {
proxy_pass http://rubyapp.com;
proxy_set_header Host $http_host;
}
Now I want to create an alias beta.example.com, but the host header passed to backend should still be www.example.com otherwise the ruby application will reject the requests. So I want something similar to below inside location directive.
if ($http_host = "beta.example.com") {
proxy_pass http://rubyapp.com;
proxy_set_header Host www.example.com;
}
What is the best way to do this?
You cannot use proxy_pass in if block, so I suggest to do something like this before setting proxy header:
set $my_host $http_host;
if ($http_host = "beta.example.com") {
set $my_host "www.example.com";
}
And now you can just use proxy_pass and proxy_set_header without if block:
location / {
proxy_pass http://rubyapp.com;
proxy_set_header Host $my_host;
}
map is better than set + if.
map $http_host $served_host {
default $http_host;
beta.example.com www.example.com;
}
server {
[...]
location / {
proxy_pass http://rubyapp.com;
proxy_set_header Host $served_host;
}
}
I was trying to solve the same situation, but with uwsgi_pass.
After some research, I figured out that, in this scenario, it's required to:
uwsgi_param HTTP_HOST $my_host;
Hope it helps someone else.
Just a small tip. Sometimes you may need to use X-Forwarded-Host instead of Host header. That was my case where Host header worked but only for standard HTTP port 80. If the app was exposed on non-standard port, then this port was lost when the app generated redirects. So finally what worked for me was:
proxy_set_header X-Forwarded-Host $http_host;

Resources