带有 nginx 的 FastAPI 不提供 HTTPS 中的静态文件

问题描述 投票:0回答:4

我有一个小型的测试 FastAPI Web 应用程序,它提供一个简单的 HTML 页面,该页面需要位于静态文件夹中的 css 样式表。 它安装在 Linode 服务器(Ubuntu 20.04 LTS)、nginx、gunicorn、uvicorn 工作人员和supervisorctl 上。 我已经使用 certbot 添加了证书。

应用程序在 http 中工作正常,但无法访问 https 中的静态文件。 当通过 http 访问时,所有基于静态的功能都可以工作,但是当通过 https 访问时,它缺少 css 样式表中的所有样式。 我需要让它工作,这样我就可以加载一个需要 css 和其他静态文件夹存储功能的更复杂的应用程序。

文件结构为:

/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 位于 /etc/nginx/sites-enabled/.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;
    }
}

并且正在使用主管脚本提供服务:

[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

在 html 中使用 url_for 调用 css 样式表,如下所示:

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

我尝试了对 nginx 中的 location /static 块进行大量修改,包括:

  • 在任一行或两行中的 static 之后添加斜杠
  • 尝试添加 https://static 或 https://www..com/home//application/static
  • 在 http 和 https 行中添加和删除静态位置
  • 将 proxy_pass 更改为 https://127.0.0.1:8000;
  • 将 root /home//application 添加到服务器部分

我已经加载了这个服务器两次,一次让 certbot 修改 nginx 文件,第二次是当前配置,我手动完成的。 我完全不知道该怎么办。

nginx https fastapi uvicorn
4个回答
7
投票

感谢@AdramKhan 的评论,为重要的演示提供了解决方法。 我在 html 页面中添加了一条元行,以允许使用 https:

访问 css 样式表
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">

这只是一个解决方法,因为它正在处理代码中某处的硬编码 HTTP 请求:如何使用内容安全策略元标记允许混合内容(http 与 https)?

解决根本原因是改变 html 文件头中静态内容的调用方式。 问题(共有三个)是这样的引用,其中有 jinja2 url_for 而不是直接的 href:

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

当替换为这种格式的引用时,使用href:

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

没有内容安全策略元,一切都可以正常工作。


0
投票

我想你希望服务器来处理它。如果您只是在端口 80 上设置一个单独的块以将所有请求永久转换为 443 (HTTPS),那就太好了:

server {
    listen 80;
    server_name yourserver.com;
    return 301 https://yourserver.com$request_uri;
}

server {
    listen 443 ssl http2;
    server_name yourserver.com;
    ...
}```

0
投票

如果使用

upstream
proxy_pass
还应设置如下:

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 ...
        }
}

0
投票

有点晚了,但我今天遇到了这个问题并修复了。我使用 nginx 作为查询我的服务器的反向代理。我最初遵循 Brad Allen 的答案,但是虽然这在我的生产服务器上有效,但它停止在本地主机上运行(因为它发送的是 HTTP 请求,而不是 HTTPS)。我在文档中看到我可以更新响应标头,而不是将标签添加到 HTML 中:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy .

我更改了生产服务器上的 nginx 配置,以便在将响应发送回用户之前向响应添加标头。

add_header Content-Security-Policy upgrade-insecure-requests;

这解决了我的问题。

我折腾了几个选项并最终决定了这个,但如果它导致问题,那么我会尝试使用环境变量“HTTPS_REQUESTS”,我在使用 Jinja2 生成 HTML 时访问该环境变量。

© www.soinside.com 2019 - 2024. All rights reserved.