产品定价 立即试用
社区版
使用Nginx反向代理保护Ollama
入门 文档 指南 安装 架构 API 常见问题
目录

使用Nginx反向代理保护Ollama

概述

Ollama是本地运行大语言模型(LLM)的强力工具,但不包含内置认证机制。 当在网络上暴露Ollama时,保护API端点成为您的责任。

本指南演示如何将Ollama与Nginx作为反向代理部署,为Ollama部署添加认证。Nginx代理充当安全守门人,在将请求转发到Ollama服务前验证凭据。

我们将介绍两种常见认证方式:

  • HTTP Basic Authentication(用户名和密码)
  • Bearer Token Authentication(API key)

Ollama和Nginx两个服务将通过Docker Compose以容器形式一起部署。 本指南重点通过可工作的实现演示概念,您可在此基础上进一步定制。 我们使用标准Ollama Docker镜像且不启用GPU加速,以便设置简洁,之后可添加GPU支持以提升性能。

文档警告图标

完成本指南后,我们强烈建议按照官方Nginx HTTPS配置指南为Nginx代理配置HTTPS,以确保凭据(密码或bearer token)始终加密传输,不会以明文在网络上发送。

前置条件

开始前,请确保已安装Docker和Docker Compose。 最简单的方式是安装Docker Desktop并确保其已运行后再继续。

设置:项目目录

首先创建主项目目录ollama-nginx-auth。本指南中创建的所有文件都将放在该目录内。

然后在ollama-nginx-auth目录内创建nginx子目录,用于存放Nginx相关配置文件。

完成后,目录结构应如下:

1
2
ollama-nginx-auth/
└── nginx/

确保后续步骤都在主目录ollama-nginx-auth下进行。

方式1:HTTP Basic Authentication

此方式使用简单的用户名和密码保护端点。 收到请求时,Nginx根据.htpasswd文件中的加密用户列表验证提供的凭据,以决定允许或拒绝访问。

.htpasswd是用于在Nginx等Web服务器上存储basic认证用户名和密码的标准文件。 文件中每行代表一个用户,包含用户名后跟冒号和加密(哈希)密码。

步骤1:创建凭据文件

从项目根目录(ollama-nginx-auth)在nginx目录内创建.htpasswd文件。以下命令创建用户名为myuser、密码为mypassword的文件。

1
docker run --rm -it httpd:alpine htpasswd -nb myuser mypassword > ./nginx/.htpasswd
1
docker run --rm -it httpd:alpine htpasswd -nb myuser mypassword | Out-File -FilePath ./nginx/.htpasswd -Encoding ascii

步骤2:创建Nginx配置文件

nginx目录(ollama-nginx-auth/nginx/basic_auth.conf)下创建basic_auth.conf文件,并粘贴以下内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
events {}

http {
    server {
        listen 80;
    
        location / {
            # 此部分强制实施HTTP Basic Authentication
            auth_basic "Restricted Access";
            auth_basic_user_file /etc/nginx/.htpasswd; # 容器内凭据文件的路径
    
            # 如果认证成功,则转发请求到Ollama
            proxy_pass http://ollama:11434;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            
            # 为慢模型响应增加超时以防止504网关超时错误
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
        }
    }
}

配置说明:

  • listen 80;:Nginx在Docker容器内监听80端口。
  • auth_basic "Restricted Access";:启用HTTP Basic Authentication。
  • auth_basic_user_file /etc/nginx/.htpasswd;:指定容器内密码文件路径。我们将把本地文件挂载到该路径。
  • proxy_pass http://ollama:11434;:将已认证的请求转发到ollama服务的内部地址。

步骤3:创建Docker Compose文件

在项目根目录(ollama-nginx-auth/docker-compose.basic.yml)创建docker-compose.basic.yml文件,并粘贴以下内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
services:
  ollama:
    image: ollama/ollama
    container_name: ollama
    volumes:
      - ollama_data:/root/.ollama
    restart: unless-stopped

  nginx:
    image: nginx:latest
    container_name: nginx_proxy
    ports:
      - "8880:80"
    volumes:
      - ./nginx/basic_auth.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/.htpasswd:/etc/nginx/.htpasswd:ro
    depends_on:
      - ollama
    restart: unless-stopped

volumes:
  ollama_data:

步骤4:运行与测试

