vllm部署模型设置api_key

模型与算力

给自建 vLLM 服务加一层 api_key 鉴权:从选参数、起服务到客户端验证,附内网暴露前的检查清单与常见报错排查顺序。

本地或内网跑了 vLLM,接口默认是裸的——谁都能调,顺手一调还能把你显卡占满。要接给同事、接进 Dify 这类上层工具,或者只是不想被同网段的人蹭,都得先加个 api_key。这篇讲清 vLLM 的 key 是怎么设的、设完客户端怎么改、什么场景下这层鉴权其实不够用,最后给一份暴露到内网前该走的检查顺序。

先明确:vLLM 默认没有鉴权

vLLM 起服务的时候,默认是不带任何身份校验的。它对外暴露的是 OpenAI 兼容接口,只要能访问到那个 IP 和端口,谁都能发请求、谁都能调模型。这不是漏洞,是它默认按「内网自用工具」来设计的。所以要不要加 api_key,本质上不是安全问题,而是「这个端口打算给谁看」的问题。

很多人以为不加 key 的风险是被入侵、被拖库。实际更常见的情况是:端口一开,隔壁网段的扫描器、云厂商的安全扫描、甚至同事随手写的一个脚本,就把你的服务当成了免费算力。请求一多,显存和并发槽位被占满,你自己的业务反而排队等不到结果。表现就是「明明没人用,怎么这么慢」。

默认不鉴权
不加 --api-key 时,vLLM 不校验 Authorization 头,任何来源都能调用
真正的代价
不是数据被偷,而是算力被白嫖,并发被挤占,自己的请求超时
判断的分界线
监听地址是 localhost 还是 0.0.0.0,决定了要不要加这层 key
部署方式要不要加 api_key说明
只在本机 localhost 自测可以不加只有本机能连,外部扫不到,省一步配置
绑定到局域网地址给同事用建议加同网段任何设备都能扫到端口,容易被顺手调用
映射到公网或云主机必须加公网扫描是常态,裸奔等于把算力放到门口
前面已有网关做鉴权可以省略但要确认网关真的拦住了,而不是只做了转发
  • 先看启动命令里的 host 参数:是 127.0.0.1 还是 0.0.0.0;
  • 再看端口有没有通过防火墙、安全组或反向代理暴露出去;
  • 然后问自己一句:这个地址,除了我,还有谁能 ping 到;
  • 如果答案是「不止我」,那就该加 key,或者把鉴权挪到前面的网关;
  • 加完 key 之后,用不带 Authorization 头的请求验一次,确认真的被拒。

有一种情况确实可以不加:你已经把鉴权交给前面的 Nginx、API 网关或者服务网格,vLLM 只在内网监听、只接受网关转发过来的流量。这时候在 vLLM 这层再配一遍 key 属于重复劳动。但前提是想清楚「谁在管」——网关的规则是谁写的、改配置要不要过审、内部服务之间走不走鉴权。如果这几条没人答得上来,那宁可 vLLM 自己带一层 key,也别把安全全押在一个没人维护的网关配置上。

还有一个容易被忽略的点:加了 key 只是挡住了「不知道 key 的人」,挡不住「知道 key 的人」。如果是多人共用一把 key,谁调了多少、谁把并发打满了,你依然查不出来。这种场景下要考虑的不是加不加 key,而是要不要按人分配 key、以及在网关层做限流。具体到各家托管服务和自建方案的差别,可以到 模型资料库(/models) 对照着看,那边按接入方式整理过。

接着往下看确定要加 key 之后,下一步是选怎么加、加在哪一层。站内把自建和走网关两种做法分开讲过,可以对着自己的部署方式挑一条路径。

起服务时怎么把 key 挂上去

挂 key 这件事只在启动命令里做一次,核心就是给 api_server 加一个鉴权参数,值填你自己临时生成的随机串。别再手敲「123456」或者「mykey」这种,等于没开鉴权。生成完存到环境变量或者密码管理器里,启动时引用它,别把明文写进 shell 历史。

  1. 生成随机串用 openssl rand -hex 32 输出一串十六进制随机字符,或者用 Python 的 secrets.token_urlsafe(32) 拿一串 URL 安全字符。长度往 32 字节以上靠,别自己敲键盘凑。
  2. 存进环境变量把结果赋给一个变量,比如 export VLLM_API_KEY=$(openssl rand -hex 32)。这样命令里引用的是变量名,不清屏也能避开历史记录里躺明文。
  3. 启动时挂上去在 vllm serve 或 api_server 的启动命令里加上对应的 api-key 参数,值写 $VLLM_API_KEY。具体参数名和可用取值不同版本会增删,去站内的 vllm api_server 参数清单对当期的写法。
  4. 验证鉴权是否生效起服务后,先不带 Authorization 头打一次接口,应当被拒;再带上 Bearer 加那张 key 打一次,应当正常返回。两次结果不一致才算挂对了。
