Node Unblocker: o que é e como configurar com segurança

Publicado:

16 min de leitura

Acar Diveroli
Autor: Acar Diveroli
Uma linha sai de uma janela BROWSER com /proxy/https://, passa por uma elipse pontilhada /proxy/ e chega a uma página TARGET

Você inicia um pequeno app Express, abre http://127.0.0.1:8080/proxy/https://example.com/ no navegador, e a página aparece no endereço do seu próprio servidor. Os links, as imagens e os formulários dela agora começam com /proxy/, então cada clique volta a passar pelo seu servidor. Esse é o Node Unblocker, o pacote unblocker do npm: algumas linhas que transformam um servidor Node.js em um proxy web que qualquer pessoa pode usar com um navegador comum. Se ficar aberto, essas mesmas linhas deixam estranhos usarem o seu servidor também; a nossa primeira instância de teste buscou uma página de administração que um serviço local só mostrava para 127.0.0.1.

Este artigo mostra como ele funciona, um servidor mínimo, um servidor reforçado que barra estranhos e endereços internos, um proxy upstream para o tráfego de saída e os limites do pacote. Todos os exemplos rodaram em 11 de outubro de 2026 com o unblocker 2.3.1, o Express 4.22.3 e o Node.js 24.11.1, contra um site de teste local, um servidor de eco WebSocket e um proxy de teste que exige usuário e senha.

O que é o Node Unblocker?

O Node Unblocker é uma biblioteca de código aberto para Node.js criada por Nathan Friedly e publicada no npm como unblocker. Em 11 de outubro de 2026, a versão mais recente era a 2.3.1, de junho de 2024; ela exige Node.js 16.17 ou mais novo e usa a licença AGPL-3.0. O README no GitHub a descreve como uma biblioteca de uso geral para carregar páginas web remotas por um proxy e reescrevê-las.

É um proxy web: um site que carrega outros sites para você. O visitante não muda nenhuma configuração e só precisa do endereço do seu servidor. Um proxy HTTP ou SOCKS5, por outro lado, é configurado em um app ou no sistema operacional e repassa o tráfego sem alterações. O Node Unblocker lida só com páginas web e WebSockets, e tem uma única saída: o IP da máquina em que roda. Se ele rodar no seu notebook, o site vê o IP da sua conexão de casa.

Como o Node Unblocker funciona?

Toda requisição cujo caminho começa com o prefixo (/proxy/ por padrão) passa por estas etapas:

  1. O navegador pede uma URL do proxy, como GET /proxy/https://example.com/. Tudo o que vem depois do prefixo é o destino. Uma URL sem esquema, como /proxy/example.com, recebe um 307 para /proxy/http://example.com/.
  2. O middleware de requisição roda. Funções embutidas ajustam os cabeçalhos Host e Referer, a compressão e os cookies. Em seguida rodam as suas próprias funções de requestMiddleware; as verificações de acesso ficam aqui.
  3. O seu servidor se conecta ao destino com os cabeçalhos do navegador, por meio de httpAgent ou httpsAgent, se você tiver definido um. O destino vê o IP do seu servidor.
  4. O middleware de resposta edita a resposta. Ele remove os cabeçalhos Content-Security-Policy, Strict-Transport-Security e Public-Key-Pins, reescreve redirecionamentos e URLs no HTML e no CSS, move os cookies para o caminho /proxy/<site>/, injeta um script e adiciona uma tag robots NOINDEX, NOFOLLOW.
  5. A página volta em streaming, pedaço por pedaço.
  6. Links perdidos são recuperados. Uma requisição relativa à raiz, como /about, chega sem o prefixo; o Node Unblocker lê o Referer e responde com um 307 para a URL completa do proxy.

O script injetado envolve fetch, XMLHttpRequest, WebSocket, elementos novos e mudanças no histórico, de modo que endereços montados por JavaScript também recebem o prefixo. Os próprios arquivos JavaScript não são reescritos. A nossa página de teste, com um link absoluto, um relativo à raiz e um relativo, uma imagem relativa ao protocolo e um formulário, voltou assim:

text
set-cookie: session=abc123; Path=/proxy/http://127.0.0.1:9000/; HttpOnly

