为什么 Flask 博客上线后最容易翻车
本地能跑,不代表适合生产。很多个人站点直接用 flask run 顶在线上,SSH 一断或机器一重启,服务就没了。还有一种更隐蔽:首页能开,但后台登录跳转成 http、日志里拿不到真实 IP、站点地图链接不对,根因往往是反向代理头没配好。
对个人博客来说,先把最小可用链路搭稳最重要:Gunicorn 跑 Flask,Nginx 接公网请求和静态文件,systemd 负责自启。这样后面再加 HTTPS、SEO 和自动发布,维护成本才低。
一套够用的部署结构
建议目录类似这样:
/var/www/flask-blog/
app.py
config.py
requirements.txt
database.db
static/
templates/
venv/
Gunicorn 只监听本机,例如 127.0.0.1:8000,公网流量全部先走 Nginx,再转发给 Gunicorn。这样静态文件可以由 Nginx 直接返回,应用本身也不用暴露公网端口。
第一步:先在虚拟环境里把 Gunicorn 跑起来
cd /var/www/flask-blog
python3 -m venv venv
source venv/bin/activate
pip install -U pip
pip install -r requirements.txt
pip install gunicorn
这个项目入口在 app.py,可以直接试跑:
gunicorn --bind 127.0.0.1:8000 --workers 2 --threads 4 app:app
个人博客通常不需要太多 worker。1 核 2G 小机器先用 2 个 worker 起步更稳,开太大反而更容易吃掉内存。这里如果都启动不了,优先查虚拟环境、依赖和环境变量。
第二步:把代理头和 ProxyFix 配对
Flask 在代理后面运行时,必须正确识别真实协议、主机名和来源地址。这个项目已经在 app.py 里接了 ProxyFix,config.py 里也有这些开关:
BEHIND_PROXY = True
PROXY_FIX_X_FOR = 1
PROXY_FIX_X_PROTO = 1
PROXY_FIX_X_HOST = 1
Nginx 这一侧至少要补齐:
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-Proto $scheme;
最容易踩坑的是没传 X-Forwarded-Proto。Nginx 明明已经做了 HTTPS 终止,但 Flask 仍把请求当成 http,后台回跳、绝对链接、canonical URL、sitemap 都可能跟着错。另一个坑是 ProxyFix 的数字别乱写。只有一层可信 Nginx 就填 1;如果前面还有 CDN 或负载均衡,要按真实层数算。
第三步:用 systemd 托管 Gunicorn
生产环境不要靠手工开进程,应该交给 systemd:
[Unit]
Description=Flask Blog Gunicorn Service
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/flask-blog
Environment="FLASK_ENV=production"
Environment="BEHIND_PROXY=true"
Environment="SESSION_COOKIE_SECURE=true"
ExecStart=/var/www/flask-blog/venv/bin/gunicorn --workers 2 --threads 4 --bind 127.0.0.1:8000 app:app
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
保存为 /etc/systemd/system/flask-blog.service 后执行:
sudo systemctl daemon-reload
sudo systemctl enable flask-blog
sudo systemctl start flask-blog
sudo systemctl status flask-blog
如果服务没起来,直接看:
sudo journalctl -u flask-blog -n 100 --no-pager
常见问题就是工作目录写错、虚拟环境路径不对、数据库权限不足。
第四步:让 Nginx 处理公网入口和静态文件
server {
listen 80;
server_name example.com www.example.com;
location /static/ {
alias /var/www/flask-blog/static/;
expires 7d;
add_header Cache-Control "public, max-age=604800";
}
location / {
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-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60;
}
}
这里重点看两件事。第一,alias 路径必须是真实绝对路径,且 Nginx 用户能读到;很多“样式丢失”其实只是静态目录权限错了。第二,改完配置一定先执行:
sudo nginx -t
sudo systemctl reload nginx
第五步:再补 HTTPS 和数据库备份
站点能稳定响应后,下一步就该启 HTTPS,并把 SESSION_COOKIE_SECURE=true 固定下来。数据库方面,小流量博客继续用 SQLite 完全没问题,但必须定时备份。这个仓库里已经有 scripts/backup_sqlite.py,可以配合 cron 跑;等后台编辑、评论和自动发布量明显上来,再考虑迁到 MySQL。
常见避坑点
1. 用 flask run 顶生产
开发服务器不是给生产用的。
2. 反代能开首页就以为配置对了
真正容易错的是登录、跳转、绝对链接和 sitemap,这些都依赖正确的代理头。
3. 服务用户和文件权限混乱
Gunicorn 如果用 www-data 跑,那么项目目录、SQLite 文件、上传目录也要给到对应权限,否则发文或上传时很容易报 500。
总结
个人 Flask 博客的生产部署,核心不是“技术栈多新”,而是底座是否稳定。Gunicorn 本机监听、systemd 托管进程、Nginx 正确反代、HTTPS 和代理头配齐,这四件事做对了,站点可维护性就会明显提升。底层稳定以后,再做 SEO、自动化发布和内容运营,才不会反复被基础设施问题拖住。