You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法在Hostinger配置WhatsApp Cloud API Webhook URL求助

WhatsApp Cloud API Webhook 部署到Hostinger(巴西服务器)验证失败问题

我本地用Node.js配置WhatsApp Cloud API的Webhook完全正常,在localhost的8000、3000端口测试都没问题,但部署到巴西Hostinger服务器后,提交的Webhook URL始终不被Meta认可。我已经指定用标准80端口,现在搞不清是代码问题还是Hostinger的配置没做好,也不确定服务器端是不是需要额外的PHP/HTML文件,求配置指导和排查方案。

我的配置文件

.env文件

WEBHOOK_VERIFY_TOKEN = "123"
GRAPH_API_TOKEN = "EAAQUA14XZBPwBO7M7sqU46sO7eSbVjYQAGJiD9rqOnOhTLIUyPPZBT2SZAbJLzOPUIGZAblwp5TiwWsPe1AgxuGtiZAJlumDrZBXH7ROct5IDZCOmJXji8OWUnfQPFrmmI3LTCkPjJygkmyx2MmtPXKp9KFcAGVRoLErlMKQl2JbFBjAi8KDyDe2rgQ9ouGd37PAozBALTQ1B48jpKYbZBUZD"   # token temporary
PORT = 80

index.js文件

// ------ IMPORTANDO MODULOS
const express = require("express");         // framework para aplicativos web do Node.js
const body_parser = require("body-parser"); // middleware para analisar corpos de solicitação em middleware antes de seus manipuladores
const axios = require("axios");             // biblioteca HTTP cliente para fazer requisições HTTP
require('dotenv').config();                 // módulo para carregar variáveis de ambiente do arquivo .env


// configuracao do Express. Criamos uma instância do aplicativo Express e usamos o body_parser.json() para analisar os corpos das solicitações como JSON
const app = express().use(body_parser.json());


const token = process.env.GRAPH_API_TOKEN;   //temporary access token dashboard api wpp
const mytoken = process.env.WEBHOOK_VERIFY_TOKEN;


// inicia o servidor Express na porta 8000 ou na porta definida em process.env.PORT

app.listen(process.env.PORT,()=>{
  console.log("webhook is listening...");
})


app.get("/",(req,res) => {
  res.status(200).send("hello this is webhook setup");
});


// é definida uma rota GET para a URL /webhook que será usada para verificação e configuração do webhook do Facebook
// para verificar a url de retorno no painel de apps (cloud api)
app.get("/webhook", (req, res) =>{
  let mode = req.query["hub.mode"];
  let token = req.query["hub.verify_token"];
  let challenge = req.query["hub.challenge"];  

  // check the mode and token sent are correct
  if (mode && token) {
    if(mode === "subscribe" && token === mytoken){
      // respond with 200 OK and challenge token from the request
      res.status(200).send(challenge);
      console.log("Webhook verified successfully!");
    }else {
      // respond with '403 Forbidden' if verify tokens do not match
      res.sendStatus(403);
    }
  } 
});


// trata as solicitações POST recebidas no endpoint /webhook, onde as mensagens recebidas do WhatsApp serão processadas e respondidas
app.post("/webhook",(req,res) => {
  let body_param = req.body;
  console.log(JSON.stringify(body_param,null,2)); // obtem a resposta e coloca no console

  // https://developers.facebook.com/docs/whatsapp/cloud-api/webhooks/payload-examples
  if(body_param.object){
    if(body_param.entry &&  // Verifica se a propriedade entry existe em body_param e se não é null ou undefined
      body_param.entry[0].changes &&  // Verifica se o primeiro elemento do array entry possui a propriedade changes e se não é null ou undefine
      body_param.entry[0].changes[0].value.message && // Verifica se o primeiro elemento do array changes possui a propriedade value com uma subpropriedade message e se não é null ou undefined
      body_param.entry[0].changes[0].value.message[0] // Verifica se o primeiro elemento do array message existe.
    ){
      let phone_no_id = body_param.entry[0].changes[0].value.metadata.phone_number_id;
      let from =        body_param.entry[0].changes[0].value.messages[0].from;
      let msg_body =    body_param.entry[0].changes[0].value.messages[0].text.body;
    
      axios({
        method: "POST",
        url: "https://graph.facebook.com/v19.0/"+phone_no_id+"/messages?access_token="+token, 
        data: {
          messaging_product: "whatsapp",
          to: from,
          text:{
            body: "Hi..."
          }
        },
        headers:{
          "Content-Type": "application/json"
        }
      })

      res.sendStatus(200);
    }else{
      res.sendStatus(404);
    }
  }
});

我尝试过将URL填入Webhook的“user”字段,但仍未被认可。


排查与解决步骤

1. 服务器端口与权限检查

  • Hostinger共享主机默认可能限制80端口直接绑定,因为共享环境通常用Apache/Nginx做反向代理:
    • 优先考虑用Hostinger面板配置反向代理,将你的域名指向Node.js服务使用的非80端口(比如3000),避免端口权限问题
    • 如果必须用80端口,确认Node.js进程有绑定低端口的权限:Linux下非root用户可执行sudo setcap 'cap_net_bind_service=+ep' $(which node)赋予权限,或用端口转发工具
  • 检查Hostinger防火墙规则,确保入站的80端口请求被允许

2. Webhook URL格式验证

  • Meta要求Webhook必须是公网可访问的HTTPS地址:
    • 必须使用完整的HTTPS URL,比如https://你的域名.com/webhook,不能用HTTP或纯IP地址
    • Hostinger共享主机默认提供免费SSL证书,确保你的域名已启用HTTPS
    • 测试URL是否能被公网访问:用在线工具检查你的/webhook端点是否可达

3. 代码逻辑修复

  • 你的/webhook GET端点存在逻辑漏洞:当mode或token参数缺失时,没有返回状态码,Meta会判定验证失败,补充如下代码:
    app.get("/webhook", (req, res) =>{
      let mode = req.query["hub.mode"];
      let token = req.query["hub.verify_token"];
      let challenge = req.query["hub.challenge"];  
    
      if (mode && token) {
        if(mode === "subscribe" && token === mytoken){
          res.status(200).send(challenge);
          console.log("Webhook verified successfully!");
        }else {
          res.sendStatus(403);
        }
      } else {
        // 补充:参数缺失时返回403
        res.sendStatus(403);
      }
    });
    
  • 确认process.env.PORT是否正确读取:在服务器启动时添加console.log("Running on port:", process.env.PORT),检查端口是否为80

4. Hostinger环境配置

  • 无需额外PHP/HTML文件,Node.js服务可独立运行,但需注意:
    • 用PM2或Hostinger自带的Node.js管理器后台运行进程,避免终端关闭后服务停止
    • 确保服务器Node.js版本与本地一致(推荐v16及以上),避免语法兼容问题

5. 本地模拟验证测试

  • 在服务器上执行curl命令,模拟Meta的验证请求:
    curl "https://你的域名.com/webhook?hub.mode=subscribe&hub.verify_token=123&hub.challenge=123456"
    
    • 若返回123456,说明验证逻辑正常;若返回403或其他错误,优先排查代码或服务器路由配置

内容的提问来源于stack exchange,提问作者Mares

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 14:04:54