<script src="/proxy/client/unblocker-client.js"></script>
<script>unblockerInit({"prefix":"/proxy/","url":"http://127.0.0.1:9000/"}, window);</script>
<title>Test page</title><meta name="ROBOTS" content="NOINDEX, NOFOLLOW"/>
<a href="/proxy/https://www.iana.org/help/example-domains">absolute</a>
<a href="/proxy/http://127.0.0.1:9000/about">root-relative</a>
<a href="contact">relative</a>
<img src="/proxy/http://cdn.example.net/logo.png">
<form action="/proxy/http://127.0.0.1:9000/login" method="post"></form>

O link relativo já aponta para dentro do caminho do proxy, então fica como está. Os cabeçalhos Content-Security-Policy e Strict-Transport-Security da página nunca chegaram ao navegador.

Qual é a diferença para um forward proxy ou um proxy reverso?

Os três ficam entre um navegador e um site. Eles diferem em quem os configura e em se alteram ou não a página.

Node UnblockerForward proxyProxy reverso
Como é usadoAbrir uma URL /proxy/Definir host:port no app ou no sistemaAcessar o endereço do próprio site
Quem configuraQuem mantém o servidorO usuário ou a empresa deleO dono do site
Altera a páginaSim: links, cookies, redirecionamentosNãoNormalmente não
HTTPSDescriptografado no seu servidorVai por túnel, continua criptografadoDescriptografado pelo dono do site
TráfegoPáginas web e WebSocketsQualquer HTTP; SOCKS5 também UDPHTTP(S) para os servidores do site

Um forward proxy atende qualquer app que tenha uma configuração de proxy e nunca precisa entender a página. Um proxy reverso (reverse proxy) como o nginx fica na frente do seu próprio site; Forward proxy e reverse proxy: qual é a diferença? explica os dois. O Node Unblocker é o caso à parte: um proxy que reescreve páginas para navegadores cujas configurações não podem ou não devem ser alteradas.

Como configurar um servidor mínimo do Node Unblocker?

Em uma pasta vazia, instale o Express e o pacote, depois salve o código como server.mjs:

bash
npm install express unblocker
js
import express from "express";
import Unblocker from "unblocker";

const app = express();
const unblocker = new Unblocker({ prefix: "/proxy/" });

// O Unblocker precisa ser uma das primeiras chamadas de app.use() e deve ficar na raiz, não em um subcaminho
app.use(unblocker);

app.get("/", (req, res) => res.send("Open /proxy/https://example.com/"));

// O listener de upgrade permite que o Unblocker repasse conexões WebSocket
app.listen(8080, "127.0.0.1").on("upgrade", unblocker.onUpgrade);

Rode node server.mjs e abra http://127.0.0.1:8080/proxy/https://example.com/. O README diz que o middleware não pode ficar em um subcaminho e avisa que usar / como prefixo causa casos extremos. Sem o listener de upgrade, chats e painéis ao vivo nas páginas que passam pelo proxy não conseguem se conectar.

O servidor escuta em 127.0.0.1, então só o seu computador consegue acessá-lo; mantenha assim. No nosso teste, ele carregou páginas, reescreveu um redirecionamento 302, repassou um POST de formulário e devolveu as mensagens WebSocket do servidor de eco. Ele também buscou /proxy/http://127.0.0.1:9000/admin, uma página que o nosso serviço de teste só mostra para a própria máquina:

text
INTERNAL ADMIN PAGE - only for 127.0.0.1

Express 5. O pacote depende do Express 4, então repetimos o teste no Express 5.3.0. Páginas, redirecionamentos, formulários e WebSockets funcionaram do mesmo jeito; o npm colocou uma cópia privada do Express 4 dentro do pacote.

Por que um servidor do Node Unblocker aberto é perigoso?

