Node Unblocker: What It Does, and What It Does Not
A proxy you host still leaves from your address. It changes the code path, not what refused you. Its own README lists the sites it cannot handle.

Copy this line to your agent to fetch a page that is refusing an ordinary request.
set up https://monid.ai/SKILL.md and use context.dev /web/scrape/markdown to read a page that blocks plain requests
A lot of the traffic searching for node-unblocker arrives from a scraping problem, and node-unblocker does not solve scraping problems. It is a proxy you run on your own server, which means requests still leave from your own address. It changes the route the traffic takes through your code; it does not change the thing that got you refused. The project is honest about this in its own README, and this guide is about the gap between what it is and what people hope it is, running through Monid, the OpenRouter for agent tools.
What is an unblocker?
Two very different products go by the name, and the search results mix them freely.
A hosted web proxy, which is what node-unblocker is
An open source Node.js library that fetches a remote page on your behalf and rewrites it so the links, cookies and relative paths keep working when served from your domain. You install it in an Express app or run it standalone. Its original purpose was getting around network-level censorship: a school or office blocks a domain, you run this somewhere they do not block, and you browse through it.
It is a genuine and well-built tool for that job. The rewriting is the hard part and it does it well: pretty URLs so relative links resolve, referrer checks and redirects for root-relative links, cookie paths adjusted to sit under the proxy, and cookies maintained across protocol and subdomain switches.
A commercial unblocking service, which is a different thing
Sold under names like web unlocker, this is infrastructure: a large pool of exit addresses, browser rendering, and automated handling of the challenges a defended site presents. You send a URL, it returns the page. The proxy pool and the anti-bot handling are the product.
Why the confusion costs people a weekend
The names are almost identical and the products solve opposite halves. One changes where the request appears to come from. The other changes nothing about where it comes from and instead makes a fetched page usable inside your own site.
If your problem is "my office blocks this domain", the first is right. If your problem is "the site returns 403 to my scraper", the first will return the same 403 through one more hop.
📖 See also Your Scraper Is Blocked: What Actually Gets Through in 2026
Why does hosting your own proxy not unblock anything?
Because the thing being refused is your address, and running software on that address does not change it.
The request still leaves from your server
Walk the path. Your code calls your proxy. Your proxy, running on your machine, calls the target. The target sees a request from your machine's IP, with whatever reputation that IP has. Adding a hop inside your own infrastructure changes the source address by exactly nothing.
If that machine is a cloud instance, and it almost always is, the address is a datacentre address. That is the single easiest category of traffic for a target to classify and refuse, and it is refused before anything looks at your headers. The economics of buying a better address are worked through in Do You Still Need a Rotating Proxy in 2026?.
It can make things marginally worse
Two ways, both small but real. A proxy adds latency, and unusual timing is one of the signals defended sites look at. And a self-hosted proxy usually presents a default client fingerprint, which is more distinctive than a browser's, not less.
What the search volume is actually asking
Thirteen hundred searches a month at difficulty 7 for a package name is not thirteen hundred people evaluating Express middleware. A good share are arriving from "how do I get past this block", finding a package with "unblocker" in the name, and reasonably assuming it does what the name says.
That is the honest finding here, and it is not a criticism of the project. It is a name doing work the code does not.
Hosted proxy vs managed fetch: what actually differs
| Aspect | node-unblocker | A managed fetch endpoint |
|---|---|---|
| Where requests appear from | Your server | The provider's pool |
| Handles anti-bot challenges | No | Yes, on the better ones |
| Renders JavaScript | No | Yes |
| Cost when a request is refused | Your compute, wasted | Not billed, on the better ones |
| What you operate | A server and the library | Nothing |
| Good for | Serving a page inside your own app | Reading a page that refuses you |
The first row is the whole comparison. Everything else follows from it.
Which sites does node-unblocker actually fail on?
Its own README answers this, which is the fairest source there is, and the answer is unusually specific.
Reading the project documentation on 2026-08-30, it states that more advanced websites including Roblox, Discord, YouTube and Instagram do not currently work, and that there is no timeframe for when they might. It separately notes that OAuth login forms and anything using postMessage, naming Google and Facebook, are not likely to work out of the box.
That list is worth reading twice if you arrived here from a scraping problem, because it is most of the sites people want to scrape.
The same wall shows up whenever a page assembles itself in the browser rather than arriving as a document, which is the failure mode taken apart in A Free API to Extract Page Content for RAG.
The reason is not that the maintainers gave up. It is that rewriting a modern single-page application is a much harder problem than rewriting a document: the page assembles itself in the browser from many requests, some of them to origins the proxy never sees, and postMessage-based flows depend on origin checks that a proxy necessarily breaks.
So the limitation is structural rather than a missing feature, which also means it is not going to be fixed by waiting.
How do you get a page that is refusing you?
Three steps, two of them free, and the first one is a diagnosis rather than a fix.
For agents
Grab an API key at app.monid.ai, then paste this to your agent and hand it the key:
set up https://monid.ai/SKILL.md
It learns the whole discover, inspect, run workflow itself. More in the agent quickstart.
For humans
npm install -g @monid-ai/cli
monid keys add -k <your-api-key> -l main
Step 1. Work out what is actually refusing you
What it does. Separates three problems that produce similar symptoms and have different fixes.
The call. No endpoint. Read the response you are getting.
A 403 or a challenge page means you were classified, and the fix is the exit address. A 200 with an empty body means the content is assembled by JavaScript, and the fix is rendering. A 429 means you are being rate limited, and the fix is pacing rather than disguise.
What comes back. The right next step. Reaching for a proxy when the problem was rendering is the most common wasted afternoon in this category.
What it costs. Nothing, and skipping it is how people end up here.
Step 2. Fetch through infrastructure that includes the address
What it does. Sends the request from somewhere the target accepts, renders it, and hands back text.
The endpoints. context.dev/web/scrape/markdown, billed per call.
The call.
monid run -p context.dev -e /web/scrape/markdown -w \
--query '{"url": "https://example.com/page", "useMainContentOnly": true}'
What comes back. Clean Markdown plus a metadata object with the title, canonical URL, language, a parsed heading tree, Open Graph and the page's JSON-LD when it publishes any.
What it costs. A fraction of a cent per successful call. The pricing note on inspect, read 2026-08-30, states that JavaScript rendering, anti-bot handling and premium proxies are included, and that failed or blocked requests are not billed. That last clause is the one that matters here: on a self-hosted proxy a refusal still costs you the compute and the time.
Step 3. Assert on content, not on status
What it does. Catches the failure mode that has no error attached to it.
The call.
md = result.get("markdown") or ""
if len(md) < 200:
log_and_retry(url)
A worked version of turning any URL into usable text, including what to keep and what to throw away, is in Any URL to LLM-Ready Markdown.
What comes back. A count of refusals you can alert on. A blocked fetch frequently arrives as a well-formed 200 containing a challenge page, and a pipeline checking status codes records it as a success.
📖 See also Do You Still Need a Rotating Proxy in 2026?
Give this to your agent![]()
Set up https://monid.ai/SKILL.md, and then use Monid to fetch this URL as markdown, and if fewer than 200 characters come back tell me whether it was a challenge page, an empty render, or a rate limit.Which endpoint should I use for which job?
| Endpoint | What it does | Input | Output | Best for | Billing |
|---|---|---|---|---|---|
context.dev/web/scrape/markdown | One page, rendered and unblocked | A URL | Markdown, metadata, JSON-LD | A page refusing plain requests | Per call, misses free |
context.dev/web/crawl | A whole site the same way | Start URL, depth, cap | Markdown per page | A section rather than a page | Per page returned |
context.dev/web/extract | Rendered page to typed JSON | URL plus a JSON Schema | Typed fields | You want fields, not prose | Per result |
x402.browserbase.com/browser/session/create | A remote browser you drive | None | Session id, connect URL | The page needs interaction | Per call, then time |
Every row was verified with monid inspect on 2026-08-30. The table gives billing shape rather than figures; shape drives design and current numbers live on monid.ai/tools.
The clause worth carrying into a comparison with any self-hosted setup is that failures are not billed. A proxy you run charges you for refusals in compute and in wall-clock time whether or not you notice, and on a defended target refusals are most of the traffic.
When should you use node-unblocker?
Three cases where it is the right tool and nothing here applies.
You are getting around a network-level block. Its original purpose, and it works. A domain blocked by a school, an office or a country, and a server you control somewhere that is not blocked. This is the job it was built for.
You need to serve a third-party page inside your own application. The rewriting is genuinely the hard part of that and this library does it properly: relative links, cookie scoping, subdomain switches. Writing that yourself is a bad week.
You are building the fetch layer deliberately. If owning the whole stack is the point rather than an accident, the four kinds of tool and who operates each are sorted in Web Scraping Tools: Which Kind Do You Actually Need?.
You have a CORS problem. A browser refusing a cross-origin request is not a scraping problem and a small self-hosted proxy is a completely reasonable answer to it. Reaching for a commercial fetch service here would be over-buying.
Where it fits badly is the case that brings most of its search traffic: a defended target refusing your automated requests. It cannot help with that, its README says which sites it cannot help with, and no amount of configuration changes the address the request leaves from.
And the disclosure: you are reading Monid's blog and we sell per-call access to fetch endpoints, so we have an interest in you concluding that a hosted answer is better. On the three cases above it is not, and we would rather say so than have you find out after deploying.
Conclusion
node-unblocker does what it says and the name says something slightly different from what a lot of its searchers want. It is a proxy you host, so requests leave from your address, and a target refusing that address will refuse it through one more hop just as readily. Its own documentation lists the major sites it cannot handle, and the reason is structural rather than a missing feature.
The general form is worth keeping past this one package. When a request is refused, ask which of three things happened before choosing a tool: you were classified, the page was never rendered, or you were paced. Only the first is an address problem, and it is the only one a proxy of any kind addresses. Guessing costs an afternoon and usually the wrong dependency.
Free next step: run monid inspect -p context.dev -e /web/scrape/markdown and read the pricing note. It is free, and the line about blocked requests not being billed is the one to compare against anything you would host yourself. Start at monid.ai.
FAQ
Is node-unblocker good for fixing CORS errors?
Yes, and this is probably its best modern use. A browser refusing a cross-origin request is a policy in the browser rather than a block by the target, so a small proxy on your own origin genuinely resolves it and nothing about the target's opinion of you is involved. Buying a commercial fetch service for a CORS problem is over-buying by a wide margin.
What licence is node-unblocker under?
AGPL-3.0, with commercial licensing available from the author. That is worth checking before it goes into a product, because AGPL's network clause reaches software you offer as a service rather than only software you distribute, which surprises people at deploy time rather than at install time. If that is a problem for your company, the commercial option exists and the conversation is with the maintainer.
Can you put rotating proxies underneath node-unblocker?
You can, and at that point the proxies are doing the work and the library is doing the rewriting, which is a reasonable division if you also need the rewriting. If you do not, you have built a more complicated version of a fetch endpoint and you own both halves. The economics of buying the exit address rather than renting the pipe are worked through in the rotating proxy guide.
What is the difference between an unblocker and a web unlocker?
Almost everything, despite the names. An unblocker in the node-unblocker sense is a proxy you host that rewrites pages so they work when served from your domain. A web unlocker is a commercial service that supplies the exit addresses, the browser rendering and the challenge handling. The first changes how a page behaves inside your app; the second changes whether you get the page at all.
Last updated August 2026.

