Bloquear o acesso com base na localização do IP

Autor:Lisa Farrell · 2026-01-18

Solução de controle de acesso no frontend baseada na localização do IP: quando a página carrega, ela faz uma requisição para https://my.ipin.io/info para obter o código do país do visitante (country). Quando o country é identificado como TW (ou outras regiões que você configurar), a página deixa de fornecer o conteúdo normal e encerra diretamente o fluxo de acesso no frontend.

Muitas demandas de “restringir acesso por região” na verdade não são para criar segurança forte (do tipo anti-hacker), e sim para cenários comuns de frontend:

  • Em alguns países/regiões o serviço ainda não é oferecido (por conformidade, direitos autorais, pagamentos, logística etc.)
  • Fazer uma verificação de região assim que o usuário entra e então decidir se deve exibir conteúdo/botões/links de download
  • Campanhas operacionais/marketing são voltadas apenas a regiões específicas; outras regiões precisam receber diretamente a mensagem “fora da área de serviço” ou “indisponível”

Esta abordagem é especialmente adequada para o frontend: o navegador faz GET https://my.ipin.io/info diretamente, e a API retorna informações geográficas com base no “IP que iniciou a requisição”, por exemplo:

{"ip":"185.220.236.7","country":"TW","region":"Taiwan","city":"Taipei"}

Depois disso, basta verificar `country === "TW"` para executar a ação de “bloquear acesso” (por exemplo: exibir um aviso de acesso negado, mostrar um texto de 404, ou sobrescrever a página original com a sua própria estrutura de página de erro). O ponto-chave aqui é: não discutimos “redirecionamento”; em vez disso, apresentamos o resultado do acesso de forma clara como erro ou recusa, para que o usuário veja o estado final “não acessível”.

1) Lógica e princípio de funcionamento (2 parágrafos)

Parágrafo 1: Fluxo
O usuário abre a página → o navegador requisita imediatamente https://my.ipin.io/info → obtém `country` → se corresponder à lista de bloqueio (por exemplo `TW`), bloqueia imediatamente a exibição subsequente da página: não renderiza mais o conteúdo normal e exibe diretamente um estado final como “acesso negado / 404 / erro personalizado”; caso contrário, continua carregando normalmente as funções e o conteúdo.

Parágrafo 2: Por que o frontend é adequado para essa API
Como o navegador chama diretamente uma API de terceiros, o terceiro vê o “IP público” do usuário, e o país/região retornado corresponde ao próprio visitante — exatamente o efeito que você deseja.

2) Implementação de código (o mais simples possível, com foco no frontend)

2.1 O mais simples: bateu, “acesso negado” (recomendado)

Coloque este trecho no <head> (quanto mais cedo, melhor). Ao corresponder, ele sobrescreve o conteúdo e mostra uma mensagem clara de “Acesso negado”. Você pode substituir o texto pelo seu aviso de conformidade ou de escopo do serviço.

Exemplo A: bloquear se country === "TW"

<script>
(async function () {
  try {
    const info = await fetch("https://my.ipin.io/info").then(r => r.json());
    if (info.country === "TW") {
      document.documentElement.innerHTML =
        "<head><title>403 Forbidden</title></head>" +
        "<body style='font-family:system-ui;padding:40px'>" +
        "<h1>403 Acesso negado</h1>" +
        "<p>O serviço não está disponível na sua região.</p>" +
        "</body>";
    }
  } catch (e) {
    // Se falhar, permitir por padrão (você pode mudar para bloquear por padrão)
  }
})();
</script>

Exemplo B: bloquear se NÃO for TW (permitir apenas TW)

<script>
(async function () {
  try {
    const info = await fetch("https://my.ipin.io/info").then(r => r.json());
    if (info.country !== "TW") {
      document.documentElement.innerHTML =
        "<head><title>403 Forbidden</title></head>" +
        "<body style='font-family:system-ui;padding:40px'>" +
        "<h1>403 Acesso negado</h1>" +
        "<p>Esta página está disponível apenas em regiões selecionadas.</p>" +
        "</body>";
    }
  } catch (e) {
    // Se falhar, permitir por padrão (você pode mudar para bloquear por padrão)
  }
})();
</script>

Exemplo C: bloquear se TW, US... (lista de bloqueio)

