--- search: exclude: true --- # nitter Setup ![](0.png) In this tutorial we're going to setup nitter, a privacy front end to browse twitter without all the trackers. ## **Initial Setup** Clone the repository and start the docker-compose: [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → apt install nim docker.io docker-compose libsass-dev libpcre3 [ nowhere.moe ] [ /dev/pts/1 ] [/srv] → git clone https://github.com/zedeus/nitter Cloning into 'nitter'... remote: Enumerating objects: 6639, done. remote: Counting objects: 100% (163/163), done. remote: Compressing objects: 100% (118/118), done. remote: Total 6639 (delta 87), reused 97 (delta 45), pack-reused 6476 Receiving objects: 100% (6639/6639), 5.26 MiB | 1.81 MiB/s, done. Resolving deltas: 100% (4852/4852), done. [ nowhere.moe ] [ /dev/pts/1 ] [/srv] → cd nitter [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → docker build -t nitter:latest . Sending build context to Docker daemon 6.428MB Step 1/18 : FROM nimlang/nim:1.6.10-alpine-regular as nim 1.6.10-alpine-regular: Pulling from nimlang/nim 213ec9aee27d: Pull complete 071ba9e2da5d: Pull complete 8c05e9a65d0e: Pull complete [...] Step 17/18 : USER nitter ---> Running in b9cbc6b2d0f5 Removing intermediate container b9cbc6b2d0f5 ---> d887fd7e9c0f Step 18/18 : CMD ./nitter ---> Running in 0dca3c6f9ba3 Removing intermediate container 0dca3c6f9ba3 ---> 24ad9b89ab85 Successfully built 24ad9b89ab85 Successfully tagged nitter:latest [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → apt install nim [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → nimble build -d:release Verifying dependencies for nitter@0.1.0 Prompt: No local packages.json found, download it from internet? [y/N] Answer: y Downloading Official package list Success Package list downloaded. Installing jester@#baca3f Downloading https://github.com/dom96/jester using git Verifying dependencies for jester@#baca3f [...] [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → nimble scss [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → nimble md [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → cp nitter.example.conf nitter.conf [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → cat nitter.conf [Server] hostname = "nitter.nowhere.moe" # for generating links, change this to your own domain/ip title = "nitter" address = "0.0.0.0" port = 8080 https = false # disable to enable cookies when not using https httpMaxConnections = 100 staticDir = "./public" [Cache] listMinutes = 240 # how long to cache list info (not the tweets, so keep it high) rssMinutes = 10 # how long to cache rss queries redisHost = "nitter-redis" # Change to "nitter-redis" if using docker-compose redisPort = 6379 redisPassword = "" redisConnections = 20 # minimum open connections in pool redisMaxConnections = 30 # new connections are opened when none are available, but if the pool size # goes above this, they're closed when released. don't worry about this unless # you receive tons of requests per second [Config] hmacKey = "aoQu6le1IQueut8hei3U" # random key for cryptographic signing of video urls # use pwgen 20 1 to generate it base64Media = false # use base64 encoding for proxied media urls enableRSS = true # set this to false to disable RSS feeds enableDebug = false # enable request logs and debug endpoints (/.tokens) proxy = "" # http/https url, SOCKS proxies are not supported proxyAuth = "" tokenCount = 10 # minimum amount of usable tokens. tokens are used to authorize API requests, # but they expire after ~1 hour, and have a limit of 500 requests per endpoint. # the limits reset every 15 minutes, and the pool is filled up so there's # always at least `tokenCount` usable tokens. only increase this if you receive # major bursts all the time and don't have a rate limiting setup via e.g. nginx # Change default preferences here, see src/prefs_impl.nim for a complete list [Preferences] theme = "Nitter" replaceTwitter = "nitter.nowhere.moe" replaceYouTube = "iv.nowhere.moe" replaceReddit = "teddit.net" proxyVideos = true hlsPlayback = false infiniteScroll = false [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → cat docker-compose.yml version: "3" services: nitter: image: zedeus/nitter:latest container_name: nitter ports: - "127.0.0.1:8080:8080" # Replace with "8080:8080" if you don't use a reverse proxy volumes: - ./nitter.conf:/src/nitter.conf:Z,ro depends_on: - nitter-redis restart: unless-stopped healthcheck: test: wget -nv --tries=1 --spider http://127.0.0.1:8080/Jack/status/20 || exit 1 interval: 30s timeout: 5s retries: 2 user: "998:998" read_only: true security_opt: - no-new-privileges:true cap_drop: - ALL nitter-redis: image: redis:6-alpine container_name: nitter-redis command: redis-server --save 60 1 --loglevel warning volumes: - nitter-redis:/data restart: unless-stopped healthcheck: test: redis-cli ping interval: 30s timeout: 5s retries: 2 user: "999:1000" read_only: true security_opt: - no-new-privileges:true cap_drop: - ALL volumes: nitter-redis: [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → docker-compose down Stopping nitter ... done Stopping nitter-redis ... done Removing nitter ... done Removing nitter-redis ... done Removing network nitter_default [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → docker-compose up -d Creating network "nitter_default" with the default driver Creating nitter-redis ... done Creating nitter ... done then you can check your service is up on port 8080: [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3582cdcf9687 zedeus/nitter:latest "/bin/sh -c ./nitter" 3 minutes ago Up 3 minutes (healthy) 127.0.0.1:8080->8080/tcp nitter 1b9a40a33d83 redis:6-alpine "docker-entrypoint.s…" 3 minutes ago Up 3 minutes (healthy) 6379/tcp nitter-redis Then setup the reverse nginx proxy: [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → vim /etc/nginx/sites-available/nitter.nowhere.moe.conf [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → cat /etc/nginx/sites-available/nitter.nowhere.moe.conf server { listen 443 ssl; server_name nitter.nowhere.moe; ssl_certificate /etc/acme/certs/nitter.nowhere.moe/fullchain.cer; ssl_certificate_key /etc/acme/certs/nitter.nowhere.moe/iv.nowhere.moe.key; ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1; ssl_prefer_server_ciphers on; ssl_ciphers EECDH+AESGCM:EDH+AESGCM; ssl_ecdh_curve secp384r1; ssl_session_timeout 10m; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; # Security Headers add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; add_header Content-Security-Policy "default-src 'none'; script-src 'self' 'unsafe-inline'; img-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; object-src 'none'; media-src 'self' blob: video.twimg.com; worker-src 'self' blob:; base-uri 'self'; form-action 'self'; frame-ancestors 'self'; connect-src 'self' https://*.twimg.com; manifest-src 'self'"; add_header X-Content-Type-Options nosniff; add_header X-Frame-Options DENY; add_header X-XSS-Protection "1; mode=block"; location / { proxy_pass http://localhost:YOUR_NITTER_PORT; } location = /robots.txt { add_header Content-Type text/plain; return 200 "User-agent: *\nDisallow: /\n"; } } [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → bash root@Datura /srv/nitter # systemctl stop nginx root@Datura /srv/nitter # acme.sh --issue --standalone -d nitter.nowhere.moe -k 4096 [ nowhere.moe ] [ /dev/pts/1 ] [/etc/nginx/sites-available] → ln -s /etc/nginx/sites-available/nitter.nowhere.moe.conf /etc/nginx/sites-enabled root@Datura /srv/nitter # systemctl start nginx [ nowhere.moe ] [ /dev/pts/1 ] [/srv/nitter] → systemctl status nginx ● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled) Active: active (running) since Sat 2023-07-15 22:55:17 CEST; 7min ago Docs: man:nginx(8) Process: 168567 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS) Process: 168568 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS) Main PID: 168569 (nginx) Tasks: 13 (limit: 77000) Memory: 15.9M CPU: 776ms CGroup: /system.slice/nginx.service ├─168569 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;" ├─169429 "nginx: worker process" ├─169430 "nginx: worker process" ├─169431 "nginx: worker process" ├─169432 "nginx: worker process" ├─169433 "nginx: worker process" ├─169434 "nginx: worker process" ├─169435 "nginx: worker process" ├─169436 "nginx: worker process" ├─169437 "nginx: worker process" ├─169438 "nginx: worker process" ├─169439 "nginx: worker process" └─169440 "nginx: worker process" Jul 15 22:55:17 Datura systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server... Jul 15 22:55:17 Datura systemd[1]: Started nginx.service - A high performance web server and a reverse proxy server. Then test to see if your website renders fine: ![](1.png) To have it available on tor you can use the following nginx config: [ nowhere.moe ] [ /dev/pts/1 ] [/etc/nginx/sites-available] → cat nitter.nowhere.moe.conf server { listen 443 ssl; server_name nitter.nowhere.moe; ssl_certificate /etc/acme/certs/nitter.nowhere.moe/nitter.nowhere.moe.cer; ssl_certificate_key /etc/acme/certs/nitter.nowhere.moe/nitter.nowhere.moe.key; ######## TOR CHANGES ######## listen 4443; listen [::]:4443; server_name nitter.daturab6drmkhyeia4ch5gvfc2f3wgo6bhjrv3pz6n7kxmvoznlkq4yd.onion; add_header Onion-Location "http://nitter.daturab6drmkhyeia4ch5gvfc2f3wgo6bhjrv3pz6n7kxmvoznlkq4yd.onion$request_uri" always; ######## TOR CHANGES ######## ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1; ssl_prefer_server_ciphers on; ssl_ciphers EECDH+AESGCM:EDH+AESGCM; ssl_ecdh_curve secp384r1; ssl_session_timeout 10m; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; # Security Headers add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; add_header Content-Security-Policy "default-src 'none'; script-src 'self' 'unsafe-inline'; img-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; object-src 'none'; media-src 'self' blob: video.twimg.com; worker-src 'self' blob:; base-uri 'self'; form-action 'self'; frame-ancestors 'self'; connect-src 'self' https://*.twimg.com; manifest-src 'self'"; add_header X-Content-Type-Options nosniff; add_header X-Frame-Options DENY; add_header X-XSS-Protection "1; mode=block"; location / { proxy_pass http://localhost:8080; } location = /robots.txt { add_header Content-Type text/plain; return 200 "User-agent: *\nDisallow: /\n"; } } [ nowhere.moe ] [ /dev/pts/1 ] [/etc/nginx/sites-available] → nginx -s reload ![](2.png) And thats it! now last step is to contribute to the overall [list of instances](https://github.com/zedeus/nitter/wiki/Instances) by sending a request to get your instance listed publicly [here](https://github.com/zedeus/nitter/issues/947).