前面几篇里,SECRET_KEY、数据库地址多半写在 app/__init__.py 里,本地开发很方便。但一旦 部署到服务器、多人协作、把代码推到 Git,硬编码会带来三类问题:
- 密码进仓库 --- 泄露风险
- 开发 / 生产配置混在一起 --- 改一处误伤另一处
- 每台机器配置不同 --- 数据库地址、调试开关没法灵活换
这一篇做一件事:把敏感和易变的配置挪到环境变量,代码只负责「读配置」,不负责「记密码」。
例子仍是通用的 Note 备忘录项目,不涉及任何真实业务。
1. 学完后你能做什么
- 用
os.environ读取配置 - 建
.env文件,本地自动加载(不提交到 Git) SECRET_KEY、DEBUG、数据库 URI 按环境切换- 知道上线时
FLASK_DEBUG=0要注意什么 - 区分 哪些该放环境变量、哪些可以写死在代码里
2. 环境变量是什么
操作系统里的一种「键值对」。程序启动时读取,不改代码就能换配置。
终端里临时设置(只对当前 shell 有效)
export SECRET_KEY="一串随机长字符串"
export FLASK_DEBUG=1
python manage.py
Flask 里读取:
import os
secret = os.environ.get("SECRET_KEY")
debug = os.environ.get("FLASK_DEBUG")
os.environ.get("KEY") --- 没有时返回 None,不会报错。
os.environ["KEY"] --- 没有时直接抛异常,一般 get 更安全。
3. 哪些配置应该外置
| 配置 | 为什么外置 | 示例环境变量 |
|---|---|---|
| Session 签名密钥 | 泄露可伪造登录 | SECRET_KEY |
| 数据库连接 | 每台机器不同 | DATABASE_URL |
| 第三方 API Key | 绝对不进仓库 | SOME_API_KEY |
| 是否调试模式 | 生产必须关 | FLASK_DEBUG |
| 邮件密码 | 敏感 | SMTP_PASSWORD |
可以写死在代码里的:上传目录相对路径、分页默认每页 10 条、业务常量------不含秘密、各环境相同 的值。
4. 改造 app/__init__.py(核心写法)
在第二篇基础上,把硬编码改成读环境变量:
import os
from flask import Flask
def _env_bool(key: str, default: bool) -> bool:
"""把环境变量转成布尔:1 / true / yes / on 视为 True。"""
v = os.environ.get(key)
if v is None or v.strip() == "":
return default
return v.strip().lower() in ("1", "true", "yes", "on")
app = Flask(name)
开发兜底;生产务必在环境里设 SECRET_KEY
_dev_fallback = "dev-only-change-me-in-production"
app.config"SECRET_KEY" = (
(os.environ.get("SECRET_KEY") or "").strip() or _dev_fallback
)
未设置时默认开发调试;生产设 FLASK_DEBUG=0
app.config"DEBUG" = _env_bool("FLASK_DEBUG", True)
app.debug = app.config"DEBUG"
数据库:优先完整 URI,否则拼 SQLite 默认路径
basedir = os.path.abspath(os.path.dirname(file))
db_uri = (os.environ.get("DATABASE_URL") or "").strip()
if not db_uri:
db_path = os.path.join(basedir, "..", "instance", "app.db")
db_uri = "sqlite:///" + db_path
app.config"SQLALCHEMY_DATABASE_URI" = db_uri
app.config"SQLALCHEMY_TRACK_MODIFICATIONS" = False
习惯可以记成:
os.environ.get("KEY") → 有就用
没有 → 给安全的本地默认值(仅开发)
生产 → 必须在环境里显式设置
5. 用 .env 文件,本地不用每次 export
每次开终端都 export 很麻烦。常见做法:项目根目录放 .env,本地开发自动加载。
方式 A:python-dotenv(推荐)
pip install python-dotenv
manage.py 最顶部(在 from app import app 之前):
from dotenv import load_dotenv
load_dotenv() # 读取项目根目录 .env,写入 os.environ
from app import app
方式 B:自己写几行(不装包也行)
def load_local_env():
path = os.path.join(os.path.dirname(file), ".env")
if not os.path.isfile(path):
return
with open(path, encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
key, _, val = line.partition("=")
key, val = key.strip(), val.strip()
if key and key not in os.environ: # 不覆盖已有环境变量
os.environkey = val
原则:shell 里已经 export 的值优先;.env 只补空缺。
6. .env 示例(本地开发)
项目根目录新建 .env(不要提交到 Git):
本地开发配置
SECRET_KEY=local-dev-secret-not-for-production
FLASK_DEBUG=1
SQLite 可不写,走代码里的默认路径
DATABASE_URL=sqlite:///instance/app.db
若改用 MySQL:
DATABASE_URL=mysql+pymysql://root:你的密码@127.0.0.1:3306/notes_db
.gitignore 里加上:
.env
*.env.local
可以提交 .env.example(无真实密码),告诉同事要配哪些项:
SECRET_KEY=请换成随机长字符串
FLASK_DEBUG=1
DATABASE_URL=
7. 生成安全的 SECRET_KEY
不要用 123456 或 dev-secret-key 上线。
python -c "import secrets; print(secrets.token_hex(32))"
输出类似 a3f8c2...,复制到生产环境变量或 .env 的 SECRET_KEY。
换了 SECRET_KEY 以后,已有用户的 Session Cookie 会失效,需要 重新登录------部署前要有心理预期。
8. 开发 vs 生产:怎么区分
不必搞很复杂,入门阶段 靠环境变量切换 就够:
| 场景 | FLASK_DEBUG |
SECRET_KEY |
数据库 |
|---|---|---|---|
本地 .env |
1 |
本地随意(仍别用太简单) | SQLite |
| 生产服务器 | 0 |
随机 64 字符 | MySQL 等 |
生产服务器上(示例):
export SECRET_KEY="生产环境随机密钥"
export FLASK_DEBUG=0
export DATABASE_URL="mysql+pymysql://user:pass@db-host:3306/notes"
export SESSION_COOKIE_SECURE=1 # 仅 HTTPS 站点
用 systemd、Docker、云平台「环境变量面板」设置,效果一样------不写进代码。
9. FLASK_DEBUG=0 上线时要注意什么
① 关掉调试页
DEBUG=True 时,报错会显示 Werkzeug 调试栈,可能暴露代码路径和变量。
生产 必须 FLASK_DEBUG=0。
② 错误页用第八篇的自定义 404 / 500
用户看不到栈信息,只看到友好页面。
③ HTTPS 与 Session Cookie
若站点走 HTTPS,建议:
app.config"SESSION_COOKIE_SECURE" = _env_bool("SESSION_COOKIE_SECURE", False)
app.config"SESSION_COOKIE_HTTPONLY" = True
本地 http://127.0.0.1 不要开 SESSION_COOKIE_SECURE------浏览器不会给非 HTTPS 发带 Secure 的 Cookie,登录会失效。
④ 生产未设 SECRET_KEY 要报警
可在 app/__init__.py 加一段检查:
import warnings
if not app.config"DEBUG" and app.config"SECRET_KEY" == _dev_fallback:
warnings.warn(
"生产环境必须设置环境变量 SECRET_KEY,不要使用代码默认值。",
UserWarning,
)
启动时看到警告,说明配置漏了。
10. 批处理脚本与 Web 共用配置
第十篇、十一篇的 note_service 可能被 脚本 调用(定时清理、导出 CSV)。脚本没有 Flask 请求,但 数据库地址应和 Web 一致。
做法:脚本开头同样 load_dotenv(),再 from app import app, db:
from dotenv import load_dotenv
load_dotenv()
from app import app, db
from app.note_service import list_all_notes
with app.app_context():
page = list_all_notes(per_page=1000)
...
同一套 DATABASE_URL,Web 和脚本不会连到不同库。
11. 流程示意
启动 manage.py
│
▼
load_dotenv() 读 .env(本地)
│
▼
app/init.py 读 os.environ
│
├─ SECRET_KEY → app.config
├─ FLASK_DEBUG → app.config"DEBUG"
└─ DATABASE_URL → SQLALCHEMY_DATABASE_URI
│
▼
Flask 启动,Session / 数据库按当前环境工作
git push(不含 .env)
│
▼
服务器用面板 / export 设生产环境变量
│
▼
同一套代码,不同配置,连生产数据库
12. 新手常踩的 6 个坑
坑 1:.env 提交到 Git
密码进仓库,即使用户删了,历史记录里可能还在。
.gitignore + 只提交 .env.example。
坑 2:生产忘了设 SECRET_KEY
大家共用代码里的 dev 默认值,Session 可被预测伪造。
生产必须单独生成随机密钥。
坑 3:生产 FLASK_DEBUG 仍为 1
报错页暴露内部信息,且性能和安全都有风险。
上线 checklist 第一项:DEBUG=0。
坑 4:本地开了 SESSION_COOKIE_SECURE
HTTPS 本地很少,Cookie 发不出去,表现为「登录立刻掉」。
本地 False,生产 HTTPS 再 True。
坑 5:环境变量名前后不一致
代码读 DATABASE_URL,.env 写 DB_URL------读不到,默默走默认 SQLite。
团队定一张配置表,名字统一。
坑 6:在代码里写真实 API Key「临时一下」
「临时」往往会留很久。
秘密只进环境变量,不进 .py 文件。
13. 实际项目里怎么用
- 敏感项 全部环境变量;代码里只有读取逻辑和开发兜底
- 仓库放
.env.example,新人复制成.env再改 - 生产用平台环境变量,不把
.env文件拷到服务器(除非有安全的密钥管理) - Web、脚本、定时任务 共用同一套数据库相关变量
- 部署前检查:
DEBUG=0、SECRET_KEY已设、HTTPS Cookie 已对齐
14. 小结
记住四件事:
- 秘密不进代码 ---
SECRET_KEY、数据库密码、API Key 走环境变量 .env本地用、不进 Git --- 配合load_dotenv()FLASK_DEBUG--- 开发1,生产0- 同一变量多处复用 --- Web 和脚本读同一个
DATABASE_URL
