NaiveProxy + Caddy 2

NaiveProxy is an anticensorship tool that uses Chrome’s network stack to better camouflage your traffic. Recent versions of NaiveProxy (we are currently on NaiveProxy 85) are implemented as customized builds of Caddy 2’s ForwardProxy plugin. Caddy 2 is in turn a major revision of Caddy 1.

Prebuilt clients for NaiveProxy are available for Linux, OpenWRT, macOS, and Windows.

In this article, you’ll learn how to install and configure NaiveProxy and Caddy 2 on a Debian server and client. You could equally use a recent version of Ubuntu. We sometimes refer to the server or client generically as Debian/Ubuntu.

1. Set Up Server

1.1. Get Domain Name and Server

Before you set up your NaiveProxy server, you’ll need to acquire a domain name. If you do not already have a domain name registrar, you can check out Freenom (free) or Porkbun (paid) or any other registrar of your choosing.

You’ll also need a virtual private server (VPS). If you do not already have a VPS provider, you can check out Linode or Google or any other provider of your choice.

At your domain name registrar, create DNS type A records pointing from the naked domain and the www subdomain to the public IP address of your VPS. You’ll need to allow some time for the DNS addition to propagate. On a PC, you can check to see if the propagation has happened by looking up your domain and subdomain. For example, if your domain name is, then open a command prompt and issue the commands:


1.2. Install and Configure Firewall

There are multiple ways to implement a firewall: nftables, iptables, ufw, and firewalld. The modern way is nftables, and that is what we will use here.

SSH into your server. Issue each of the following commands in turn to install and start nftables:

apt update && apt upgrade -y
apt install nftables -y
systemctl enable nftables
systemctl start nftables

Configure the firewall to accept related traffic, internal traffic, and ping requests:

nft add rule inet filter input ct state related,established counter accept
nft add rule inet filter input iif lo counter accept
nft add rule inet filter input ip protocol icmp icmp type echo-request counter accept

Open port 22 for SSH. If you can restrict the port 22 rule so that only certain source IP addresses are whitelisted for SSH access, then so much the better.

nft add rule inet filter input tcp dport 22 counter accept

Open ports 80 and 443 for HTTP and HTTPS requests, respectively:

nft add rule inet filter input tcp dport { http, https } counter accept

Drop all unexpected input:

nft add rule inet filter input counter drop

Save the rules:

nft list ruleset > /etc/nftables.conf

1.3. Implement BBR

Bottleneck Bandwidth and Round-trip propagation time (BBR) is a TCP congestion control algorithm developed at Google. Under certain types of network congestion, it will improve your latency. Implement BBR TCP congestion control on your server with the following commands:

cat >> /etc/sysctl.d/50-bbr.conf <<EOF
sysctl -p /etc/sysctl.d/50-bbr.conf

1.4. Create Camouflage Website

So that your server can pass even a manual inspection, install a few pages of web content.

