• 使反向代理后的 Flask 的 url_for() 使用 https

    这个问题困扰我多时,曾尝试多个方案未果,今天终于搜到了正确的解决方法,记录下来。

    我的网络拓扑: 外部 → docker的nginx → docker的gunicorn → flask

    问题

    Flask 代码中的 redirect(url_for('login')) 会跳回 http 的登录页面。

    我也不想给每个 url_for() 添加强制 https 的参数。

    解决

    from werkzeug.middleware.proxy_fix import ProxyFix
    from flask import Flask
    
    app = Flask(__name__)
    app.wsgi_app = ProxyFix(app.wsgi_app)

    相关参考

  • 使用 Nginx+Gunicorn 部署 Flask,with venv+systemd

    记录一下我的部署过程。

    Flask

    文件为 /root/myproject/application.py,其中的 Flask 实例为

    app = Flask(__name__)

    Gunicorn

    /root/myproject/ 中新建一个虚拟环境 venv 并激活虚拟环境,使用 pip 安装 Flask 等模块。然后安装 gunicorn:

    pip install gunicorn

    装好之后,执行命令:

    gunicorn --bind 127.0.0.1:8000 application:app   # application为文件名 app为实例名

    http://127.0.0.1:8000 应该是可以访问的。(服务器可能需要做一下端口转发,不然就绑定 0.0.0.0)

    Systemd

    我希望服务器重启后,也可以自动启动 web server。

    新建 /usr/lib/systemd/system/gunicorn.service,内容如下:

    [Unit]
    Description=gunicorn daemon
    After=network.target
    
    [Service]
    WorkingDirectory=/root/myproject
    ExecStart=/root/myproject/venv/bin/gunicorn -w 1 --bind 127.0.0.1:8000 application:app
    PrivateTmp=true
    Environment=key=value
    
    [Install]
    WantedBy=multi-user.target

    然后执行 systemctl enable gunicorn,重启一下服务器,之后执行 systemctl status gunicorn 确认服务正常启动。这里备注一下“Environment=key=value”这一行,systemd 启动的服务是不带环境变量的,被这个坑了好久🤣。

    Nginx

    最后,我使用 nginx 进行转发,和实现 https 访问。修改 /etc/nginx/conf.d/default.conf

    server {
        listen       443 ssl;
        server_name  myproject;
    
        access_log  /var/log/nginx/access.log;
        error_log   /var/log/nginx/error.log;
    
        location / {
            proxy_pass http://127.0.0.1:8000;
            proxy_redirect     off;
            proxy_set_header   Host                $host:$server_port;
            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   $scheme;
         }
    
        ssl_certificate     /path/yourssl.cer;
        ssl_certificate_key /path/yourssl.key;
        ssl_session_timeout  5m;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        ssl_protocols TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
    }

    最后,测试一下 https://server_ip 看看能不能访问。