做法适用情况不适用情况
命令行直接填随机串本地临时调试、单机验证命令会进历史记录或共享终端
环境变量引用常规部署,key 不希望出现在进程参数明文里环境本身不可信、变量会被日志带出
启动脚本里从密钥文件读多实例复用同一套部署流程需要按人区分 key、按 key 统计
外面套一层网关要按人发 key、按 key 限流或做审计只是单机自己用,图省事

这里有个容易踩的坑:vLLM 这层鉴权是单 key 语义,服务端只认「有没有这张 key」,不区分是谁在用,也不给你做按 key 的配额或限流。所以如果你要按人发 key、按 key 看用量或者按 key 限速,别在这层硬凑,应该在外面加一层网关,由网关分发多把 key、做统计和限流,vLLM 只管模型推理那部分。

  • 钥匙生成后先存好再启动,别启动完才回头找
  • 验证分两次:不带 key 应被拒、带 key 应通过
  • 多人共用一套服务时,先在网关侧规划 key 的分配方式
  • 不同版本参数名可能不同,以站内当期参数清单为准,别照抄旧命令
  • key 一旦怀疑泄露,直接换一串重启,成本比排查低

改完怎么确认鉴权真的生效了

改完参数重启服务,别急着接业务,先花两分钟把三件事按顺序跑一遍。很多人卡在「看起来能通」就收工了,结果鉴权其实没生效,谁都能裸调。下面这套顺序的关键是:先证明不带 key 会被拦,再证明带对 key 能放行,最后证明带错 key 还是被拦。

  1. 不带 Authorization 头请求直接 curl 模型列表接口,什么头都不加。预期是 401 或等价的未授权响应,绝不能返回正常数据。如果这里就正常返回了,说明鉴权根本没挂上,后面的测试都不用做了。
  2. 带正确的 Bearer 头再请求一次把启动时配置的那个 key 拼成 Authorization: Bearer 再打一遍同一个接口。预期返回模型列表,说明服务确实读到了 key 并且比对通过。这一步能通,才说明链路是活的。
  3. 换一个错的 key 再打一遍把 key 改成随便一串明显不对的值,第三次请求同一接口。预期仍然是被拒。这一步是为了排除「服务压根没校验、只是网络刚好能连」这种假阳性。只有前两步对、第三步也拦得住,才算真的生效。
# 1) 不带头
curl -i http://127.0.0.1:8000/v1/models

# 2) 带正确的 key
curl -i -H "Authorization: Bearer 你的KEY" http://127.0.0.1:8000/v1/models

# 3) 带一个错的 key
curl -i -H "Authorization: Bearer wrong-key-test" http://127.0.0.1:8000/v1/models
请求方式预期结果不符合说明什么
不加 Authorization 头401 / 未授权key 没生效,或接口没走鉴权中间件
带正确 Bearer 头正常返回模型列表key 没被正确读入,或头格式不对
带错误 Bearer 头仍然 401 / 未授权校验逻辑没跑,可能是代理放行或参数没解析

三步里任何一步不符合预期,排查顺序是固定的:先回去翻启动日志,确认启动参数里那个 key 有没有被解析进去、有没有报参数拼写错误;再看服务前面是不是挂了反向代理,比如 nginx 或网关,它们有可能把 Authorization 头改写或直接丢掉。顺序别倒过来先怀疑客户端。

下一步鉴权确认没问题后,去站内把服务暴露方式和调用示例对一遍,避免上线后才发现端口或路径不对。

客户端和上层工具怎么改

先说结论:绝大多数兼容 OpenAI 协议的客户端,配置项就两个——base_url 和 api_key,模型名填你启动 vLLM 时用的那个名字(就是 --served-model-name 那块的东西,客户端别自己猜)。先把这两项填对,比研究客户端源码快得多。

