███████╗███╗ ██╗██╗███████╗██████╗ ███████╗
██╔════╝████╗ ██║██║██╔════╝██╔══██╗██╔════╝
███████╗██╔██╗ ██║██║███████╗██████╔╝█████╗
╚════██║██║╚██╗██║██║╚════██║██╔═══╝ ██╔══╝
███████║██║ ╚████║██║███████║██║ ██║
╚══════╝╚═╝ ╚═══╝╚═╝╚══════╝╚═╝ ╚═╝
SNISPF is a lightweight command-line tool that helps you get past internet censorship. It works by messing with the way your connection introduces itself to firewalls, so filtered websites slip through undetected. Runs on Windows, macOS, and Linux -- no drivers, no admin rights needed for most features.
Maintained by @Rainman69
- How Does It Work?
- Requirements
- Installation
- Quick Start Guide
- Configuration
- Bypass Methods Explained
- Fragment Strategies
- Platform Support
- Troubleshooting
- How It Works (Technical Deep Dive)
- Project Structure
- Running the Tests
- License
- Acknowledgements
When you visit a website over HTTPS, your device sends a "hello" message (called a TLS ClientHello) that contains the website name in plain text. This is known as the SNI (Server Name Indication). Internet censorship systems (called DPI -- Deep Packet Inspection) read that name and decide whether to block the connection.
SNISPF sits between your app and the internet. It intercepts that "hello" message and either chops it up or sends a decoy so the censorship system can't read the real website name. The actual destination server still gets the full, correct message and works normally.
┌──────────┐ ┌─────────┐ ┌─────────┐ ┌──────────────┐
│ Your App ├────>│ SNISPF ├────>│ DPI / ├────>│ Real Server │
│ (browser,│ │ (local │ │Firewall │ │ (e.g. │
│ v2ray, │ │ proxy) │ │ │ │ Cloudflare) │
│ etc.) │ │ │ │ │ │ │
└──────────┘ └─────────┘ └─────────┘ └──────────────┘
│ │
│ sends fake / │ sees fake or
│ fragmented │ incomplete SNI
│ hello message │ --> lets it through
- Python 3.8 or newer (check with
python3 --versionorpython --version) - That's it. No external dependencies, no C compilers, no kernel modules.
If you don't have Python yet:
| OS | How to install Python |
|---|---|
| Windows | Download from python.org. During install, check "Add Python to PATH". |
| macOS | Run brew install python (if you have Homebrew) or download from python.org. |
| Ubuntu / Debian | sudo apt update && sudo apt install python3 python3-pip |
| Fedora | sudo dnf install python3 python3-pip |
| Arch | sudo pacman -S python python-pip |
| Android (Termux) | pkg install python |
This installs the snispf command system-wide:
git clone https://github.com/Rainman69/SNISPF.git
cd SNISPF
pip install .Now you can run it from anywhere:
snispf --helpNo install needed. Just clone and run:
git clone https://github.com/Rainman69/SNISPF.git
cd SNISPF
python run.py --helpIf you want to run it as a Python module:
git clone https://github.com/Rainman69/SNISPF.git
cd SNISPF
python -m sni_spoofing.cli --helpgit clone https://github.com/Rainman69/SNISPF.git
cd SNISPF
docker build -t snispf .
docker run --rm -p 40443:40443 snispfThe simplest way to start -- using the default settings:
snispf -l 0.0.0.0:40443 -c 188.114.98.0:443 -s auth.vercel.comWhat each part means:
| Flag | What it does | Example value |
|---|---|---|
-l |
The local address and port SNISPF listens on | 0.0.0.0:40443 (all interfaces, port 40443) |
-c |
The real server IP and port to forward traffic to | 188.114.98.0:443 (a Cloudflare IP) |
-s |
The fake website name to show the firewall | auth.vercel.com (an allowed domain) |
Tip: If you're not sure what IP or fake SNI to use, the defaults above work for many Cloudflare-based setups.
Once SNISPF is running, configure your application (web browser, V2Ray, Xray, proxy client, etc.) to connect through:
Address: 127.0.0.1
Port: 40443
That's it. Your traffic now goes through SNISPF, which handles the bypass automatically.
You can configure SNISPF two ways: with a config file or with command-line flags. Flags override the config file when both are used.
Generate a default config:
snispf --generate-config config.jsonThis creates a config.json file you can edit. Then run with:
snispf --config config.json# Basic usage
snispf -l :40443 -c 188.114.98.0:443 -s auth.vercel.com
# Use the strongest bypass method
snispf -l :40443 -c 188.114.98.0:443 -s dl.google.com -m combined
# See verbose debug output
snispf -l :40443 -c 188.114.98.0:443 -s auth.vercel.com -v
# Check what your system supports
snispf --infoHere's what each field in config.json does:
{
"LISTEN_HOST": "0.0.0.0",
"LISTEN_PORT": 40443,
"CONNECT_IP": "188.114.98.0",
"CONNECT_PORT": 443,
"FAKE_SNI": "auth.vercel.com",
"BYPASS_METHOD": "fragment",
"FRAGMENT_STRATEGY": "sni_split",
"FRAGMENT_DELAY": 0.1,
"USE_TTL_TRICK": false,
"FAKE_SNI_METHOD": "prefix_fake"
}| Field | What it does | Default |
|---|---|---|
LISTEN_HOST |
IP address to listen on. 0.0.0.0 means all network interfaces. |
0.0.0.0 |
LISTEN_PORT |
Port number to listen on locally. | 40443 |
CONNECT_IP |
The real server's IP address to forward traffic to. | 188.114.98.0 |
CONNECT_PORT |
The real server's port. | 443 |
FAKE_SNI |
A website name that is NOT blocked in your region. The firewall will see this instead of the real one. | auth.vercel.com |
BYPASS_METHOD |
Which bypass technique to use: fragment, fake_sni, or combined. |
fragment |
FRAGMENT_STRATEGY |
How to split the hello message: sni_split, half, multi, or tls_record_frag. |
sni_split |
FRAGMENT_DELAY |
How long to wait between sending fragments (in seconds). | 0.1 |
USE_TTL_TRICK |
Use the IP TTL trick for extra stealth. Needs root/admin. | false |
FAKE_SNI_METHOD |
Sub-method for fake_sni: prefix_fake, ttl_trick, or disorder. |
prefix_fake |
usage: snispf [-h] [--config CONFIG] [--generate-config PATH]
[--listen HOST:PORT] [--connect IP:PORT] [--sni HOSTNAME]
[--method {fragment,fake_sni,combined}]
[--fragment-strategy {sni_split,half,multi,tls_record_frag}]
[--fragment-delay SECONDS] [--ttl-trick]
[--verbose] [--quiet] [--version] [--info]
| Flag | Short | Description |
|---|---|---|
--config |
-C |
Path to a JSON config file |
--generate-config |
Create a default config file and exit | |
--listen |
-l |
Local listen address (HOST:PORT) |
--connect |
-c |
Target server address (IP:PORT) |
--sni |
-s |
Fake SNI hostname |
--method |
-m |
Bypass method: fragment, fake_sni, or combined |
--fragment-strategy |
How to fragment: sni_split, half, multi, tls_record_frag |
|
--fragment-delay |
Seconds to wait between fragments | |
--ttl-trick |
Enable TTL trick (needs elevated privileges) | |
--no-raw |
Disable raw socket injection even if available | |
--verbose |
-v |
Show detailed debug output |
--quiet |
-q |
Only show warnings and errors |
--version |
-V |
Print version and exit |
--info |
Show what your platform supports and exit |
Splits your TLS hello message into multiple pieces so the firewall can't read the website name from any single piece.
Best for: Most situations. Works everywhere, no special privileges needed.
Normal: [Full hello: ...SNI=blocked-site.com...] --> Firewall blocks it
SNISPF: [Piece 1: ...SN] --> Firewall sees incomplete name
[Piece 2: I=blocked-site.com...] --> Too late, already let through
Injects a decoy hello message with an allowed website name that DPI parses, but the server drops.
Best for: When fragmentation alone doesn't work. Most effective with root/admin.
With root (Linux): Uses raw socket injection to send the fake ClientHello with a TCP sequence number that falls outside the server's receive window. DPI sees it and whitelists the connection. The server drops it because the sequence number is out of range. This is the same technique used by the original patterniha tool.
Without root: Falls back to fragmenting the real ClientHello (same as fragment method). Sending a fake ClientHello on the same TCP stream would corrupt the TLS handshake, so we don't do that.
With root: [Fake hello: seq=out-of-window] --> DPI sees it, server drops it
[Real hello: seq=normal] --> Server processes, DPI ignores
Without root: Falls back to fragment method
Uses both methods at the same time: injects a fake hello (if root is available), then sends the real hello in fragments.
Best for: Aggressive DPI systems. This is the most effective option.
With root: Injects the fake via raw socket (out-of-window seq trick) and fragments the real ClientHello. Hits DPI from two angles simultaneously.
Without root: Fragments only (the fake injection is skipped since it can't be done safely without raw sockets).
snispf -l :40443 -c 188.114.98.0:443 -s dl.google.com -m combined
# On Linux, run with sudo for the full seq_id trick:
sudo snispf -l :40443 -c 188.114.98.0:443 -s dl.google.com -m combinedThese control how the hello message gets split up (used by fragment and combined methods):
| Strategy | What it does | When to use it |
|---|---|---|
sni_split |
Cuts right through the middle of the website name. | Default and most effective for most firewalls. |
half |
Cuts the entire message in half. | Simple fallback if sni_split doesn't work. |
multi |
Chops into many small 24-byte pieces. | For firewalls that try to reassemble two fragments. |
tls_record_frag |
Creates multiple valid TLS records from one message. | For firewalls that understand TLS but don't handle multi-record. |
Example:
snispf -l :40443 -c 188.114.98.0:443 -s auth.vercel.com --fragment-strategy multi| Platform | Works? | Notes |
|---|---|---|
| Windows 10 / 11 | Yes | No admin needed for basic methods |
| Linux (Ubuntu, Debian, Fedora, Arch, etc.) | Yes | Use sudo for raw injection (seq_id trick) |
| macOS | Yes | Fragmentation and TTL trick only (no AF_PACKET) |
| Android (Termux) | Yes | Install Python first: pkg install python |
| WSL / WSL2 | Yes | Works like native Linux |
The fragment method works everywhere using standard socket options (TCP_NODELAY). The fake_sni and combined methods are most effective on Linux with root, where they use AF_PACKET raw sockets to inject fake packets with out-of-window TCP sequence numbers. Without root, they fall back to fragmentation.
Ports below 1024 need root/admin. Use a higher port:
snispf -l :40443 ...Something else is using that port. Pick a different one:
snispf -l :50443 ...Try these steps in order:
-
Switch bypass method:
fragment->combined->fake_snisnispf -l :40443 -c 188.114.98.0:443 -s auth.vercel.com -m combined
-
Try different fragment strategies:
sni_split->multi->tls_record_fragsnispf -l :40443 -c 188.114.98.0:443 -s auth.vercel.com --fragment-strategy multi
-
Increase the delay between fragments:
snispf -l :40443 -c 188.114.98.0:443 -s auth.vercel.com --fragment-delay 0.2
-
Try a different fake SNI. Pick a major website that's not blocked in your area:
snispf -l :40443 -c 188.114.98.0:443 -s dl.google.com
-
Double-check the target IP and port. Make sure
CONNECT_IPactually points to the server you want.
The TTL trick needs elevated privileges:
- Linux / macOS: Run with
sudo - Windows: Run the terminal as Administrator
On Linux, consider using combined or fake_sni with sudo instead. The raw injection method (seq_id trick) is more reliable than the TTL trick because it's independent of network topology.
On Linux, run as root. This enables raw packet injection which is the same technique as the original patterniha tool:
sudo snispf -l :40443 -c 188.114.98.0:443 -s auth.vercel.com -m combinedsnispf --infoThis shows which features are available on your platform.
When a TLS connection starts, the client sends a ClientHello message containing the SNI. DPI systems inspect this to filter connections.
SNISPF splits the ClientHello into multiple TCP segments so the SNI is divided across packets:
Normal: [TLS Record: ...SNI=blocked-site.com...] --> DPI reads and blocks
SNISPF: [Fragment 1: ...SN] --> DPI sees incomplete SNI
[Fragment 2: I=blocked-site.com...] --> DPI can't match pattern
This works because many DPI systems only inspect the first TCP segment or don't reassemble the full TCP stream.
A fake ClientHello with an allowed SNI is sent before the real one:
Step 1: [Fake ClientHello: SNI=allowed-site.com] --> DPI allows it
Step 2: [Real ClientHello: SNI=blocked-site.com] --> DPI already decided
The server ignores the fake because it's a malformed/incomplete handshake.
The fake packet is sent with a low IP TTL (Time To Live):
Fake packet (TTL=3): Reaches DPI (2 hops away) but expires before the server
Real packet (TTL=64): Reaches the server normally
The DPI sees the fake SNI and allows the traffic. The server never sees the fake packet at all.
SNISPF/
├── sni_spoofing/ # Main package
│ ├── __init__.py # Version and metadata
│ ├── cli.py # Command-line interface and argument parsing
│ ├── forwarder.py # Core async TCP forwarder
│ ├── bypass/ # Bypass strategy implementations
│ │ ├── __init__.py # Exports all strategies
│ │ ├── base.py # Abstract base class for strategies
│ │ ├── fragment.py # TLS fragmentation bypass
│ │ ├── fake_sni.py # Fake SNI bypass (with raw injection support)
│ │ ├── combined.py # Combined (fragment + fake SNI) bypass
│ │ └── raw_injector.py # AF_PACKET raw injection (seq_id trick)
│ ├── tls/ # TLS packet handling
│ │ ├── __init__.py # ClientHello builder and parser
│ │ └── fragment.py # TLS record fragmentation logic
│ └── utils/ # Utility functions
│ └── __init__.py # Network helpers, platform detection
├── tests/
│ └── test_tls.py # Unit tests
├── config.json # Default configuration file
├── run.py # Run without installing (python run.py)
├── pyproject.toml # Python package configuration
├── Dockerfile # Docker support
├── LICENSE # MIT License
└── README.md # You are here
cd SNISPF
python -m pytest tests/ -vOr without pytest:
python -m unittest tests.test_tls -v- Fixed the seq_id problem with
fake_sniandcombinedmethods. The old code sent the fake ClientHello as regular data on the same TCP stream, which the server would receive and try to parse as a real TLS record. This corrupted the handshake every time. Now on Linux with root, SNISPF usesAF_PACKETraw socket injection to send the fake ClientHello with an out-of-window TCP sequence number (seq = ISN + 1 - len(fake)). DPI parses it and whitelists the connection; the server drops it because the sequence number falls before its receive window. This is the same technique used by patterniha's original tool and the Go reimplementation. - Fixed
fake_sniwithout raw sockets. Previously it would send the fake ClientHello on the real TCP stream, breaking the TLS handshake. Now it falls back to fragmenting the real ClientHello at the SNI boundary instead of corrupting the connection. - Fixed
combinedwithout raw sockets. Same issue -- no longer sends junk data on the real TCP stream. Falls back to fragmentation-only. - Fixed the
multifragment strategy timeout. The old 5-byte chunk size produced 100+ fragments with 0.1s delay each, causing a 10+ second stall before the handshake could even complete. Bumped to 24-byte chunks (~22 fragments), which keeps the fragment count reasonable while still splitting the SNI across multiple packets. - Added
raw_injector.py: the raw packet sniffer and injector module. Monitors the TCP handshake viaAF_PACKET, captures the SYN ISN and the 3rd ACK template, injects the fake ClientHello 1ms after the handshake completes, and confirms the server ignored it by watching for an ACK withack == ISN + 1. - Added
--no-rawCLI flag to disable raw socket injection even when running as root. - Platform capability detection now reports
af_packetandraw_injectionstatus. - Bumped version to 1.1.0.
- Fixed
supported_versionsTLS extension in the fake ClientHello builder. The version list length byte was encoded as two bytes (04 03) instead of one (04), which shifted every extension after it by one byte. This corruptedpsk_key_exchange_modes,key_share, andpadding, making the fake ClientHello malformed. Strict TLS parsers and some DPI systems would reject it outright. - Fixed the bidirectional relay task setup. The two relay directions (client-to-server and server-to-client) were referencing the wrong peer task during creation, which could cause one relay direction to fail to clean up when the other direction closed.
- Bumped version to 1.0.1.
MIT License. See LICENSE for the full text.
This project is a cross-platform conversion of patterniha's original Windows-only SNI-Spoofing tool.
The raw socket injection logic (seq_id trick) was ported from selfishblackberry177's Go reimplementation.