nginx proxy redirect with fastapi routers - nginx

I am attempting to develop a web app with nginx and fastapi and react. These are all new to me.
Regarding the fastapi, I have a main FastAPI that has a login route. I have added additional routes with APIRouter class and FastAPI.include_router method. I refered to this documentation
Development setup
fastapi runs on port 8000 with uvicorn
react frontend runs on port 5000 with npm start
axios base url is localhost:8000
Everything works as expected and the axios calls return correct response.
Production setup
fastapi runs on port 80 with gunicorn
frontend built with npm build
axios base url is localhost
all request handled with nginx in frontend
I based the following nginx conf file on this documentation
# conf file for server
# proxy pass for api requests
upstream api {
server backend;
}
# main server for app
server {
listen 80;
server_name localhost;
access_log /var/log/nginx/frontend;
error_log /var/log/nginx/frontend;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $server_name;
proxy_redirect off;
proxy_pass http://api;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
Note that I am using docker compose and have a backend service and a frontend service.
With this configuration the login route works correctly, but the routes added via FastAPI.include_router return HTTP 307 redirect and the response header shows requested url as http://api/api/path/to/route instead of http://localhost/api/path/to/route. So it appears that nginx successfully proxy passes requests for a route directly in the main fastapi app module but redirects to the wrong url for requests to routes added with app.include_router. According to this reference, I should have proxy_redirect off; in the /api location block, but if I remove this line, then nginx redirects the request and returns the correct response.
question
why is nginx redirecting the request even when I include proxy_redirect off; in the conf file.
why does the redirect not return the correct response unless I remove proxy_redirect off;

Related

NGINX Forward HTTPS request to HTTP Backend Server and Back

On a single server instance, I have an NGINX web server that operates without any problems with the HTTPS and I have a backend server in Spring Boot running on port 8080. I do not want to open this port to the internet, therefore I would like to setup a reverse proxy with NGINX to forward the request that start with /api to my backend and return the response.
When I send request to the domain in the browser, my frontend application which runs in browser, sends some requests to my backend (starting with /api), my frontend uses the following base url:
http://my-ip:8080/api
And the nginx configuration is as follows:
server {
listen 80;
ssl_certificate /cert/cert.pem;
ssl_certificate_key /cert/privkey.pem;
server_name www.mydomain.com;
rewrite ^(.*) https://$server_name$1 permanent;
}
server {
listen 443 ssl;
server_name www.mydomain.com mydomain.com;
ssl_certificate /cert/cert.pem;
ssl_certificate_key /cert/privkey.pem;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 404 /index.html;
location = / {
root /usr/share/nginx/html;
internal;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location /api {
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;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:8080;
}
}
I keep getting Mixed Content Error and my backend requests are being blocked by the browser since my Frontend uses http for the request.
If I try to use https in the Frontend URL, such as:
https://my-ip:8080/api
Then I get a different error:
GET https://my-ip/api/... net::ERR_CERT_COMMON_NAME_INVALID
This is probably because my certificate is generated for my domain name and not for the IP.
Solution: In the frontend, we should send the request to https version and use the actual domain instead of the ip address because the domain name should match the domain of the certification.
The request: https://my-domain:8080/api
Then the nginx forwards this request properly.

Nginx does not display custom 404 page with Gunicorn + Flask

I'm trying to display a custom 404 error page on my site, but it seems that Nginx does not catch the 404. Instead, it shows a 404 page from Gunicorn(flask app)
nginx.conf:
server {
listen 80 default_server;
server_name example.com www.example.com;
location / {
proxy_pass http://flask:8080/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
error_page 404 /notfound.html;
location /notfound.html {
root /var/www/html;
}
error_page 500 502 503 504 /maintenance.html;
location /maintenance.html {
root /var/www/html;
}
}
Both flask app and Nginx are running on separate docker containers.
I purposefully brought the flask app down to see what happens, after a
while It redirects to 504(Timeout) and the maintenance.html appeared. - works fine.
But on a 404 error instead of displaying this custom notfound.html, it displays a 404 Not Found from Flask App (The 404 page did not had any nginx version on it).
proxy_intercept_errors on; should solve this problem.

flask oidc auth callback redirects to http instead of https

This is my first attempt to deploy a plotly dash python web app. I followed below tutorials to get going
digital ocean flask app with gunicorn and nginx
Okta authentication for flask app using OpenIdConnect
The app runs fine on an ec2 instance with nginx and gunicorn all in docker containers. The redirect to okta for authentication and back works fine (using ec2 instance public ip)
After setting redirect for domain name via aws load balancer (HTTPS) it started failing complaining 404 as url scheme returned was http instead of https.
First i added OVERWRITE_REDIRECT_URI config with https which fixed incorrect redirect uri problem on okta side
Then tried ProxyFix and all options in below SO posts but after redirect to /authorization-code/callback?code=<long code value>, the response always comes back with http://<my_website_name>/<page> instead of https
Make Flask's url_for use the 'https' scheme in an AWS load balancer without messing with SSLify
X-Forwarded-Proto and Flask
I'm stuck at this point, what am i missing here?
Thanks
nginx conf.d/conf
upstream app_server {
server dash:8050;
}
server {
listen 80;
server_name _;
location / {
try_files $uri #proxy_to_app;
}
location #proxy_to_app {
gzip_static on;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_buffering off;
proxy_redirect off;
proxy_pass http://app_server;
}
}

nginx proxied redirects use the port number of the proxy, not the host

I'm setting up a web/app/db stack, and the nginx proxy configuration isn't working the way I thought it would.
so here is an example of the stack...the url of the application is:
https://testapp.com
here is the nginx config:
server {
listen 8886;
server_name _;
root /usr/share/nginx/html;
include /etc/nginx/default.d/*.conf;
#ELB
if ($http_user_agent = 'ELB-HealthChecker/2.0') {
return 200 working;
}
#HTTP to HTTPS
if ($http_x_forwarded_proto != 'https') {
return 301 https://$host$request_uri;
}
location / {
set $proxy_upstream_name "testapp.com";
port_in_redirect off;
proxy_pass http://internal-alb.amazonaws.com:8083/;
proxy_redirect off;
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-Host $server_name;
proxy_set_header Access-Control-Allow-Origin $http_origin;}
The app is proxied to an internal AWS alb, and it forwards it to a single (at this point) application server.
I'm able to get the site to serve. However, the application creates a redirect on login, and I get the following response.
Request URL:https://testapp.com/login
Request Method:POST
Status Code:302
Remote Address:34.192.444.29:443
Referrer Policy:no-referrer-when-downgrade
Response Headers
content-language:en-US
content-length:0
date:Mon, 11 Sep 2017 18:35:34 GMT
location:http://testapp.com:8083/testCode
server:openresty/1.11.2.5
status:302
The redirect fails because it's being served on 443, not 8083.
For some reason the app or the proxy isn't updating the port as it doing it's reverse proxy thing, so that the redirect has the proxied port NOT the actual application port 443.
What do I need to do with nginx config to get it to redirect correctly.
thanks.
myles.
The normal behaviour of the nginx is to rewrite the upstream address to the address the page was served from. It looks like instead of using your upstream address (http://internal-alb.amazonaws.com:8083/), your app is responding using a mixture of the two (http://testapp.com:8083). You can either change the app behaviour, or, to fix it at the nginx level, can use the proxy_redirect directive.
I'm reasonably sure the directive to fix this is proxy_redirect http://testapp.com:8083/ https://testapp.com/;

Nginx reverse folder location to external website

As a Nginx newbie i am trying to get a reverse proxy working to an external domain. Later on I will need to port to an internal domain. When trying to reverse proxy to an external domain i seem to hit a wall and the response is a 404 cannot be found.
The goal is when i try to access http://localhost/example the reverse proxy serves www.example.com.
This is my config:
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /example/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://www.example.com/;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
Any hint what i am doing wrong?
When speaking about http, webserver detects which of configured servers was requested by checking Host header. In you case, you tell nginx to proxy requests to another server, but instruct it to pass original Host header to that server. Obviously, there is no configuration on remote server, which could accept request with that domain. So it responds you with 404.
To make it work, just change header to proxy_set_header Host www.example.com; where instead of www.example.com you should use same host as in proxy_pass directive. (or same variable)

Resources