<script>
(async function () {
  const blocked = ["TW", "US"]; // Bloquear se corresponder a qualquer item
  try {
    const info = await fetch("https://my.ipin.io/info").then(r => r.json());
    if (blocked.includes(info.country)) {
      document.documentElement.innerHTML =
        "<head><title>403 Forbidden</title></head>" +
        "<body style='font-family:system-ui;padding:40px'>" +
        "<h1>403 Acesso negado</h1>" +
        "<p>O serviço não está disponível na sua região.</p>" +
        "</body>";
    }
  } catch (e) {}
})();
</script>

2.2 Exibir 404 Not Found (forma de apresentar “indisponível/não existe”)

Alguns produtos preferem que, em certas regiões, pareça que “a página não existe”, em vez de indicar explicitamente “restrito”. Nesse caso, você pode sobrescrever o conteúdo com uma estrutura de página de erro com semântica de 404. Observação: isso ainda é uma apresentação no frontend e não altera de fato o código de status HTTP retornado pelo servidor, mas para a experiência de acesso à página costuma ser suficientemente claro.

<script>
(async function () {
  try {
    const { country } = await fetch("https://my.ipin.io/info").then(r => r.json());
    if (country === "TW") {
      document.documentElement.innerHTML =
        "<head><title>404 Not Found</title></head>" +
        "<body style='font-family:system-ui;padding:40px'>" +
        "<h1>404 Not Found</h1>" +
        "<p>The requested URL was not found on this server.</p>" +
        "</body>";
    }
  } catch (e) {}
})();
</script>

2.3 Lista de bloqueio como array (mais próximo da vida real)

Na prática, normalmente não se bloqueia apenas uma região; mantém-se uma lista. O exemplo abaixo continua bem simples: ao corresponder qualquer item, exibe imediatamente “acesso negado”.

<script>
(async function () {
  const blocked = ["TW"]; // Códigos de país/região a bloquear
  try {
    const info = await fetch("https://my.ipin.io/info").then(r => r.json());
    if (blocked.includes(info.country)) {
      document.documentElement.innerHTML =
        "<head><title>403 Forbidden</title></head>" +
        "<body style='font-family:system-ui;padding:40px'>" +
        "<h1>403 Acesso negado</h1>" +
        "<p>O serviço não está disponível na sua região.</p>" +
        "</body>";
    }
  } catch (e) {}
})();
</script>

2.4 React (o mais curto utilizável: ao corresponder, mostrar página de erro)

import { useEffect, useState } from "react";

export default function App() {
  const [blocked, setBlocked] = useState(false);

  useEffect(() => {
    (async () => {
      try {
        const info = await fetch("https://my.ipin.io/info").then(r => r.json());
        if (info.country === "TW") setBlocked(true);
      } catch (e) {}
    })();
  }, []);

  if (blocked) {
    return (
      <div style={{ fontFamily: "system-ui", padding: 40 }}>
        <h1>403 Acesso negado</h1>
        <p>O serviço não está disponível na sua região.</p>
      </div>
    );
  }

  return <div>...</div>;
}

2.5 Vue (também o mais curto: ao corresponder, mostrar conteúdo de erro)

<script>
export default {
  data() {
    return { blocked: false };
  },
  async mounted() {
    try {
      const info = await fetch("https://my.ipin.io/info").then(r => r.json());
      if (info.country === "TW") this.blocked = true;
    } catch (e) {}
  }
}
</script>

<template>
  <div v-if="blocked" style="font-family:system-ui;padding:40px">
    <h1>403 Acesso negado</h1>
    <p>O serviço não está disponível na sua região.</p>
  </div>

  <div v-else>...</div>
</template>

3) Implementação em WordPress / Joomla / Magento / Shopify (abordagem frontend + código)

A lógica é a mesma em todos: inserir um “JS super simples” no Head global do site (ou no arquivo de layout). Como o objetivo é “controle no frontend + apresentação de erro”, basta garantir que o script rode cedo o suficiente para concluir a verificação antes do corpo da página renderizar; em caso de correspondência, exibir diretamente o estado final “acesso negado/404/mensagem de erro”.

Recomendo usar este snippet de forma unificada (versão mais simples de “acesso negado”):

<script>
(async function () {
  try {
    const info = await fetch("https://my.ipin.io/info").then(r => r.json());
    if (info.country === "TW") {
      document.documentElement.innerHTML =
        "<head><title>403 Forbidden</title></head>" +
        "<body style='font-family:system-ui;padding:40px'>" +
        "<h1>403 Acesso negado</h1>" +
        "<p>O serviço não está disponível na sua região.</p>" +
        "</body>";
    }
  } catch (e) {}
})();
</script>

3.1 WordPress

  • Opção A (mais fácil): usar um plugin de inserção de scripts no header/footer e colar no Header (site inteiro)
  • Opção B: inserir o script antes de </head> no arquivo do tema `header.php`

