Como funciona esta verificação
O Scanaris analisa o HTML da tua página principal e percorre todos os elementos com atributo src, href ou action: scripts, imagens, folhas de estilo, ligações, formulários. Para cada valor verifica se aponta para um host que não devia existir fora da tua máquina ou da tua rede: localhost, 127.0.0.1, 0.0.0.0, os IP privados 10.x.x.x, 192.168.x.x e 172.16.x.x a 172.31.x.x, e qualquer domínio terminado em .local. Se encontrar algum, abre um achado de severidade baixa com as primeiras referências. Só se examina o HTML inicial; o que o JavaScript construir depois não é visto por esta via.
Porque importa
Uma referência a http://localhost:3000/api/... em produção tem duas consequências. A primeira é funcional: para o teu utilizador, localhost é o seu próprio computador, por isso essa imagem não carrega, esse formulário é enviado para lado nenhum e esse script falha em silêncio. A segunda é de informação: estás a contar a qualquer um que a tua API corre na porta 3000, que o teu backend interno se chama api.empresa.local ou que a tua rede usa a gama 10.0.1.x. Nada disso é uma falha explorável por si só, daí a severidade baixa, mas são peças que um atacante guarda para depois, e ainda denunciam que o deploy foi feito sem revisão.
Porque acontece
É quase sempre um URL escrito à mão durante o desenvolvimento que ninguém mudou. A IA que gerou o projeto pôs http://localhost:8000 no fetch do formulário, ou no src de uma imagem de teste, e funcionou localmente, por isso ficou. Outras causas: um .env de desenvolvimento que acabou no build de produção, um WordPress migrado de um ambiente local com o URL do site por atualizar, ou um action de formulário copiado de um exemplo.
Como corrigir
- Tira os URL do código. A base da tua API vai numa variável de ambiente (
NUXT_PUBLIC_API_BASE,NEXT_PUBLIC_API_URL,VITE_API_URL) com um valor diferente por ambiente. Em produção, a plataforma (Vercel, Netlify, Railway) injeta-a no build. - Usa caminhos relativos sempre que o recurso viva no teu próprio domínio:
/api/contactoem vez dehttp://localhost:3000/api/contacto. Funcionam igual localmente e em produção. - WordPress: atualiza os URL do site nas Definições gerais e substitui os URL antigos na base de dados com uma ferramenta que respeite a serialização.
- Procura no build antes de fazer deploy. Um passo de CI que falhe se o resultado compilado contiver estes hosts corta o problema pela raiz:
grep -rEl "localhost|127\.0\.0\.1|192\.168\.|\.local\b" dist/ .output/public/ 2>/dev/null
Se sair algum ficheiro, há uma referência a corrigir antes de publicar.
Confirma que está corrigido
curl -s https://teudominio.com | grep -oE '(src|href|action)="https?://[^"]+"' | grep -iE "localhost|127\.0\.0\.1|0\.0\.0\.0|10\.[0-9]+\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|\.local\b"
Não deve sair nada. Testa também uma ou duas páginas interiores, sobretudo as que têm formulários.
Perguntas frequentes
É uma imagem de teste que ninguém vê. Importa?
O browser de cada visitante tenta carregá-la a partir do seu próprio computador, e o HTML continua a anunciar o teu host interno. Muda-a ou apaga-a; é um minuto.
O JavaScript compilado também é revisto?
Esta verificação não, só o HTML. Mas um URL interno no bundle costuma vir acompanhado de outras coisas que não deviam estar lá; segredos em JavaScript procura chaves e tokens, e comentários HTML procura restos no próprio HTML. As três juntas cobrem o que costuma sobrar de um ambiente de desenvolvimento.
A minha ligação aponta para um domínio .local de propósito.
Os domínios .local estão reservados para redes locais (mDNS) e não resolvem na internet. Se o que queres é ligar a uma ferramenta interna, usa um subdomínio real com autenticação. O que a tua app deixa fugir sem querer, com mais exemplos, está em segredos que a tua app deixa fugir no browser.