客户端里的字段填什么
Base URL / API 地址你的服务地址,带不带 /v1 两种都试一次
API Key / 密钥你启动时设的那个 key,没设就得先设一个
Model / 模型名部署时用的 served-model-name,照抄

base_url 带不带 /v1 是最高频的坑。有的客户端自己会拼 /v1/chat/completions,你多写一个 /v1 就变成 /v1/v1/...;有的客户端要求你必须写全。没法一眼看出来的话,两种各试一次,哪个返回正常用哪个,别去翻文档猜。

  1. 先验证服务本身通不通用 curl 直接打一次 chat/completions,确认服务能返回内容,排除是服务端问题还是客户端配置问题
  2. 填 base_url 两种写法先按带 /v1 填一次,报错就改成不带 /v1 再试,两次之内基本能定位
  3. 填 api_key 和模型名key 用你启动参数里设的那个,模型名严格照抄 served-model-name,不要用模型文件路径名
  4. 确认返回内容随便问一句短问题,能稳定返回就说明链路通了,再接到实际业务里

接 Dify、Open WebUI 这类平台时,填的是同一个 key,但注意这些平台会把 key 存在它自己的服务端。如果你在公网能访问的测试实例上填了生产 key,等于把 key 放到了别人能碰到的地方。测试实例就用一个单独的、权限受限的 key,别图省事复用。

  • 平台侧存 key 是常态,先确认这个实例谁能访问
  • 测试实例单独发一个 key,别和生产共用
  • 确认平台能不能限制模型范围,能限就限
  • key 一旦怀疑暴露,直接换掉重启服务,别只改客户端配置

还有一种情况是客户端根本不走 OpenAI 协议,比如只认自己的私有格式。这种不是配置问题,是协议不匹配,硬调字段没用,要么换个客户端,要么在前面套一个协议转换的转发层。判断方法很简单:看它文档里有没有「兼容 OpenAI」这类字眼,没有就别浪费时间试。

key 本身怎么管

先说结论:vLLM 自带的这层 key 只是「进门口令」,不是账号体系。它的作用是让请求方在调用时多带一个字段,服务端对一下是否匹配,匹配就放行。它不知道谁是谁、调了多少次、该扣多少钱。所以「怎么管」的重点不是把它当机密文件供起来,而是别让它出现在不该出现的地方,并且换的时候别把线上打挂。

三个最常踩的坑,按出现频率排

坑典型表现为什么麻烦
写进代码仓库key 直接硬编码在启动脚本或客户端里,push 上去仓库可能公开,历史提交删不干净,协作者人手一份
写进前端浏览器里直接 fetch 模型接口,key 放 JS 变量打开开发者工具就能看到,等于公开
留在命令行历史curl 时把 key 拼在参数里,之后按上箭头就复现本机 history、终端日志、同事录屏都可能留存

正确的存放位置:环境变量或本地 .env

  1. 服务端读取环境变量启动 vLLM 时从环境变量取 key,而不是写死在命令里。可以先用一行命令确认变量确实注入了,比如 echo 检查前缀,别打印完整值。
  2. 客户端也用环境变量客户端代码统一从环境变量读,本地开发放 .env 文件。写完用一次实际请求验证:把环境变量临时改错,请求应该被拒,说明确实是从变量读的。
  3. 把 .env 加进忽略清单在仓库的忽略文件里加上 .env 以及任何带 key 的本地配置文件。加完用版本控制的状态命令确认它没有被跟踪,再提交一次看看它是否出现在变更列表里。
  4. 给一份 .env.example仓库里只保留字段名和占位符的示例文件,真实值永远不进仓库。这样新同事知道要配哪几个变量,也不会误提交。
  • 服务端:环境变量注入,启动命令里不出现明文 key;
  • 客户端:环境变量或 .env,代码里只出现变量名;
  • 仓库:.env 进忽略清单,只提交 .env.example;
  • 终端:不用带 key 的 curl 直接贴进聊天窗口,需要分享就先把 key 替换成占位符;
  • 容器:用密钥管理或挂载文件,别把 key 写进镜像层。

轮换节奏:什么时候必须换,怎么换不中断

