Authelia本身不是网关,仅做认证校验,必须搭配Nginx、Traefik或Go反向代理使用;其/api/authz/forward-auth要求严格遵循ForwardAuth协议:POST请求、透传原始头、同步响应、超时控制及X-Original-URL等关键头补全。

Authelia 本身不是网关,它不转发请求,只做认证校验。想用它做“极简反向代理校验网关”,必须搭配反向代理(如 Nginx、Traefik 或自研 Go 代理)使用 —— Authelia 只负责在请求抵达后端前返回 200 OK(放行)或 401/403(拦截)。直接用 Go 写个“带 Authelia 校验的代理”是可行的,但别误以为能绕过 ForwardAuth 协议。
ForwardAuth 是强制通信协议,不是可选功能
Authelia 的 /api/authz/forward-auth 接口要求上游代理严格遵循 ForwardAuth 规范:
- 把原始请求头(尤其是 Authorization、Cookie、X-Forwarded-For)原样发过去
- 接收响应时只看 200 状态码和指定响应头(如 Remote-User)
- 非 200 必须中断转发,返回对应错误(不能静默忽略或 fallback)
- 所有校验逻辑必须同步完成,Authelia 不支持异步回调或轮询
- 常见错误:Go 代理里用
http.DefaultClient调Authelia但没设超时,导致单个卡住的认证拖垮整个连接池 - 必须显式设置
Timeout: 5 * time.Second,且Transport要复用,避免每请求新建连接 - 别手动拼
GET /api/authz/forward-auth?uri=...——Authelia要求 POST + 完整原始请求体(即使为空),否则会拒绝 - 转发请求前,需把
X-Original-URL、X-Original-Method等头补全,否则Authelia策略匹配可能失效
Go 中嵌入 ForwardAuth 校验的最小实现
核心不是写新代理,而是改造已有 httputil.ReverseProxy 的 Director 和前置拦截逻辑:
func autheliaCheck(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. 构造 ForwardAuth 请求
authReq, _ := http.NewRequest("POST", "http://authelia:9091/api/authz/forward-auth", nil)
authReq.Header.Set("X-Original-URL", r.URL.String())
authReq.Header.Set("X-Original-Method", r.Method)
authReq.Header.Set("X-Forwarded-For", getClientIP(r))
for _, h := range []string{"Authorization", "Cookie", "User-Agent"} {
if v := r.Header.Get(h); v != "" {
authReq.Header.Set(h, v)
}
}
<pre class="brush:php;toolbar:false;"> // 2. 同步调用,带超时
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Do(authReq)
if err != nil || resp.StatusCode != http.StatusOK {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// 3. 提取用户信息,注入 context(可选)
user := resp.Header.Get("Remote-User")
r = r.WithContext(context.WithValue(r.Context(), ctxKeyUser, user))
// 4. 放行给下游 proxy
next.ServeHTTP(w, r)
})}
注意:next 应该是已配置好 Director 和 ModifyResponse 的 *httputil.ReverseProxy 实例,不是裸 http.ServeMux。
路径截断与 Host 头透传必须手工对齐
Authelia 的策略规则(如 domain.example.com/resource/*)依赖原始请求的 Host 和完整 Path。而 Go 反向代理默认会改写 req.URL.Host 和 req.URL.Path,若未同步还原,Authelia 就看不到真实访问路径:
-
Director函数里,必须保留r.Host到req.Header.Set("Host", r.Host),否则 Authelia 收到的是后端地址(如user-srv:8080)而非域名 - 若网关做了路径前缀剥离(如
/api/v1/ → /),则需在 ForwardAuth 请求中用X-Original-URL传原始值,不能只传处理后的req.URL - 别依赖
req.URL.EscapedPath()—— 它不保证与原始 URI 一致,应从r.URL.RequestURI()或r.RequestURI提取 -
Remote-Groups等响应头需手动复制到下游请求头,否则后端服务拿不到授权上下文
真正容易被忽略的点:Authelia 返回的 Remote-User 是字符串,但 Go 的 context 值传递没有类型安全,下游 handler 若直接类型断言为 string 却忘了检查 ok,panic 会直接终止整个请求链。校验网关的健壮性不在转发逻辑,而在每一处边界判断是否显式覆盖了 nil、空字符串、超时错误这三类情况。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