3.2 Joomla! / Joomla

  • No template atual, encontrar <head> em `index.php` e colar antes de </head>
  • Ou usar a área do backend/template para adicionar código/HTML personalizado no head (varia por template)

3.3 Magento (abordagem de injeção no frontend)

  • Se a intenção é apenas “bloquear na camada de apresentação”, adicione o script ao head global do tema (área head em theme/layout)
  • No painel do Magento, muitas vezes existe uma configuração semelhante a “HTML Head” (varia por versão/tema) com o objetivo de injetar conteúdo no <head> do site inteiro

> Como seu objetivo é “evitar código do lado do servidor ao máximo”, aqui não expandimos XML/módulos — se você consegue inserir o script no head, ele funciona.

3.4 Shopify

  • Online Store → Themes → Edit code
  • Encontrar `theme.liquid` e colar o script antes de </head> (site inteiro)

3.5 Outros sistemas comuns (padrão geral)

  • Qualquer sistema que permita editar o <head> global: colocar o script o mais cedo possível no head
  • Se quiser restringir apenas algumas páginas: inserir o script somente nos templates dessas páginas

4) Conclusão

O núcleo deste artigo pode ser resumido em uma frase:

> O navegador chama https://my.ipin.io/info para obter { country }. Se country === "TW", o acesso à página é encerrado diretamente exibindo “acesso negado / 404 / erro personalizado”, implementando assim uma solução frontend para bloquear acesso por região de IP.

É barato e rápido de implementar e funciona bem para “aviso de conformidade / aviso de indisponibilidade por região / gating no frontend”. Em muitos casos, a “apresentação de erro” no frontend já atende às necessidades de produto e operação: o usuário entende claramente que a página está indisponível na região dele, e o conteúdo principal e os pontos de entrada deixam de ser expostos. Se você precisar de um bloqueio mais rigoroso “difícil de burlar”, adicione uma camada no servidor ou no CDN.

5) Perguntas frequentes (FAQ)

Q1: O bloqueio no frontend realmente consegue “negar acesso”?

Mais precisamente, ele permite que a camada de apresentação mostre um resultado de recusa claro (por exemplo, uma página 403/404/mensagem de erro), impedindo que usuários “normais” continuem usando as funções. Porém, pessoas com conhecimento podem desativar JavaScript ou alterar scripts, então não é uma barreira de segurança impossível de contornar. Se você precisa de “proibição absoluta”, faça também no servidor ou no CDN. Se o objetivo é aviso de conformidade e indisponibilidade, o bloqueio no frontend costuma ser suficiente.

Q2: Por que colocar o script no <head> e o mais cedo possível?

Quanto mais cedo a verificação acontecer, maior a chance de encerrar o fluxo antes do conteúdo principal ser renderizado, reduzindo o “flash” de ver a página rapidamente antes do bloqueio. Além disso, evita carregar recursos desnecessários (scripts, imagens, componentes). É o local mais racional para experiência e desempenho.

Q3: E se a requisição a https://my.ipin.io/info falhar?

A estratégia mais comum é “falhou, libera por padrão”, para não bloquear usuários normais por causa de uma falha de API externa. Outra estratégia é “falhou, bloqueia por padrão”, apropriada para cenários de conformidade rígida. Você pode escolher com base no risco do negócio. Em qualquer caso, mantenha a lógica simples: obteve country, verifica; não obteve, aplica a política padrão.

Q4: Pode haver problema de CORS?

Pode. Se a API não permitir acesso cross-origin do navegador, o fetch será bloqueado. Nesse caso, você precisa garantir que a API habilite CORS (por exemplo, permitindo seu domínio), caso contrário o navegador não conseguirá ler o JSON retornado. Para uma abordagem “frontend-first”, CORS é o principal ponto a validar previamente.

Q5: Qual padrão segue o valor country?

Normalmente é um código de país/região de duas letras (comumente ISO 3166-1 alpha-2). TW é um exemplo típico. Em produção, recomenda-se padronizar em letras maiúsculas e manter a lista de bloqueio no mesmo formato para evitar falhas por diferença de caixa.

Q6: Como mudar de “bloquear um país” para “permitir apenas alguns países”?

Basta inverter a lógica:
- allowlist: permitir apenas ["US","JP"] e para o restante mostrar “acesso negado/erro”
- blocklist: bloquear apenas ["TW"] e permitir o resto
Ambos são essencialmente checar se country está em uma lista; a diferença é o comportamento padrão.