这层 key 没有细粒度审计,泄漏了也未必能第一时间发现,所以判断标准要粗一点、往严里靠:只要有人离职、key 被共享给过外部、或者曾经贴在群里 / 工单里 / 截图里,就直接换,不要评估「应该没事」。定期轮换的频率站内没有统一建议,可以到 指南中心(/guides/) 看通用的密钥轮换思路,再结合你们自己的规模定。

  1. 挑一个低峰切换窗口服务端和客户端必须同时改,中间必然有一段不一致的时间。选调用量最低的时段,提前在群里说一声。
  2. 先服务端同时接受新旧两把 key让服务端临时认可两把 key,此时旧客户端仍然能通。验证方式是分别用新旧 key 各发一次请求,两个都应该成功。
  3. 再逐台改客户端按机器或按服务分批更新客户端配置并重启。每改完一批,就用监控或日志确认没有认证失败堆积。
  4. 最后摘掉旧 key确认所有客户端都在用新 key 后,服务端移除旧 key。摘掉后再用旧 key 发一次请求,应该被拒,这才算收尾完成。

一台机器跑多个实例:一个实例一把 key

如果同一台机器上起了多个 vLLM 实例,或者一个实例服务多个上游业务,别图省事共用一把 key。每把 key 对应一个明确的调用方,出问题时看服务端日志就能定位到是谁在打,而不用猜。代价只是多配几个环境变量,收益是排查时间从「翻半小时日志」降到「看一眼就知道」。

场景key 怎么分出问题时怎么定位
同机多实例每个实例一把独立 key看是哪个实例的日志出现认证记录
一个实例多个上游业务每个业务一把 key按 key 区分调用来源,定位到具体业务线
测试与生产混用测试单独一把,绝不复用生产 key测试打满也不影响生产,也便于随时作废

它做不到什么,别把它当正式服务的安全边界

如果你的模型是要对外提供正式服务的,这层 key 不够用:它没有按量计费、没有按次配额、没有细粒度审计,也无法在单个调用方异常时只掐那一个。正式对外时应该在前面加一层网关或鉴权代理,把计费、限流、审计交给那一层来做,vLLM 这层 key 只当内网的第二道门。反过来,如果只是小团队内网自用、调用方都是自己人,那这层 key 加上环境变量管理就够了,不必上更重的方案。

要不要暴露到公网:先过这张清单

vLLM 自己那个 api_key 属于最内层的一道门,挡的是「谁拿着 key 谁能调模型」,它不管端口是不是被扫、也不管别人是不是拿它当免费算力。所以在配 key 之前,先把「这个服务到底要不要给外面看」想清楚,顺序反了后面会一直补漏洞。

  1. 先问能不能只在内网用能内网解决就不要做端口映射。让调用方和 vLLM 在同一内网或同一容器网络里,通过内网地址直连;验证方式是换一台外网机器去连这个地址,连不上才说明没暴露。
  2. 跨网络就上隧道或 VPN确实要跨公网访问,优先用 WireGuard、Tailscale 这类隧道,或者公司已有的 VPN,让调用方先进内网再连 vLLM。验证方法是看服务监听地址仍绑在内网网卡上,公网侧扫不到这个端口。
  3. 必须开端口时,前面加反代Nginx/Caddy 这类反代放在最前面做 TLS 终止,同时挂上限流和 IP 白名单,vLLM 只监听回环或内网。验证方式是直接从公网访问 vLLM 的裸端口,应当连不通,只能走反代的域名或入口。
  4. 只放业务需要的路径反代里只转发业务真正用到的那几条前缀,把管理类、调试类、指标类端点挡在外面。验证方式是在反代上逐条列白名单,再拿一台不在白名单的机器试那几个被挡路径,确认返回拒绝而不是正常响应。
  5. 暴露前后各量一次延迟上公网之前,先用站内的接口测速页面记一条延迟基线;暴露之后再量一次。如果同一个请求的延迟明显变差、抖动变大,多半是有人蹭你的算力或者端口被扫到了,先去查访问日志的来源 IP。
暴露方式谁来挡第一层什么时候不适用
仅内网地址网络本身就不通调用方和模型不在同一网络时
隧道 / VPN隧道或 VPN 的鉴权对接方只支持公网 HTTP 时
反代 + 白名单TLS、限流、IP 白名单调用方 IP 不固定又做不了鉴权时
直接映射公网端口只有 vLLM 的 api_key任何生产或长期在线的场景
  • 服务是否只监听内网或回环地址,公网侧能否扫到端口;
  • 反代是否做了 TLS、限流和 IP 白名单三件事;
  • 管理类、调试类、指标类端点是否确认没被一起转发;
  • 暴露前后的延迟基线是否各测过一次并做了对比;
  • 访问日志是否留了来源 IP,出问题时能不能回溯;
  • key 是否按期轮换,且没有在客户端或前端里硬编码。

