Stealth VPN with Outline over HTTPS

Running Outline VPN over HTTPS (port 443) to slip past censorship. Learn how to blend in with HTTPS traffic and what it actually takes to stay invisible.

WORDS: 1382 | CODE BLOCKS: 12 | EXT. LINKS: 18

What We Are Doing

If you are somewhere that blocks VPNs through deep packet inspection (DPI), SNI filtering, or crude port blocking, the goal is to make your VPN traffic look as boring as possible.
The quick win is to put it on port 443, the same port used by HTTPS. That is the lifeline of the modern web, so most firewalls are reluctant to block it.

But think of it as changing your outfit but not your walk: just putting Shadowsocks (which Outline runs under the hood) on port 443 does not make it indistinguishable from real HTTPS. To a modern DPI system, it still has a unique fingerprint. If you want to disappear in plain sight, you need to wrap it in actual TLS or another disguise layer.

Why Port 443 Matters and Its Limits

Port 443 is the default for encrypted web traffic. Blocking it would break Gmail, YouTube, Facebook, banking apps, and much more.
Using 443 for your VPN helps against simple port-based blocks. It does not stop advanced DPI, which can still detect Shadowsocks’ handshake patterns even inside encryption.

Think of it as changing your outfit but not your walk. Observers may not see your face, but they still recognize the way you move.

How Outline Works

Outline is a friendly manager and server wrapper for Shadowsocks. It runs a SOCKS5 proxy with strong AEAD encryption and offers an API for generating and revoking access keys.
It does not speak TLS natively. To mimic HTTPS convincingly, you must add a plugin or a front-end reverse proxy that handles TLS and passes traffic inside a valid protocol like WebSocket.

1. Provision Your Server

Choose a VPS provider that is not hostile to privacy. Good options include Hetzner, DigitalOcean, and Vultr. The Oracle free tier works if you enjoy tinkering, but it can be unreliable.
Stick to Ubuntu 20.04 or 22.04 for compatibility.

Open only these inbound ports for now:

1443/tcp   # VPN traffic disguised as HTTPS
253/udp    # DNS (for fallback only, see DNS notes below)

2. Install Outline Server

Run the official installer:

bash
1curl -sS https://getoutline.org/install.sh | sudo bash

This will:

  • Install Docker if needed
  • Set up the Outline Manager service
  • Give you an API URL and fingerprint for connecting

Manage the server using the Outline Manager desktop app.

3. Put It on Port 443

By default, Outline listens on a high random port. To change it to 443, you need to modify the Docker Compose configuration directly, as editing access.txt won’t change the listening port of the Shadowsocks server managed by Outline. The access.txt file is for access keys, not server configuration.

Edit the Docker Compose mapping to expose port 443 and ensure the Outline service inside the container listens on port 443.

First, access your server via SSH and open the docker-compose.yml file:

bash
1sudo nano /opt/outline/docker-compose.yml

Locate the ports section for the shadowsocks service and ensure it maps the external port 443 to the internal container port 443. It might look something like this initially, with a high random port:

yaml
1ports:
2  - "8080:8080" # Example, your random port will be different

Change it to:

yaml
1ports:
2  - "443:443"

Additionally, you might need to ensure the Shadowsocks configuration within the Docker container is also set to listen on port 443. While Outline typically handles this when it sets up the service, if you encounter issues, you may need to check Outline’s internal configuration files (though this is less common for simple port changes). For most setups, just changing the docker-compose.yml ports mapping is sufficient.

Restart:

bash
1docker-compose -f /opt/outline/docker-compose.yml down
2docker-compose -f /opt/outline/docker-compose.yml up -d

This gets you the “minimal camouflage” setup. It helps against basic censorship but still looks like Shadowsocks to advanced DPI.

Running Outline over HTTPS significantly enhances security and can help bypass certain network restrictions. This involves setting up a reverse proxy (like Nginx) to handle SSL/TLS encryption and forward traffic to your Outline server.

First, ensure you have a domain name (e.g., vpn.example.com) pointed to your server’s IP address.

Install Nginx and Certbot

bash
1sudo apt update
2sudo apt install nginx certbot python3-certbot-nginx -y

Obtain an SSL/TLS Certificate

Use Certbot to get a free SSL/TLS certificate from Let’s Encrypt for your domain. Replace vpn.example.com with your actual domain.

bash
1sudo certbot --nginx -d vpn.example.com

Follow the prompts. Certbot will automatically configure Nginx to use the certificate and set up automatic renewals.