Um servidor aberto deixa qualquer pessoa mandar tráfego a partir do seu IP e chegar a lugares que só o seu servidor alcança:

  • O seu IP leva a culpa. Spam de formulário e tentativas de login por força bruta enviados por ele saem do seu endereço. Os sites bloqueiam esse endereço ou reclamam com a sua hospedagem, que pode suspender o servidor.
  • A sua rede interna fica ao alcance. Isso é server-side request forgery (SSRF), a falsificação de requisição do lado do servidor: alguém de fora faz o seu servidor pedir um endereço que essa pessoa não consegue alcançar sozinha. Em um servidor na nuvem, o alvo clássico é 169.254.169.254, onde o serviço de metadados entrega credenciais. O OWASP SSRF Prevention Cheat Sheet recomenda uma lista de destinos permitidos e a verificação do IP para o qual cada nome é resolvido.
  • O servidor lê tudo. O HTTPS termina no seu servidor, porque ele precisa ler as páginas para reescrevê-las; por isso, as senhas digitadas nos sites que passam pelo proxy atravessam o servidor em texto puro.
  • Todos os sites compartilham a mesma origem. Os sites que passam pelo proxy só são separados pelo caminho dos cookies, e o MDN observa que Path não é uma medida de segurança. Um script em uma dessas páginas pode ler os cookies de outro site que não tenham HttpOnly.

Um proxy web também não dá direitos novos a ninguém. Usá-lo para contornar o filtro de uma escola ou de um local de trabalho, ou os termos de um site, continua violando essas regras, e o robots.txt e os limites de taxa de um site valem para o seu servidor como para qualquer outro visitante.

Como proteger um servidor do Node Unblocker?

Trate-o como uma ferramenta interna para pessoas definidas e uma lista curta de sites. A versão reforçada acrescenta:

  1. Um login: um único login HTTP Basic para a equipe, comparado em tempo constante.
  2. Uma lista de hosts permitidos, só nas portas 80 e 443, verificada em requestMiddleware.
  3. Uma verificação de cada resposta de DNS no lookup dos agentes, porque qualquer domínio pode apontar para 127.0.0.1. O net.BlockList do Node guarda as faixas privadas e também reconhece endereços IPv4 escritos no formato IPv6.
  4. Remoção de cabeçalhos, para que o cabeçalho Authorization da equipe e o IP do cliente em X-Forwarded-For nunca cheguem ao site.
  5. Uma proteção para WebSocket, porque requisições de upgrade pulam o middleware do Express.
  6. Um limite de taxa, uma linha de log e uma página de erro simples.
bash
npm install express unblocker express-rate-limit

Depois, substitua o server.mjs por esta versão:

js
import crypto from "node:crypto";
import dns from "node:dns";
import http from "node:http";
import https from "node:https";
import net from "node:net";
import express from "express";
import { rateLimit } from "express-rate-limit";
import Unblocker from "unblocker";

const { UI_USER, UI_PASS, PORT = "8080" } = process.env;
if (!UI_USER || !UI_PASS) throw new Error("Set UI_USER and UI_PASS");
const ALLOWED = ["example.com", "iana.org"]; // sites que a sua equipe pode abrir (subdomínios incluídos)

// 1. Faixas de endereços às quais o proxy nunca deve se conectar (loopback, privadas, link-local, metadados da nuvem)
const blocked = new net.BlockList();
for (const [range, bits] of [["0.0.0.0", 8], ["10.0.0.0", 8], ["100.64.0.0", 10], ["127.0.0.0", 8],
  ["169.254.0.0", 16], ["172.16.0.0", 12], ["192.168.0.0", 16]]) blocked.addSubnet(range, bits);
blocked.addAddress("::1", "ipv6");
blocked.addSubnet("fc00::", 7, "ipv6");
blocked.addSubnet("fe80::", 10, "ipv6");
const isBlockedIp = (ip) => blocked.check(ip, net.isIPv6(ip) ? "ipv6" : "ipv4");

// 2. Verifica cada resposta de DNS antes de abrir um socket, para que um nome permitido não aponte para dentro da sua rede
function safeLookup(hostname, options, callback) {
  dns.lookup(hostname, options, (err, address, family) => {
    if (err) return callback(err);
    const answers = Array.isArray(address) ? address : [{ address, family }];
    if (answers.some((a) => isBlockedIp(a.address))) {
      return callback(new Error(`${hostname} resolves to a blocked address`));
    }
    callback(null, address, family);
  });
}

// 3. Só hosts da lista de permissões nas portas padrão 80/443
function isAllowed(target) {
  const { hostname, port } = new URL(target);
  return port === "" && ALLOWED.some((h) => hostname === h || hostname.endsWith(`.${h}`));
}