这张清单里任何一条答不上来,就先别开公网。先把暴露面收回去,再回头配 api_key 才有意义;真需要公开访问的,去站内接口测速页面 /speed-test 先记一条延迟基线,暴露后再对着同一入口复测,数字变化会直接告诉你有没有被人蹭。关于 key 本身怎么设、怎么轮换,不在这一节展开。

排查顺序:401 之外的坑

带上正确的 key 还报 401,别急着换 key,九成是请求头写法的问题。检查 Authorization 这一行是不是严格的「Bearer + 一个空格 + 你的 key」,大小写、空格数量、有没有多余引号都会让服务端认不出来。用 curl 或 SDK 都先打印一遍实际发出去的 header 再判断。

  • 拼写:Bearer 首字母大写,后面必须有一个半角空格,不能是冒号或等号
  • 粘贴:从配置文件或环境变量复制时容易带上换行、引号、末尾空格
  • 前缀:有的客户端会自动加 Bearer,你再手写一遍就变成两个 Bearer
  • 位置:确认放在请求头而不是查询参数或请求体里
  • 编码:key 里若有特殊字符,注意 shell 转义和配置文件引号处理
  1. 打印实际请求头用 curl 加 -v,或让 SDK 打开日志,看到真正发出去的那行 Authorization;比对是否等于「Bearer 空格 key」。
  2. 确认参数位置如果服务根本起不来或参数像被忽略,检查启动命令里参数是放在子命令之前还是之后;有些写法要求放在子命令之后才生效,放错位置会被静默丢弃而不是报错。
  3. 通了但慢,先分离鉴权能正常返回但延迟高,和 key 基本无关。先看并发数是否被打满、显存是否吃紧,到站内显存与并发相关的页面对一遍自己的配置。
  4. 完全连不上,先查端口和绑定连接被拒或超时,先确认监听端口和绑定地址是否对得上(例如只监听了本机地址却从外部访问),再回头怀疑鉴权。
现象最可能的层面先查什么
正确 key 仍 401请求头格式Bearer 与 key 之间的空格、是否重复加前缀
服务起不来/参数无效启动命令参数是否放在子命令之后,是否被静默忽略
能通但很慢资源与并发并发上限、显存占用,对照站内并发与显存页面
完全连不上网络与绑定端口是否开放、绑定地址是否只限本机
换 key 后依旧 401环境不一致客户端实际读取的 key 与配置文件是否同一个

常见问题

vLLM 设置 api_key 之后,Ollama 那边的做法能照搬吗?

不能照搬。两者的鉴权机制不是一套东西,环境变量名、参数位置都不一样。如果你同时跑着两个框架,建议每个都单独查一遍对应文档,别凭印象改配置,改错了往往表现为「连不上」,很难第一眼看出来是鉴权还是网络问题。

只跑单机 localhost,还有必要设 key 吗?

单机自测阶段可以先不设,省一步。但只要你打算把它接进别的工具、或者以后可能要给别人调,建议一开始就设上,后面加客户端配置时习惯已经养成,不用回头再改一遍客户端和服务端。

一个 key 能不能分给多个人用?

技术上可以,但这层鉴权本身不区分调用者,没法按人看用量、也没法单独吊销某个人。如果确实要按人管,正确做法是在 vLLM 前面加一层网关,由网关发多把 key 并做配额,vLLM 只认网关过来的那一个。

设完 key 之后原来能用的客户端挂了,怎么快速定位?

先把客户端和鉴权解耦验证:用命令行带正确 header 直接请求一次模型列表,能通说明服务端没问题,问题在客户端配置;不能通说明服务端没读进参数。这样一次就能把范围缩到一半,比反复改配置快。

这层 key 泄露了会有什么后果?

最直接的后果是别人用你的卡跑推理,你的业务请求被挤慢甚至超时,显存和算力被白占。因为它不带计费能力,损失以资源为主。发现泄露就立刻换 key 并重启服务,同时检查有没有别的东西通过这个入口被访问到。

本文依据各平台公开文档与官方公告整理,只做中文归纳;具体做法、参数与限制以官方文档为准。