在构建本地或云端搜索引擎系统时,EasySearch 凭借其轻量、高性能、易部署等优势,逐渐成为众多开发者和技术爱好者的首选。但在实际部署过程中,如何借助 Nginx 为 EasySearch 提供高效、稳定且安全的访问入口,尤其是在身份认证方面,仍然是一个关键技术环节。
本教程将围绕 Basic Auth 认证机制展开,系统讲解如何通过 Nginx 实现安全防护、认证信息透传等常见配置场景,帮助你在多种实际部署环境中快速搭建可靠的访问控制机制。
无论你是在搭建家庭 NAS 服务,还是在企业环境中集成搜索引擎系统,本教程都能为你提供一套可落地、可复用的 Nginx 安全认证解决方案。。
下面是我的 Nginx 配置文件示例。我们通过 Docker 启动 Nginx 容器,并将本地编写好的配置文件挂载到容器中,从而实现自定义的反向代理和认证逻辑:
1 2 3 4 5 docker run -d \ --name my-nginx \ -p 80:80 \ -v $(pwd )/default.conf:/etc/nginx/conf.d/default.conf \ nginx
default.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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 server { listen 80; server_name localhost; location / { return 200 'Nginx is running.\n' ; add_header Content-Type text/plain; } location /es/ { proxy_pass https://backend:9200/; proxy_http_version 1.1; 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_set_header Connection "" ; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS' ; add_header Access-Control-Allow-Headers 'Authorization,Content-Type' ; rewrite ^/es/(.*)$ /$1 break ; } }
🌐 配置整体结构 1 2 3 server { listen 80 ; server_name localhost;
监听端口 :监听本地 80
端口(HTTP 默认端口)
服务名称 :用于匹配请求的 Host
,这里是 localhost
🎉 欢迎页(根路径 /
) 1 2 3 4 location / { return 200 'Nginx is running.\n' ; add_header Content-Type text/plain; }
请求 /
会返回纯文本响应 "Nginx is running."
,可用于验证 Nginx 是否启动正常。
add_header Content-Type text/plain
:指定响应内容为纯文本。
🔁 /es/
代理 EasySearch 后端服务
🚚 请求头处理 1 2 3 4 5 6 proxy_http_version 1 .1 ;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_set_header Connection "" ;
proxy_http_version 1.1
:确保代理使用 HTTP/1.1,支持长连接。
Host
:保留原始请求的主机名。
X-Real-IP
/ X-Forwarded-For
:传递客户端真实 IP。
X-Forwarded-Proto
:传递原始协议(http / https)。
Connection ""
:用于避免默认的 keep-alive
设置引起错误(推荐保留)。
🔐 可选的认证头(注释中)
可选开启,用于将认证信息转发到后端。
上面的字符串是 admin:123456
的 base64 编码。可以根据需要开启。
🌍 CORS 设置(跨域) 1 2 3 add_header Access-Control-Allow-Origin *;add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS' ;add_header Access-Control-Allow-Headers 'Authorization,Content-Type' ;
允许任意源访问(前端页面可以跨域请求 /es/
)。
支持的方法:GET、POST、OPTIONS。
允许传递的请求头:Authorization 和 Content-Type。
✅ 适用于 AJAX 调试、前后端分离等场景。
🔧 URL 重写 1 rewrite ^/es/(.*)$ /$1 break ;
移除 /es/
前缀,转发给后端。例如: 用户请求 /es/_cat/indices
实际转发到 /cat/indices
。
break
表示在当前 location 中中止后续重写检查。
📦 可选静态资源(被注释掉)
若开启,可以直接通过 /static/xxx.js
访问 Nginx 本地 /usr/share/nginx/html/static/xxx.js
文件。
🔁 如果你想保留 /es/
前缀,则删除 rewrite
行。
在启动服务后,当我们通过浏览器访问 Nginx 时,页面会弹出身份验证窗口。需要注意的是,这里的认证提示实际上来自后端的 EasySearch 服务,而非 Nginx 本身,说明请求中的认证信息未在 Nginx 层被处理或透传,因此由 EasySearch 发起了再次认证的要求。
在输入正确的用户名和密码后,我们可以看到 Nginx 成功代理请求,并返回了来自 EasySearch 的 API 响应,说明认证流程已顺利通过,后端服务正常可用。
如果希望由 Nginx 代为完成 EasySearch 的身份验证,也就是实现自动登录的效果,可以在配置文件中添加如下指令,将认证信息通过 HTTP 头部传递给后端:
1 proxy_set_header Authorization "Basic YWRtaW46YWRtaW4=";
其中,YWRtaW46YWRtaW4xMjM=
是使用 Base64 编码后的 用户名:密码
字符串(例如 admin:admin
)。Nginx 在转发请求时会自动携带该 Header,从而实现对 EasySearch 的自动认证。
需要注意的是,Nginx 的配置文件修改后不会自动生效。为了确保配置被正确加载,需在每次更改配置文件后重启对应的容器服务。这是容器化部署中常见的操作流程,确保新配置被正确应用。
为了更直观地观察请求行为,我们使用了 Postman 进行测试。可以发现,即使在 Postman 中未显式添加任何认证信息,依然能够成功访问 EasySearch 集群。这说明前端未输入认证信息,但由于 Nginx 曾经缓存了认证状态,或配置了自动透传,导致后端依旧接收到了有效的认证头,从而允许了访问。
这种现象虽然在测试中提升了访问便利性,但在实际部署中可能带来安全风险,因此在生产环境中建议对认证流程进行严格控制,确保后端服务不会因为前端认证机制的疏漏而被绕过。
在一些教程中,常会提到一个名为 .htpasswd
的文件,它用于在 Nginx 层实现基本认证。当启用该机制后,Nginx 会对所有访问进行用户身份验证。
此时,是否将认证信息透传给后端服务,则由 proxy_pass_request_headers
参数决定。该参数默认值为 on
,也就是说,当认证通过后,客户端发送的 Authorization 头部信息会被 Nginx 一并转发给后端服务。
为了验证这一行为,我编写了一个简单的 Flask 程序作为后端,用于观察请求中的 Header 内容。在真正将请求代理至 EasySearch 之前,我先让 Nginx 将请求反向代理到这个 Flask 应用,从而可以直观地查看是否存在 Authorization 头被透传的情况。
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 from flask import Flask, request,abortimport base64app = Flask(__name__) @app.route('/' ) def hello_world (): print ("📥 Headers received from Nginx:" ) print ("Host:" , request.headers.get('Host' )) print ("X-Real-IP:" , request.headers.get('X-Real-IP' )) print ("X-Forwarded-For:" , request.headers.get('X-Forwarded-For' )) print ("X-Forwarded-Proto:" , request.headers.get('X-Forwarded-Proto' )) print (request.headers) auth_header = request.headers.get('Authorization' ) print ("Authorization:" , auth_header) if not auth_header or not auth_header.startswith('Basic ' ): abort(401 , description="Missing or invalid Authorization header" ) encoded = auth_header.split(' ' )[1 ] decoded = base64.b64decode(encoded).decode('utf-8' ) username, password = decoded.split(':' , 1 ) print (username, password) return 'Hello World!' if __name__ == '__main__' : app.run(host='0.0.0.0' , port=8000 ,debug=True )
这个是 flask 的打印的结果.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Host: secure-nginx.orb.local X-Real-IP: 192.168.215.1 X-Forwarded-For: 192.168.215.1 X-Forwarded-Proto: http Host: secure-nginx.orb.local X-Real-Ip: 192.168.215.1 X-Forwarded-For: 192.168.215.1 X-Forwarded-Proto: http Authorization: Basic YWRtaW46YWRtaW4= Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh-TW;q=0.9,zh;q=0.8 Cookie: perf_dv6Tr4n=1 Authorization: Basic YWRtaW46YWRtaW4= admin admin 192.168.X.X - - [24/Apr/2025 15:55:59] "GET / HTTP/1.1" 200 -
为了解决双重认证的问题,我们启用了认证信息透传的配置(默认的 roxy_pass_request_headers on;)。启用该配置后,用户只需在访问 Nginx 时进行一次手动身份验证。Nginx 会将用户提供的凭证通过 HTTP Header 透传至后端的 EasySearch 服务,从而避免二次认证。当用户直接访问 EasySearch 时,依然需要单独输入凭证进行认证;但通过 Nginx 访问时,只需在前端认证一次即可完成整个请求流程。
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 59 60 61 62 63 64 65 66 67 curl -k https://easysearch:9200 {"error" :{"root_cause" :[{"type" :"security_exception" ,"reason" :"Missing authentication information for REST request [/]" ,"header" :{"WWW-Authenticate" :"Basic realm=\"Security\" charset=\"UTF-8\"" }}],"type" :"security_exception" ,"reason" :"Missing authentication information for REST request [/]" ,"header" :{"WWW-Authenticate" :"Basic realm=\"Security\" charset=\"UTF-8\"" }},"status" :401}⏎ ------- curl -v -u "admin:admin" http://nginxhost/es/ * Trying 192.168.5.171:9201... * Connected to 192.168.5.171 (192.168.5.171) port 9201 * ALPN: curl offers h2,http/1.1 * (304) (OUT), TLS handshake, Client hello (1): * CAfile: /etc/ssl/cert.pem * CApath: none * (304) (IN), TLS handshake, Server hello (2): * (304) (IN), TLS handshake, Unknown (8): * (304) (IN), TLS handshake, Request CERT (13): * (304) (IN), TLS handshake, Certificate (11): * SSL certificate problem: self signed certificate * Closing connection curl: (60) SSL certificate problem: self signed certificate More details here: https://curl.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the web page mentioned above. * Host localhost:80 was resolved. * IPv6: ::1 * IPv4: 127.0.0.1 * Trying [::1]:80... * Connected to localhost (::1) port 80 * Server auth using Basic with user 'admin' > GET /es/ HTTP/1.1 > Host: localhost > Authorization: Basic YWRtaW46YWRtaW4= > User-Agent: curl/8.7.1 > Accept: */* > * Request completely sent off < HTTP/1.1 200 OK < Server: nginx/1.27.4 < Date: Thu, 24 Apr 2025 07:45:10 GMT < Content-Type: application/json; charset=UTF-8 < Content-Length: 552 < Connection: keep-alive < Access-Control-Allow-Origin: * < Access-Control-Allow-Methods: GET, POST, OPTIONS < Access-Control-Allow-Headers: Authorization,Content-Type < { "name" : "easysearch-node1" , "cluster_name" : "infinilabs" , "cluster_uuid" : "VcMD__DwSYSUqear8wp-XA" , "version" : { "distribution" : "easysearch" , "number" : "1.11.1" , "distributor" : "INFINI Labs" , "build_hash" : "4d0be0343919fb1a605e3c8284326b7e069eb9bf" , "build_date" : "2025-03-14T09:33:12.182925Z" , "build_snapshot" : false , "lucene_version" : "8.11.4" , "minimum_wire_lucene_version" : "7.7.0" , "minimum_lucene_index_compatibility_version" : "7.7.0" }, "tagline" : "You Know, For Easy Search!" } * Connection
本次将 Nginx 的访问认证密码修改为 admin123
后,发现在请求过程中出现了两次身份验证的提示。具体表现为:当用户输入错误的密码时,Nginx 会首先返回一次 401 Unauthorized。由于 Nginx 与 EasySearch 使用了不同的认证信息,Nginx 在将请求头(包括 Authorization 字段)转发至 EasySearch 时,EasySearch 检测到凭据不匹配,也会返回一次 401。由此导致了双重身份认证失败的现象,影响了正常访问流程。
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 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 curl -v -u "admin:admin" http://localhost/es/ * Trying 192.168.5.171:9201... * Connected to 192.168.5.171 (192.168.5.171) port 9201 * ALPN: curl offers h2,http/1.1 * (304) (OUT), TLS handshake, Client hello (1): * CAfile: /etc/ssl/cert.pem * CApath: none * (304) (IN), TLS handshake, Server hello (2): * (304) (IN), TLS handshake, Unknown (8): * (304) (IN), TLS handshake, Request CERT (13): * (304) (IN), TLS handshake, Certificate (11): * SSL certificate problem: self signed certificate * Closing connection curl: (60) SSL certificate problem: self signed certificate More details here: https://curl.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the web page mentioned above. * Host localhost:80 was resolved. * IPv6: ::1 * IPv4: 127.0.0.1 * Trying [::1]:80... * Connected to localhost (::1) port 80 * Server auth using Basic with user 'admin' > GET /es/ HTTP/1.1 > Host: localhost > Authorization: Basic YWRtaW46YWRtaW4= > User-Agent: curl/8.7.1 > Accept: */* > * Request completely sent off < HTTP/1.1 401 Unauthorized < Server: nginx/1.27.4 < Date: Thu, 24 Apr 2025 09:21:09 GMT < Content-Type: text/html < Content-Length: 179 < Connection: keep-alive * Authentication problem. Ignoring this. < WWW-Authenticate: Basic realm="Restricted Area" < <html> <head ><title>401 Authorization Required</title></head> <body> <center><h1>401 Authorization Required</h1></center> <hr><center>nginx/1.27.4</center> </body> </html> * Connection ❰xu❙~/OrbStack/docker/containers/secure-nginx/etc/nginx❱✔≻ curl -v https://192.168.5.171:9201/ (base) 17:21:09 curl -v -u "admin:admin123" http://localhost/es/ * Trying 192.168.5.171:9201... * Connected to 192.168.5.171 (192.168.5.171) port 9201 * ALPN: curl offers h2,http/1.1 * (304) (OUT), TLS handshake, Client hello (1): * CAfile: /etc/ssl/cert.pem * CApath: none * (304) (IN), TLS handshake, Server hello (2): * (304) (IN), TLS handshake, Unknown (8): * (304) (IN), TLS handshake, Request CERT (13): * (304) (IN), TLS handshake, Certificate (11): * SSL certificate problem: self signed certificate * Closing connection curl: (60) SSL certificate problem: self signed certificate More details here: https://curl.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the web page mentioned above. * Host localhost:80 was resolved. * IPv6: ::1 * IPv4: 127.0.0.1 * Trying [::1]:80... * Connected to localhost (::1) port 80 * Server auth using Basic with user 'admin' > GET /es/ HTTP/1.1 > Host: localhost > Authorization: Basic YWRtaW46YWRtaW4xMjM= > User-Agent: curl/8.7.1 > Accept: */* > * Request completely sent off < HTTP/1.1 401 Unauthorized < Server: nginx/1.27.4 < Date: Thu, 24 Apr 2025 09:21:16 GMT < Content-Type: application/json; charset=UTF-8 < Content-Length: 381 < Connection: keep-alive * Authentication problem. Ignoring this. < WWW-Authenticate: Basic realm="Security" charset="UTF-8" < * Connection {"error" :{"root_cause" :[{"type" :"security_exception" ,"reason" :"Missing authentication information for REST request [/]" ,"header" :{"WWW-Authenticate" :"Basic realm=\"Security\" charset=\"UTF-8\"" }}],"type" :"security_exception" ,"reason" :"Missing authentication information for REST request [/]" ,"header" :{"WWW-Authenticate" :"Basic realm=\"Security\" charset=\"UTF-8\"" }},"status" :401}⏎
场景编号
Nginx 是否开启认证
EasySearch 是否开启认证
实际认证次数
说明
①
否
否
0 次
完全开放,任何请求无需验证。
②
否
✅ 是
1 次
访问时直接弹出 EasySearch 的认证窗口,用户需输入凭证。
③
✅ 是
否
1 次
仅在 Nginx 层验证,验证通过后直接访问后端。
④
✅ 是
✅ 是
2 次(默认)
Nginx 和 EasySearch 各自认证,用户需连续输入两次密码。
⑤
✅ 是
✅ 是
1 次(透传,proxy_pass_request_headers on;)
Nginx 开启认证,并通过 proxy_set_header Authorization
透传给 EasySearch,用户仅需输入一次密码即可完成认证。