// Cabeçalhos que o site não pode ver: o login da equipe e o IP do cliente adicionado pelo proxy reverso
const STRIP = ["authorization", "forwarded", "x-forwarded-for", "x-forwarded-host", "x-forwarded-proto", "x-real-ip"];

function checkTarget(data) {
  const ok = isAllowed(data.url);
  console.log(new Date().toISOString(), ok ? "ALLOW" : "DENY", data.url);
  if (ok) {
    for (const h of STRIP) delete data.headers[h];
    return;
  }
  if (data.clientResponse) return data.clientResponse.status(403).send("This site is not on the allowlist.");
  throw new Error("Blocked WebSocket target"); // requisições de upgrade não têm objeto de resposta
}

// 4. Um login compartilhado para a equipe (HTTP Basic), comparado em tempo constante
const hash = (s) => crypto.createHash("sha256").update(s).digest();
function loginOk(req) {
  const [scheme, value = ""] = (req.headers.authorization ?? "").split(" ");
  const given = Buffer.from(value, "base64").toString();
  return scheme === "Basic" && crypto.timingSafeEqual(hash(given), hash(`${UI_USER}:${UI_PASS}`));
}

const unblocker = new Unblocker({
  prefix: "/proxy/",
  requestMiddleware: [checkTarget],
  httpAgent: new http.Agent({ lookup: safeLookup }),
  httpsAgent: new https.Agent({ lookup: safeLookup }),
});

const app = express();
app.set("trust proxy", "loopback"); // o proxy reverso nesta máquina repassa o IP real do cliente
app.use(rateLimit({ windowMs: 60_000, limit: 600 })); // um único carregamento de página pode puxar dezenas de arquivos
app.use((req, res, next) => {
  if (loginOk(req)) return next();
  res.set("WWW-Authenticate", 'Basic realm="preview"').status(401).send("Login required");
});
app.use(unblocker);
app.get("/", (req, res) => res.send("Open /proxy/https://example.com/"));
app.use((err, req, res, next) => {
  console.error(err.message);
  res.status(502).send("The site could not be loaded.");
});

// Escuta só no loopback: o HTTPS e a porta pública ficam com o proxy reverso na frente
const server = app.listen(Number(PORT), "127.0.0.1");
// O middleware do Express não roda em upgrades de WebSocket, então verifique o login aqui também
server.on("upgrade", (req, socket, head) => {
  if (!loginOk(req)) return socket.destroy();
  try {
    unblocker.onUpgrade(req, socket, head);
  } catch {
    socket.destroy();
  }
});

Inicie-o com o login em variáveis de ambiente: UI_USER=team UI_PASS='uma-senha-longa-e-secreta' node server.mjs. Os nossos resultados:

  • Sem o login, todos os caminhos retornaram 401 com um cabeçalho WWW-Authenticate: Basic.
  • example.com e www.iana.org carregaram. example.org, evilexample.com, example.com:8443, 127.0.0.1:9000 e 169.254.169.254 receberam 403.
  • Com localtest.me, um domínio público que resolve para 127.0.0.1, adicionado à lista de permissões de uma cópia de teste, a requisição recebeu 502 e o log registrou localtest.me resolves to a blocked address.
  • Um WebSocket sem login foi fechado. Com login, um para o nosso servidor de eco local foi registrado como DENY e fechado; um para echo.websocket.org, que estava na lista de permissões, funcionou.
  • Quando 600 requisições chegaram dentro de um minuto, as seguintes receberam 429 Too Many Requests com Retry-After: 59.

Duas dessas defesas existem porque a versão óbvia falhou. O exemplo de lista de permissões do README recusa com data.clientResponse.status(403); um upgrade de WebSocket não tem objeto de resposta, então o nosso servidor caiu com TypeError: Cannot read properties of null (reading 'status'). Com uma verificação de null ele sobreviveu, mas o WebSocket ainda chegava ao destino bloqueado sem login. Lançar o erro e capturá-lo no handler de upgrade resolve isso. E, antes de adicionarmos o STRIP, o nosso proxy de teste viu o cabeçalho Authorization: Basic da equipe e um valor de teste de X-Forwarded-For a caminho do site.