使用指定的compose文件启动服务。-f参数指定要使用的文件。可能需要一定时间。

1
docker compose -f docker-compose.basic.yml up -d

在Ollama容器内直接执行命令拉取模型。我们使用适合测试的轻量模型gemma3:1b。可能需要一定时间。

1
docker exec -it ollama ollama pull gemma3:1b

使用您的用户(myuser)测试:

1
2
3
curl http://localhost:8880/api/generate \
  -u myuser:mypassword \
  -d '{"model": "gemma3:1b", "prompt": "Why is the sky blue?", "stream": false}'
1
2
3
4
5
$headers = @{
    "Authorization" = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("myuser:mypassword"))
}
$body = '{"model": "gemma3:1b", "prompt": "Why is the sky blue?", "stream": false}'
Invoke-RestMethod -Uri http://localhost:8880/api/generate -Method Post -Headers $headers -Body $body -ContentType "application/json"

使用错误凭据进行API调用以验证失败情况:

1
2
3
curl http://localhost:8880/api/generate \
  -u wronguser:wrongpassword \
  -d '{"model": "gemma3:1b", "prompt": "This will fail", "stream": false}'
1
2
3
4
5
$headers = @{
    "Authorization" = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("wronguser:wrongpassword"))
}
$body = '{"model": "gemma3:1b", "prompt": "This will fail", "stream": false}'
Invoke-RestMethod -Uri http://localhost:8880/api/generate -Method Post -Headers $headers -Body $body -ContentType "application/json"

输出将显示 401 Unauthorized 错误。

步骤5(可选):管理用户

可在 .htpasswd 文件中轻松添加或删除用户。对该文件的修改会立即生效,无需重启Nginx。

文档信息图标

添加用户时请始终使用 htpasswd 命令。该工具会正确加密密码并确保凭据以Nginx要求的格式存储。 手动向文件添加明文密码将无效。

添加新用户:

再次运行 htpasswd 命令。以下示例添加用户 anotheruser,密码为 anotherpassword

1
docker run --rm -it httpd:alpine htpasswd -nb anotheruser anotherpassword >> ./nginx/.htpasswd
1
docker run --rm -it httpd:alpine htpasswd -nb anotheruser anotherpassword | Out-File -FilePath ./nginx/.htpasswd -Encoding ascii -Append

需要多少用户即可重复执行该命令。

删除用户:

在文本编辑器中打开 ./nginx/.htpasswd 文件,删除对应用户所在行即可。

方式2:Bearer Token(API Key)认证

此方式使用密钥token。您将在纯文本文件中管理密钥,Nginx将配置为读取该文件且无需重启服务。

步骤1:创建API Keys文件

nginx 目录(ollama-nginx-auth/nginx/api_keys.txt)下创建 api_keys.txt 文件,将API key粘贴进去,每行一个。

1
2
my-secret-api-key-1
admin-key-abcdef

步骤2:创建Nginx配置文件

nginx 目录(ollama-nginx-auth/nginx/bearer_token.conf)下创建 bearer_token.conf 文件,并粘贴以下内容。 此配置包含用于动态读取API keys文件的 Lua 脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
events {}

http {
    server {
        listen 80;

        location / {
            # Lua script to read keys from a file and check against the Authorization header
            # This code runs for every request to this location.
            access_by_lua_block {
                local function trim(s)
                    return (s:gsub("^%s*(.-)%s*$", "%1"))
                end
            
                -- Function to read keys from the file into a set for quick lookups
                local function get_keys_from_file(path)
                    local keys = {}
                    local file = io.open(path, "r")
                    if not file then
                        ngx.log(ngx.ERR, "cannot open api keys file: ", path)
                        return keys
                    end
                    for line in file:lines() do
                        line = trim(line)
                        if line ~= "" then
                            keys[line] = true
                        end
                    end
                    file:close()
                    return keys
                end

                -- Path to the keys file inside the container
                local api_keys_file = "/etc/nginx/api_keys.txt"
                local valid_keys = get_keys_from_file(api_keys_file)

                -- Check the Authorization header
                local auth_header = ngx.var.http_authorization or ""
                local _, _, token = string.find(auth_header, "Bearer%s+(.+)")

                if not token or not valid_keys[token] then
                    return ngx.exit(ngx.HTTP_UNAUTHORIZED)
                end
            }
         
            # If access is granted, forward the request to Ollama
            proxy_pass http://ollama:11434;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            
            # Increase timeouts for slow model responses to prevent 504 Gateway Timeout errors
            proxy_connect_timeout 300s;
            proxy_send_timeout 300s;
            proxy_read_timeout 300s;
        }
    }
}