mkdir -p /var/www/html
apt install wget zip unzip -y
cp -rf sample-blog-master/html/* /var/www/html/

1.5. Install Go Language

On your PC, open Firefox and visit Normally you would download the most recent version of Go for 64-bit Linux. At the time of writing, this is named go1.15.1.linux-amd64.tar.gz. However, Go version 1.15 causes an error when you try to run Caddy, qtls.ClientSessionState not compatible with tls.ClientSessionState. Therefore use go1.14.8.linux-amd64.tar.gz

In your SSH session with your server, download the archive for the Go language:


Extract the archive into /usr/local:

tar -C /usr/local -xzf go1.14.8.linux-amd64.tar.gz

Add /usr/local/go/bin to your executable PATH by editing the system-wide profile:.

vi /etc/profile

Add a line:

export PATH=$PATH:/usr/local/go/bin

Save the file. Log out and log in again:


Check that the Go language is installed by issuing the command:

go version

You should see a response that looks like this:

go version go1.14.8 linux/amd64

1.6. Get Source for ForwardProxy

Get the source code for the NaiveProxy version of Caddy’s ForwardProxy:

apt install git -y
git clone -b naive

1.7. Custom Build of Caddy 2

Download and install packages and dependencies for custom builds of the Caddy 2 web server:

go get -u

Use xcaddy to build Caddy with the ForwardProxy plugin, as modified by the NaiveProxy project, which you just downloaded:

~/go/bin/xcaddy build --with$PWD/forwardproxy

The download and build will take several minutes. When it finishes, copy the Caddy binary into a more central location:

cp caddy /usr/bin

1.8. Allow Caddy to Bind to Privileged Ports

Allow Caddy to bind to “privileged” ports (i.e. TCP/IP port numbers below 1024):

setcap cap_net_bind_service=+ep /usr/bin/caddy

1.9. Generate Strong Password

Generate a strong password to access your proxy server:

cat /dev/urandom | tr -dc a-zA-Z0-9 | head -c32; echo;

The example result that we will use in the rest of this tutorial:


1.10. Configure Caddy with Forwardproxy

Edit the Caddy configuration JSON file:

mkdir /etc/caddy
vi /etc/caddy/config.json

Use the template below, making these changes:

  "admin": {"disabled": true},
  "logging": {
    "sink": {"writer": {"output": "discard"}},
    "logs": {"default": {"writer": {"output": "discard"}}}
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [":443"],
          "routes": [{
            "handle": [{
              "handler": "forward_proxy",
              "hide_ip": true,
              "hide_via": true,
              "auth_user": "yourname",
              "auth_pass": "MFUZ4eMngSRJ9888tvM8S9HjEULm2Ptj",
              "probe_resistance": {"domain": ""}
          }, {
            "match": [{"host": ["", ""]}],
            "handle": [{
              "handler": "file_server",
              "root": "/var/www/html"
            "terminal": true
          "tls_connection_policies": [{
            "match": {"sni": ["", ""]}
    "tls": {
      "automation": {
        "policies": [{
          "subjects": ["", ""],
          "issuer": {
            "email": "",
            "module": "acme"

Save the file.

1.11. Create Systemd Service File

Create a systemd service file for Caddy:

vi /usr/lib/systemd/system/caddy.service

Insert contents like this:


ExecStart=/usr/bin/caddy run --config /etc/caddy/config.json


Save the file.

1.12. Run Caddy on Server

Start Caddy with the NaiveProxy version of ForwardProxy, check that it is working, then exit your session with the server:

systemctl enable caddy
systemctl start caddy
systemctl status caddy
ss -tulpn | grep caddy

You can check that the camouflage website is in place by opening Firefox on your PC and visiting your host, e.g.,

2. Set Up Client

2.1. Download Caddy Client

Now go to work on your PC. Open Firefox. Visit the releases page for NaiveProxy.

In Firefox, download the latest release of the NaiveProxy client for 64-bit Linux. For example, at the time of writing it is named naiveproxy-v85.0.4183.83-2-linux-x64.tar.xz.

In a terminal, extract the archive:

cd ~/Downloads
tar -xf naiveproxy-v85.0.4183.83-2-linux-x64.tar.xz

2.2. Configure Caddy Client

Edit the configuration file for the Caddy client:

cd naiveproxy-v85.0.4183.83-2-linux-x64
vi config.json

Use the template below, making these changes:

"listen": "socks://",
"proxy": ""

Save the file.

2.3. Run NaiveProxy

In your terminal on the client, issue the command:

./naive config.json

Leave the terminal open, with the NaiveProxy client still running in it.

2.4. Configure Firefox

Open Firefox. From the hamburger menu, select Preferences > General. Scroll down to Network Settings. Click the Settings button.

2.5. End-to-end Test

Open Firefox.

Visit IP Chicken.

You should see the IP address of your remote server, not your local client.

3. Get Help and Report Issues

Help is available from the Caddy community. Report any NaiveProxy issues on GitHub.