【问题标题】:FastAPI with nginx does not serve static files in HTTPS带有 nginx 的 FastAPI 不在 HTTPS 中提供静态文件
【发布时间】:2021-12-03 16:15:53
【问题描述】:

我有一个小型的测试 FastAPI Web 应用程序,它提供一个简单的 HTML 页面,该页面需要位于静态文件夹中的 css 样式表。它安装在 Linode 服务器(Ubuntu 20.04 LTS)、nginx、gunicorn、uvicorn workers 和 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

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

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

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

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

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

【问题讨论】:

  • 如果您访问 HTTPS URL,浏览器将阻止 HTTP 请求加载的资源。确保您的 img/css/js URL 未使用 http:// 进行硬编码
  • 一切正常。尝试在服务器 https 和 http 中添加尾部斜杠。然后还将根目录更改为您的应用程序目录。只是尝试重新启动服务器。使用 systemctl。请记住,您需要重新启动两者。 Nginx 和 gunicorn。请通过您的 fastapi 应用程序提供文件服务,因为 nginx 在文件共享方面速度很快。如果它仍然没有修复,请在 ping 我之后。
  • 将此添加到您的 html 页面。如果你真的有硬编码 http url 的问题。它将所有http请求重定向到https。
  • @emptyhua,谢谢,我正在使用 url_for,编辑问题以显示实际行。
  • @AkramKhan,谢谢,添加元数据就成功了。哇!

标签: nginx https fastapi uvicorn


【解决方案1】:

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

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

这只是一种解决方法,因为它正在处理代码中某处的硬编码 HTTP 请求:How can I allow Mixed contents (http with https) using content-security-policy meta tag?

解决根本原因是改变在 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"/>

没有 Content-Security-Policy 元数据一切正常。

【讨论】:

    【解决方案2】:

    我认为您希望服务器处理它。如果您只是在端口 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;
        ...
    }```
    

    【讨论】:

    • 将端口 80 更改为重定向,如本答案所示,然后注释掉 html 中的元 http 等效行。重定向效果很好,但再次失去了静态目录访问权限,并且没有使用 css 样式表。添加了与 http 等效的行,它与重定向一起使用。很想弄清楚我与其他人显然有什么不同并导致了这个问题。
    • 这是否与从磁盘(也在静态文件夹中)使用 Bootstrap 4.1 有关?
    • Kenndy,我听从了你的建议,找到了直接的 http 调用。编辑答案以显示结果。
    【解决方案3】:

    如果使用upstreamproxy_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 ...
            }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-10-23
      • 2020-10-08
      • 2020-05-03
      • 2021-12-02
      • 2020-09-14
      • 2019-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多