🚀 Nginx Proxy Manager集成Cloudflare Turnstile全攻略
虽然NPM不原生支持Turnstile验证,但通过 自定义代码注入 + 访问控制规则 的组合方案,可实现无缝集成。以下是详细步骤:


一、基础环境准备

  1. 启用NPM高级模式

    • 登录NPM面板 → 点击右上角「齿轮」图标 → 勾选「Enable Advanced Configuration」
  2. 安装Lua支持(关键步骤)
    在宿主机执行:

    # 进入NPM容器
    docker exec -it nginx-proxy-manager bash
    
    # 安装LuaJIT及依赖
    apt update && apt install -y libluajit-5.1-2 lua-cjson
    
    # 验证模块加载
    nginx -V 2>&1 | grep -o with-http_lua_module  # 应输出"with-http_lua_module"
    

二、分场景配置方案

场景1:保护登录页面(/admin)

  1. 创建代理主机

    • 域名:admin.yourdomain.com
    • 目标URL:Halo后台地址(如http://halo:8090/admin
  2. 添加高级配置
    在「Advanced」标签页填入:

    location / {
        access_by_lua_block {
            local http = require "resty.http"
            local cjson = require "cjson"
    
            -- 仅验证POST请求
            if ngx.req.get_method() == "POST" then
                ngx.req.read_body()
                local args = ngx.req.get_post_args()
                local token = args.cf_turnstile_token
    
                if not token then
                    ngx.exit(ngx.HTTP_FORBIDDEN)
                end
    
                local httpc = http.new()
                local res, err = httpc:request_uri("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
                    method = "POST",
                    body = "secret=YOUR_SECRET_KEY&response="..token,
                    headers = {["Content-Type"] = "application/x-www-form-urlencoded"}
                })
    
                if not res or res.status ~= 200 then
                    ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
                end
    
                local data = cjson.decode(res.body)
                if not data.success then
                    ngx.exit(ngx.HTTP_FORBIDDEN)
                end
            end
        }
    
        proxy_pass http://halo_backend;
    }
    

场景2:保护评论提交接口(/api/comments)

  1. 创建访问控制规则

    • 路径:/api/comments
    • 策略:Custom
  2. 注入验证脚本

    set $turnstile_sitekey "YOUR_SITE_KEY";
    
    location /api/comments {
        access_by_lua_file /app/lua/turnstile_verify.lua;
        proxy_pass http://halo_backend;
    }
    

    创建/app/lua/turnstile_verify.lua

    ngx.header.Content_Type = "application/json"
    
    local token = ngx.req.get_headers()["X-Turnstile-Token"]
    if not token then
        ngx.status = ngx.HTTP_FORBIDDEN
        ngx.say('{"error":"missing_token"}')
        return ngx.exit(ngx.HTTP_FORBIDDEN)
    end
    
    -- 后续验证逻辑与场景1相同
    

三、前端适配方案

方法A:修改Halo主题

  1. 在评论表单中添加:
    <div class="cf-turnstile" 
         data-sitekey="YOUR_SITE_KEY" 
         data-callback="onVerify"></div>
    
    <script>
    function onVerify(token) {
        document.getElementById('comment-form').appendChild(
            Object.assign(document.createElement('input'), {
                type: 'hidden',
                name: 'cf_turnstile_token',
                value: token
            })
        );
    }
    </script>
    

方法B:通过NPM插入全局脚本

  1. 在NPM的「Custom Locations」中添加:
    Location: /*
    Content: 
    <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
    

四、性能优化配置

  1. 启用缓存
    在NPM容器的/etc/nginx/nginx.conf中添加:

    lua_shared_dict turnstile_cache 10m;
    
  2. 调整超时时间
    修改高级配置:

    proxy_read_timeout 300s;
    lua_socket_connect_timeout 5s;
    lua_socket_send_timeout 5s;
    lua_socket_read_timeout 5s;
    

五、监控与调试

  1. 实时日志查看

    docker logs -f nginx-proxy-manager --tail 100 | grep 'turnstile'
    
  2. 生成验证统计报表
    使用GoAccess分析:

    docker run --rm -it -v /path/to/access.log:/logs/log.goaccess goaccess/goaccess \
           -f /logs/log.goaccess \
           --log-format='%h %^[%d:%t %^] "%r" %s %b "%R" "%u"' \
           --date-format=%d/%b/%Y \
           --time-format=%T
    

⚠️ 注意事项

  • 修改容器内配置后需重启NPM:docker restart nginx-proxy-manager
  • 每次NPM面板升级可能覆盖自定义配置,建议备份/data/nginx/custom目录
  • 遇到Lua模块加载失败时,检查容器内/etc/nginx/nginx.conf是否包含load_module modules/ndk_http_module.so;

📌 替代方案:若不愿修改容器,可:

  1. 在Cloudflare设置页面规则,对特定路径启用Turnstile
  2. 使用Cloudflare Workers作为前置验证层