Open source version of some performance improvement for running nitter instance xcancel
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-03-21 12:32:24 +01:00
nginx doc: document docker compose too 2026-03-21 12:21:29 +01:00
nitter doc: document docker compose too 2026-03-21 12:21:29 +01:00
README.md doc: add benefits 2026-03-21 12:32:24 +01:00

Nitter Public Instance Customizations

Nginx configuration for caching nitter HTML pages and proxying /pic/ and /video/ media directly from Twitter CDN — used in production on xcancel.com.

Performance benefits

These optimizations were measured in production on xcancel.com:

  • Caching HTML pages reduced CPU load by ~60% across all cores. Most popular posts and profiles are served directly from nginx cache without ever hitting nitter. Since a small number of posts account for the majority of traffic, the cache hit rate is very high.
  • Proxying /pic/ and /video/ in nginx reduced CPU load by ~30% across all cores. Media proxying is pure I/O (fetch from Twitter CDN, forward to client) — nitter doesn't need to be involved. Handling this in nginx with Lua avoids spinning up nitter's runtime for every image and video request.

Combined, these two changes cut nitter's CPU usage by roughly 90%, making it feasible to run a large public instance on modest hardware.

Requirements

  • OpenResty (nginx + LuaJIT) — the Lua scripts require OpenResty's ngx API and ffi module. Standard nginx won't work.

What's included

Caching

Nginx proxy cache for nitter HTML pages with cookie-based bypass. Users who change nitter preferences (theme, proxy settings, etc.) bypass the cache; everyone else gets served from cache.

Two cache profiles:

  • cache-with-bypass.conf — 30 min cache, respects user preference cookies (for HTML pages)
  • cache-without-bypass.conf — 2 hour cache, no bypass (for static assets, media)

A pathcached shared dict optimization tracks which URI paths are already cached, so you can skip expensive access checks (rate limiting, antibot) on subsequent requests for those paths.

Twitter CDN Proxy (/pic/ and /video/)

Instead of letting nitter proxy all media through itself, nginx handles /pic/ and /video/ requests directly by fetching from Twitter CDN (twimg.com). This reduces load on nitter and lets you run the CDN proxy on a separate server if needed.

How it works:

  1. Nitter generates HMAC-signed media URLs (signature rotates daily)
  2. Lua scripts in nginx verify the HMAC signature
  3. For valid requests: nginx fetches directly from Twitter CDN with 1-week browser caching
  4. For .m3u8/.vmap video manifests: falls back to nitter (these need content rewriting that only nitter can do)

Setup:

  • Set M.HMAC_KEY in nginx/lua/nitter-hmac.lua to match the hmacKey value in your nitter.conf
  • See nginx/example-nginx.conf for how to wire up the locations and internal proxy

Running multiple nitter processes

Nitter is single-threaded, so running multiple processes behind nginx is the way to scale. The included nitter/docker-compose.yml uses deploy.replicas to spawn 5 replicas by default:

deploy:
  replicas: 5

This creates containers named nitter-nitter-1 through nitter-nitter-5. List each replica in the nginx upstream block so nginx load-balances across them:

upstream nitter {
  server nitter-nitter-1:8080;
  server nitter-nitter-2:8080;
  server nitter-nitter-3:8080;
  server nitter-nitter-4:8080;
  server nitter-nitter-5:8080;
}

All replicas share the same nitter.conf and Redis instance. Adjust the replica count to your CPU/memory budget. See nitter/docker-compose.yml for the full Docker Compose setup.

Nitter patches

Running the CDN proxy on a separate server requires patches to nitter. See: https://github.com/unixfox/periodic-build-with-github-actions/tree/master/nitter/patches

File overview

nitter/

Docker Compose setup for running nitter with Valkey (Redis). Contains a nitter service (scalable with --scale) and a nitter-redis service. The nitter service is configured as read-only with dropped capabilities for security. Set the $IMAGE env var to your nitter Docker image.

nginx/

nitter/
└── docker-compose.yml              # Docker Compose for nitter + valkey (use --scale to run multiple)
nginx/
├── example-nginx.conf              # Example nginx config showing how to use everything
├── extra-conf/
│   ├── cache-with-bypass.conf      # Cache profile: 30 min, cookie bypass
│   ├── cache-without-bypass.conf   # Cache profile: 2 hours, no bypass
│   └── proxy-nitter.conf          # Proxy pass to nitter upstream
└── lua/
    ├── nitter-hmac.lua            # HMAC-SHA256 verification (shared by pic + video)
    ├── nitter-pic_proxy.lua       # /pic/ → Twitter CDN proxy
    └── nitter-video_proxy.lua     # /video/ → Twitter CDN proxy (mp4/ts/m4s only)