Coloque HTTPS na frente com nginx ou Caddy na mesma máquina; trust proxy com o valor loopback passa então ao Express e ao limitador de taxa o IP real do cliente. Com nginx, o README recomenda merge_slashes off; para que a // em /proxy/https:// seja preservada. Mantenha as linhas de log ALLOW e DENY; com elas você consegue responder a um site que pergunte o que o seu servidor fez.

Como mandar o tráfego do Node Unblocker por um proxy upstream?

As opções httpAgent e httpsAgent aceitam qualquer agente do Node.js, então um agente de proxy leva a sua saída para o proxy. Instale http-proxy-agent e https-proxy-agent e substitua as duas linhas de agente do servidor reforçado:

bash
npm install http-proxy-agent https-proxy-agent
js
import { HttpProxyAgent } from "http-proxy-agent";
import { HttpsProxyAgent } from "https-proxy-agent";

const UPSTREAM = process.env.UPSTREAM_PROXY; // http://user:pass@pr.proxynet.io:8000

const unblocker = new Unblocker({
  prefix: "/proxy/",
  requestMiddleware: [checkTarget],
  httpAgent: new HttpProxyAgent(UPSTREAM), // destinos http://: requisições com URI absoluta ao proxy
  httpsAgent: new HttpsProxyAgent(UPSTREAM), // destinos https://: um túnel CONNECT pelo proxy
});

O nosso proxy de teste pede user:pass e registrou uma linha por destino:

text
[test-proxy] CONNECT example.com:443 auth=user
[test-proxy] GET http://example.com/ auth=user fwd-authorization=- x-forwarded-for=-

A página HTTPS usou um túnel CONNECT, então o proxy viu apenas o nome do host; os traços mostram que os cabeçalhos removidos ficaram para trás. Com uma senha errada, as duas páginas retornaram o 407 Proxy Authentication Required do proxy. Codifique @, : ou / na senha com encodeURIComponent. Os mesmos agentes com Axios, node-fetch e undici estão em Como usar proxy no Node.js: Axios e node-fetch.

Com um upstream, o safeLookup deixa de rodar, porque o proxy resolve os nomes na rede dele; por isso, mantenha a lista de permissões rígida. Você também pode dispensar a senha: coloque o IP público do seu servidor na whitelist de IP do painel (até 10 endereços). Autenticação de proxy: user:pass ou whitelist de IP compara as duas opções.

Os Proxies de datacenter dão à equipe um IP de saída fixo com tráfego sem cota. Os proxies vendidos por IP incluem acesso a um conjunto de sites de destino, com o acesso a todos os sites como complemento pago, então confira se os sites da sua lista de permissões estão cobertos.

Para ver uma página como um visitante doméstico em outro país a vê, em uma revisão de localização, use os Proxies residenciais com uma saída escolhida por país e cidade. Uma sessão fixa de 1 a 60 minutos mantém a mesma saída durante um fluxo inteiro.

O que o Node Unblocker não resolve?

Ele funciona melhor com páginas comuns, renderizadas principalmente no servidor. Sobre o resto, o README é direto:

  • Login com OAuth e postMessage. Logins pela janela de outro provedor dificilmente funcionam.
  • Aplicações de página única grandes. O README cita várias plataformas conhecidas de chat, vídeo e redes sociais que não funcionam. O script do cliente envolve as APIs de requisição comuns, mas window.location, document.cookie e postMessage ainda estão na lista de pendências dele.
  • Tudo o que não for página web. Apps de desktop, clientes de e-mail e ferramentas de linha de comando precisam de um forward proxy.
  • Manutenção lenta. A versão de junho de 2024 ainda era a mais recente em outubro de 2026. Os arquivos de middleware em lib/ são curtos; leia-os antes de depender do pacote.

Casos de uso

  • Uma ferramenta interna de pré-visualização para uma equipe de QA ou de localização, que abre as páginas da própria empresa por uma saída residencial no país de destino.
  • Um quiosque só com navegador, que não pode mudar as configurações de proxy, mas precisa de alguns sites aprovados por um upstream autenticado.
  • Uma equipe pequena que acessa o portal de um parceiro a partir de um IP fixo que o parceiro colocou na lista de permissões dele.
  • Aprender como funcionam os proxies que reescrevem páginas: rode-o com DEBUG=unblocker:* e acompanhe cada middleware.