Configure Nginx as a Reverse Proxy

Create a new Nginx configuration file for your domain:

bash
1sudo nano /etc/nginx/sites-available/vpn.example.com

Add the following configuration. This assumes your Outline server is listening on localhost:443 (as configured in the previous step). If you changed Outline’s port, adjust proxy_pass accordingly.

nginx
 1server {
 2    listen 80;
 3    server_name vpn.example.com;
 4    return 301 https://$host$request_uri;
 5}
 6
 7server {
 8    listen 443 ssl;
 9    server_name vpn.example.com;
10
11    ssl_certificate /etc/letsencrypt/live/vpn.example.com/fullchain.pem;
12    ssl_certificate_key /etc/letsencrypt/live/vpn.example.com/privkey.pem;
13    ssl_protocols TLSv1.2 TLSv1.3;
14    ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384';
15    ssl_prefer_server_ciphers off;
16
17    location / {
18        proxy_pass http://127.0.0.1:443; # Or your Outline's listening address and port
19        proxy_set_header Host $host;
20        proxy_set_header X-Real-IP $remote_addr;
21        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
22        proxy_set_header X-Forwarded-Proto $scheme;
23        proxy_buffering off;
24        proxy_max_temp_file_size 0;
25    }
26}

Enable the Nginx Configuration

Create a symbolic link to enable the new configuration and then test Nginx syntax and restart.

bash
1sudo ln -s /etc/nginx/sites-available/vpn.example.com /etc/nginx/sites-enabled/
2sudo nginx -t
3sudo systemctl restart nginx

With this setup, Nginx handles the HTTPS connection, decrypts the traffic, and forwards it to your Outline server, which can now listen on its internal port (e.g., 443) without directly managing SSL/TLS certificates.

5. Cloudflare Routing

Cloudflare does not proxy raw TCP over 443 unless you pay for Spectrum. The free plan works for HTTP, HTTPS, and WebSocket. Therefore, to route your Outline VPN traffic through Cloudflare’s free tier, your Outline client must use a WebSocket plugin.

Configure your Nginx (as described in the HTTPS section) to proxy WebSocket traffic to your Outline Shadowsocks server. This involves adding a location block in your Nginx configuration to handle a specific WebSocket path (e.g., /your_websocket_path) and proxy it to your Outline server’s internal address and port.

On the client side, ensure your Shadowsocks client is configured to use a WebSocket plugin (like v2ray-plugin) and the specified WebSocket path. For example, with Shadowsocks-Rust, your client configuration might look like this:

json
1{
2  "server": "your_domain.com",
3  "server_port": 443,
4  "password": "your_password",
5  "method": "chacha20-ietf-poly1305",
6  "plugin": "v2ray-plugin",
7  "plugin_opts": "tls;host=your_domain.com;path=/your_websocket_path"
8}

This setup makes your VPN traffic appear as regular web traffic to Cloudflare, further enhancing obfuscation and bypassing advanced deep packet inspection (DPI).

6. Connect Your Client

Download the Outline client for your platform:

Paste in your access key from the Outline Manager.
If you switch from raw IP to a domain for TLS, you must regenerate or edit the key to match the new hostname.

7. Test Your Stealth

Testing is more than “it connects”. Use these:

  • gfw.report to check if the handshake is blocked in censorship regions
  • Wireshark or mitmproxy to inspect packet patterns

Minimal camouflage will still show a Shadowsocks signature. Strong camouflage should be indistinguishable from normal HTTPS.

DNS warning: Many censorship regimes hijack or block UDP 53. For better stealth, configure DNS-over-HTTPS (DoH) or DNS-over-TLS (DoT) on your device or in the Outline client.

Active Probing Defense

In some places, censors will connect to your IP on 443 and try to speak Shadowsocks. If your server replies, they blacklist you.
Plugins like Cloak or nginx fronting can drop these connections unless they match a known pattern.

Summary

  • Port 443 is a good first step, but on its own it only beats basic port blocking.
  • For serious stealth, add a TLS or WebSocket transport with a valid certificate.
  • Cloudflare routing only works if traffic is inside HTTP or WebSocket.
  • Protect against active probing in high-risk environments.

References and Further Reading

If you only need to get around crude blocks, changing Outline to port 443 might be enough. If you are under active, sophisticated censorship, you will need to dress it up in TLS and maybe hide it behind something bigger like Cloudflare. I will try this idea and write another blog detailing the journey.

The more it looks and acts like normal HTTPS, the longer it will survive.