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:
- 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 um307para/proxy/http://example.com/. - O middleware de requisição roda. Funções embutidas ajustam os cabeçalhos
HosteReferer, a compressão e os cookies. Em seguida rodam as suas próprias funções derequestMiddleware; as verificações de acesso ficam aqui. - O seu servidor se conecta ao destino com os cabeçalhos do navegador, por meio de
httpAgentouhttpsAgent, se você tiver definido um. O destino vê o IP do seu servidor. - O middleware de resposta edita a resposta. Ele remove os cabeçalhos
Content-Security-Policy,Strict-Transport-SecurityePublic-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 robotsNOINDEX, NOFOLLOW. - A página volta em streaming, pedaço por pedaço.
- Links perdidos são recuperados. Uma requisição relativa à raiz, como
/about, chega sem o prefixo; o Node Unblocker lê oReferere responde com um307para 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:
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 Unblocker | Forward proxy | Proxy reverso | |
|---|---|---|---|
| Como é usado | Abrir uma URL /proxy/ | Definir host:port no app ou no sistema | Acessar o endereço do próprio site |
| Quem configura | Quem mantém o servidor | O usuário ou a empresa dele | O dono do site |
| Altera a página | Sim: links, cookies, redirecionamentos | Não | Normalmente não |
| HTTPS | Descriptografado no seu servidor | Vai por túnel, continua criptografado | Descriptografado pelo dono do site |
| Tráfego | Páginas web e WebSockets | Qualquer HTTP; SOCKS5 também UDP | HTTP(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:
npm install express unblockerimport 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:
INTERNAL ADMIN PAGE - only for 127.0.0.1Express 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
Pathnão é uma medida de segurança. Um script em uma dessas páginas pode ler os cookies de outro site que não tenhamHttpOnly.
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:
- Um login: um único login HTTP Basic para a equipe, comparado em tempo constante.
- Uma lista de hosts permitidos, só nas portas 80 e 443, verificada em
requestMiddleware. - Uma verificação de cada resposta de DNS no
lookupdos agentes, porque qualquer domínio pode apontar para127.0.0.1. Onet.BlockListdo Node guarda as faixas privadas e também reconhece endereços IPv4 escritos no formato IPv6. - Remoção de cabeçalhos, para que o cabeçalho
Authorizationda equipe e o IP do cliente emX-Forwarded-Fornunca cheguem ao site. - Uma proteção para WebSocket, porque requisições de upgrade pulam o middleware do Express.
- Um limite de taxa, uma linha de log e uma página de erro simples.
npm install express unblocker express-rate-limitDepois, substitua o server.mjs por esta versão:
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
401com um cabeçalhoWWW-Authenticate: Basic. example.comewww.iana.orgcarregaram.example.org,evilexample.com,example.com:8443,127.0.0.1:9000e169.254.169.254receberam403.- Com
localtest.me, um domínio público que resolve para127.0.0.1, adicionado à lista de permissões de uma cópia de teste, a requisição recebeu502e o log registroulocaltest.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
DENYe fechado; um paraecho.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 RequestscomRetry-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:
npm install http-proxy-agent https-proxy-agentimport { 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:
[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.cookieepostMessageainda 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.0sem 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.clientResponsesem verificação emrequestMiddleware. Nos upgrades ele énulle derruba o processo. - Confiar só em uma verificação do nome do host. Verifique o IP resolvido no
lookupdo agente. - Repassar todos os cabeçalhos do navegador.
AuthorizationeX-Forwarded-Forchegam 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
| Necessidade | Recomendação |
|---|---|
| Aprender como funciona | Servidor mínimo em 127.0.0.1 |
| Uma equipe pequena abre um conjunto fixo de sites | Servidor reforçado atrás de um proxy reverso com HTTPS |
| Ver páginas como visitantes de outro país as veem | Servidor reforçado com upstream residencial |
| Um IP de saída fixo que um parceiro possa liberar | Servidor reforçado, com upstream de datacenter se necessário |
| Apps, scripts ou um navegador inteiro | Um forward proxy (HTTP ou SOCKS5) |
| Logins com OAuth ou aplicações web grandes | Um navegador comum com forward proxy |
| Um proxy público para qualquer pessoa | Nã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.