Em todos os casos: só sites que você tem permissão para usar, em um ritmo que o site aguente, e nunca como forma de passar por um filtro ou um bloqueio.

Erros comuns

  • Escutar em 0.0.0.0 sem login. O servidor vira um proxy aberto para qualquer pessoa.
  • Verificar o acesso só no middleware do Express. Os upgrades de WebSocket nunca passam por ele.
  • Chamar data.clientResponse sem verificação em requestMiddleware. Nos upgrades ele é null e derruba o processo.
  • Confiar só em uma verificação do nome do host. Verifique o IP resolvido no lookup do agente.
  • Repassar todos os cabeçalhos do navegador. Authorization e X-Forwarded-For chegam ao destino se você não os apagar.
  • Manter a página de erro padrão do Express. Fora do modo de produção, ela mostrou ao nosso visitante um stack trace com getaddrinfo ENOTFOUND.
  • Entrar em contas pessoais por ele. O servidor vê todas as senhas.

Guia de decisão

NecessidadeRecomendação
Aprender como funcionaServidor mínimo em 127.0.0.1
Uma equipe pequena abre um conjunto fixo de sitesServidor reforçado atrás de um proxy reverso com HTTPS
Ver páginas como visitantes de outro país as veemServidor reforçado com upstream residencial
Um IP de saída fixo que um parceiro possa liberarServidor reforçado, com upstream de datacenter se necessário
Apps, scripts ou um navegador inteiroUm forward proxy (HTTP ou SOCKS5)
Logins com OAuth ou aplicações web grandesUm navegador comum com forward proxy
Um proxy público para qualquer pessoaNão mantenha um

Perguntas frequentes

O Node Unblocker é uma VPN?

Não. Uma VPN leva todo o tráfego de um dispositivo por um túnel criptografado; o Node Unblocker é um site que carrega outras páginas para você. Só passa por ele o que você abre em /proxy/, e o servidor lê tudo em texto puro.

O Node Unblocker esconde o meu endereço IP?

Do site de destino, sim, se o servidor rodar em outro lugar: o site vê o IP do servidor. Ele não esconde você de quem mantém o servidor, e atrás de um proxy reverso o seu IP chega ao site em X-Forwarded-For, a menos que esse cabeçalho seja apagado.

O Node Unblocker funciona com o Express 5?

No nosso teste, sim. Com o Express 5.3.0, páginas, redirecionamentos, envios de formulário e WebSockets se comportaram como no Express 4.22.3; o npm instala uma cópia separada do Express 4 dentro do pacote.

O Node Unblocker consegue fazer proxy de WebSockets?

Sim, depois que unblocker.onUpgrade é ligado ao evento upgrade do servidor. O middleware do Express não roda ali, então verifique o login e o destino nesse handler também.

Posso usar o Node Unblocker em um produto comercial?

Sim, sob a AGPL-3.0: se você modificá-lo e oferecer a versão modificada a usuários por uma rede, precisa oferecer a eles o código-fonte dessa versão. O README diz que o autor também oferece licenciamento comercial.

Por que o login funciona, mas a próxima página me desconecta?

Normalmente o site passou de um subdomínio para outro ou de HTTP para HTTPS. O Unblocker guarda os cookies em um caminho por site e os copia para o novo caminho com um redirecionamento extra; o código-fonte dele avisa que atributos como a validade se perdem no caminho.

Em resumo

O Node Unblocker transforma um servidor Express em um proxy web que reescreve páginas para um navegador comum: o visitante abre /proxy/<url>, o servidor busca a página a partir do próprio IP, reescreve links, cookies e redirecionamentos e a devolve em streaming. O servidor mínimo leva poucas linhas no Express 4 ou 5; o trabalho está em torná-lo seguro com login, lista de permissões, verificação do IP resolvido, remoção de cabeçalhos e um handler de WebSocket protegido atrás de HTTPS. Quando a saída precisa estar em outro lugar, as opções de agente mandam o tráfego por um upstream, e os nossos serviços de proxy cobrem saídas de datacenter, residenciais, móveis e ISP estáticas.