配置说明:

  • listen 80;:Nginx在Docker容器内监听80端口。
  • access_by_lua_block:为每个请求执行Lua脚本以验证Bearer token。
    • 脚本在每次请求时从 /etc/nginx/api_keys.txt 读取有效API key。
    • Authorization: Bearer <token> header中提取token。
    • 若token缺失或不在有效key列表中,返回401 Unauthorized响应。
  • proxy_pass http://ollama:11434;:将已认证的请求转发到 ollama 服务的内部地址。

步骤3:创建Docker Compose文件

在项目根目录(ollama-nginx-auth/docker-compose.bearer.yml)创建 docker-compose.bearer.yml 文件,并粘贴以下内容。 该 docker-compose.bearer.yml 使用包含所需Lua模块的Nginx镜像(openresty/openresty)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
services:
  ollama:
    image: ollama/ollama
    container_name: ollama
    volumes:
      - ollama_data:/root/.ollama
    restart: unless-stopped

  nginx:
    # Use the OpenResty image which includes the Nginx Lua module
    image: openresty/openresty:latest
    container_name: nginx_proxy
    ports:
      - "8880:80"
    volumes:
      # Mount the new Nginx config and the API keys file
      - ./nginx/bearer_token.conf:/usr/local/openresty/nginx/conf/nginx.conf:ro
      - ./nginx/api_keys.txt:/etc/nginx/api_keys.txt:ro
    depends_on:
      - ollama
    restart: unless-stopped

volumes:
  ollama_data:

步骤4:运行与测试

使用指定的compose文件启动服务。-f 参数指定要使用的文件。

1
docker compose -f docker-compose.bearer.yml up -d

拉取模型(若已在方式1中执行过则较快):

1
docker exec -it ollama ollama pull gemma3:1b

使用有效API key测试请求:

1
2
3
curl http://localhost:8880/api/generate \
  -H "Authorization: Bearer my-secret-api-key-1" \
  -d '{"model": "gemma3:1b", "prompt": "Explain black holes to a 5-year-old", "stream": false}'
1
2
3
4
5
$headers = @{
    "Authorization" = "Bearer my-secret-api-key-1"
}
$body = '{"model": "gemma3:1b", "prompt": "Explain black holes to a 5-year-old", "stream": false}'
Invoke-RestMethod -Uri http://localhost:8880/api/generate -Method Post -Headers $headers -Body $body -ContentType "application/json"

使用无效API key测试以验证失败情况:

1
2
3
curl http://localhost:8880/api/generate -v \
  -H "Authorization: Bearer invalid-key" \
  -d '{"model": "gemma3:1b", "prompt": "This will fail", "stream": false}'
1
2
3
4
5
$headers = @{
    "Authorization" = "Bearer invalid-key"
}
$body = '{"model": "gemma3:1b", "prompt": "This will fail", "stream": false}'
Invoke-RestMethod -Uri http://localhost:8880/api/generate -Method Post -Headers $headers -Body $body -ContentType "application/json"

步骤5(可选):管理API Keys

在文本编辑器中打开 ./nginx/api_keys.txt 文件。添加、修改或删除key(每行一个),然后保存。

由于Lua脚本在每次请求时都会读取该文件,修改会在下一次API请求时立即生效。

例如,可编辑该文件,删除 admin-key-abcdef key,保存后尝试在测试请求中使用该key。 请求将返回401 Unauthorized错误。

使用

启动或停止服务时,使用 docker compose updocker compose down 命令,并确保为所选认证方式指定对应文件(docker-compose.basic.ymldocker-compose.bearer.yml)。

  • 无论哪种方式,从项目目录运行以下命令启动服务,将 <compose-file-name> 替换为正确的文件名:
    1
    
    docker compose -f <compose-file-name> up -d
    
  • 完成后,使用对应文件名停止容器:
    1
    
    docker compose -f <compose-file-name> down
    

下一步

Ollama端点就绪后,建议执行以下操作: