Build a Wikipedia mirror with CloudFlare Workers.
Create a CloudFlare Worker, paste the contents of main.js into the editor and deploy. Add a route for your domain if you're not using the default workers.dev domain.
No editing required — the mirror derives its own origin from the incoming request, so there is nothing to find-and-replace. (If you want to restrict which hostnames the Worker answers on, fill in CFG.ALLOWED_HOSTNAMES.)
Visit the domain name to use.
| Path | Upstream |
|---|---|
/ |
www.wikipedia.org (portal) |
/<lang>/... |
<lang>.wikipedia.org/... |
/-/<hostname>/... |
<hostname>/..., restricted to Wikimedia-family domains |
/image-proxy/... |
upload.wikimedia.org/... (legacy alias) |
/-/langs.json |
Diagnostics: the language set currently recognised |
/-/client.js |
Standalone copy of the injected client patch (for debugging) |
Multilingual versions are accessed via URL paths instead of subdomains, e.g. https://YOUR-DOMAIN/en for English and https://YOUR-DOMAIN/zh for Chinese.
- No hardcoded language list. Language codes are recognised by shape (
LANG_SHAPE), which matches all 296 Wikipedia language codes including hyphenated legacy ones likezh-min-nanandbat-smg. Only three MediaWiki root paths collide with that shape (api,www,cdn-cgi), so they're excluded explicitly. An authoritative code set is fetched from Meta'ssitematrixAPI, cached at the edge for a week, refreshed in the background, and fails open — so it never blocks a request. - Everything goes through the mirror. All Wikimedia-family absolute URLs are mapped to mirror paths, in HTML and in JavaScript/CSS/JSON payloads. This matters because ResourceLoader ships its module source as a hardcoded absolute URL inside the
startupmodule (mw.loader.addSource({"local":"https://<lang>.wikipedia.org/w/load.php"})); if that isn't rewritten, every lazily-loaded module is fetched straight from upstream and fails. - Streaming HTML rewriting via
HTMLRewriterinstead of running hundreds ofreplaceAllpasses over a multi-megabyte document. - A client-side patch is inlined at the very top of
<head>so it runs before theasyncstartup module. It maps URLs created at runtime: dynamic<script>/<link>(which is exactly howmw.loaderfetches modules),fetch,XMLHttpRequest,sendBeacon,EventSource,WebSocket, and URL-bearing attribute writes. - CSP is augmented, not deleted. Upstream's third-party allowlist is preserved; the mirror's own origin and Cloudflare's analytics beacon are added, and the Wikimedia reporting endpoints are stripped.
- Reader preferences survive, tracking cookies don't. Only
mf_useformat,stopMobileRedirectandmwclientpreferencesare passed between browser and upstream — enough for the mobile/desktop toggle and for dark mode / font size. Wikimedia's tracking cookies (WMF-Uniq,GeoIP, …) are dropped so upstream can't fingerprint visitors through the mirror. Since mobile and desktop are the same URL with different HTML, the preference is folded into the edge cache key; visitors with no preference cookie still share one cache entry.
- Doesn't support login/editing (Cloudflare IPs are blocked upstream, and session cookies aren't proxied).
- Gadgets that load from third-party CDNs still reach those CDNs directly; only Wikimedia-family domains are proxied.
data-mw-original-hrefattributes and COinS citation metadata deliberately keep the original upstream hostnames, since rewriting them would corrupt archive links and bibliographic records.