skip to Main Content

I have a small, test FastAPI web application that is serving a simple HTML page that requires a css style sheet located in the static folder. It is installed on a Linode server (Ubuntu 20.04 LTS), nginx, gunicorn, uvicorn workers, and supervisorctl. I have added a certificate using certbot.

The application works fine in http but does not access the static files in https. When accessed in http all static-based features work but when accessed with https it lacks all styling from css stylesheet. I need to get this working so I can load a much more complex app that needs css and other static folder-stored features.

The file structure is:

/home/<user_name>/application
- main.py
- static
   |_ css
   |_ bootstrap
- templates
   |_ index.html

main.py:

import fastapi
import uvicorn
from fastapi import Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates


api = fastapi.FastAPI()

api.mount('/static', StaticFiles(directory='static'), name='static')
templates = Jinja2Templates(directory="templates")


@api.get('/')
@api.get('/index', response_class=HTMLResponse)
def index(request: Request):
    message = None
    return templates.TemplateResponse("index.html", {"request": request,
        'message': message})


if __name__ == '__main__':
    uvicorn.run(api, port=8000, host='127.0.0.1')

nginx is at /etc/nginx/sites-enabled/<my_url>.nginx

server {
    listen 80;
    server_name www.<my_url>.com <my_url>.com;
    server_tokens off;
    charset utf-8;

    location / {
        try_files $uri @yourapplication;
    }

    location /static {
        gzip            on;
        gzip_buffers    8 256k;

        alias /home/<user_name>/application/static;
        expires 365d;
    }


    location @yourapplication {
        gzip            on;
        gzip_buffers    8 256k;

        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Protocol $scheme;
    }
  }

server {
    listen              443 ssl;
    server_name         www.<my_url>.com;
    ssl_certificate /etc/letsencrypt/live/<my_url>.com/fullchain.pem; # mana>
    ssl_certificate_key /etc/letsencrypt/live/<my_url>.com/privkey.pem; # ma>
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    location / {
        try_files $uri @yourapplication;
    }

  location /static {
        gzip            on;
        gzip_buffers    8 256k;

        alias /home/<user_name>/application/static;
        expires 365d;
    }

    location @yourapplication {
        gzip            on;
        gzip_buffers    8 256k;

        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Protocol $scheme;
    }
}

and am serving using supervisor script:

[program:api]
directory=/home/<user_name>/application
command=gunicorn -b 127.0.0.1:8000 -w 4 -k uvicorn.workers.UvicornWorker main:api
environmentenvironment=PYTHONPATH=1
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stderr_logfile=/var/log/app/app.err.log
stdout_logfile=/var/log/app/app.out.log

The css stylesheet is called in the html using url_for like this:

<link href="{{ url_for('static', path='/css/ATB_style.css') }}" rel="stylesheet">

I have tried a whole host of modifications to the location /static block in nginx including:

  • adding slash after static in either line or both
  • trying to add https://static or https://www.<my_url>.com/home/<my_url>/application/static
  • adding and removing the location static from the http and https lines
  • changing proxy_pass to https://127.0.0.1:8000;
  • added root /home/<user_name>/application to the server section

I have loaded this server twice, once letting certbot modify the nginx file the second, and current configuration, where I did it manually. I am at a complete loss on what to do.

3

Answers


  1. Chosen as BEST ANSWER

    Thanks to @AdramKhan for the comment that provided a work-around for an important demo. I added a meta line to my html page to allow access to the css stylesheet with https:

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

    This was only a work-around as it is dealing with a hard-coded HTTP request in the code somewhere per this: How can I allow Mixed contents (http with https) using content-security-policy meta tag?

    Solving the root cause was changing how static content was called in the head of html files. The problems (there were three) were with references like this where there was a jinja2 url_for instead of a direct href:

    <link href="{{ url_for('static', path='/css/MH_style.css') }}" rel="stylesheet">
    

    When replaced with a reference of this format, using href:

    <link rel="stylesheet" href="/static/css/MH_style.css"/>
    

    Everything worked without the Content-Security-Policy meta.


  2. I’d think you want the server to handle it. If you just setup a separate block on port 80 to convert all requests to 443 (HTTPS) permanently, you’d be good:

    server {
        listen 80;
        server_name yourserver.com;
        return 301 https://yourserver.com$request_uri;
    }
    
    server {
        listen 443 ssl http2;
        server_name yourserver.com;
        ...
    }```
    
    Login or Signup to reply.
  3. In case of using upstream, proxy_pass should also be set as follow:

    http {
        upstream myapp {
            server application_container:4001;
        }
    
        server {
    
            listen 80;
            server_name localhost;
    
            location / {
                ...
            }
    
            location /myapp/ {
                    proxy_pass http://myapp;
                    ...
            }
    
            location /static/ {
                proxy_pass http://myapp;
                alias ...
            }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search