OxidProxy — User Guide

Document version: 1.15
Software version: 2.6.2
Website: https://oxidproxy.com
Support: support@oxidproxy.com · Telegram

TipYou do not have to read any of this. Hand the AI agent instruction to any agent with SSH access to the server — it will ask what it needs and deploy the server itself. The guide below is for later, when you want to understand the settings.

Contents

  1. Product overview
  2. System requirements
  3. Installation
    - 3.1 Automatic installation (recommended)
    - 3.2 Manual installation
    - 3.3 What the installer creates
    - 3.4 Upgrading and removing
  4. Initial setup
  5. Configuration (config.yaml)
    - 5.1 Licensing
    - 5.2 Network settings
    - 5.3 OS fingerprinting
    - 5.4 IPv6 subnets and rotation
    - 5.5 API and user management
    - 5.6 DNS resolver
    - 5.7 Traffic shaping
    - 5.8 Timeouts
    - 5.9 Service account
    - 5.10 Blacklist
    - 5.11 Logging
    - 5.12 MITM mode (mitm_listeners)
    - 5.13 Dashboard branding
  6. HTTP API
  7. Web dashboard
  8. Service management
  9. Logging and monitoring
  10. Command line (CLI)
  11. Troubleshooting
  12. FAQ
  13. Changelog

1. Product overview

OxidProxy is a high-performance SOCKS5(h) / HTTP CONNECT IPv6 proxy server written in Rust. It is meant to run on your own dedicated or virtual server and acts as a gateway: it accepts incoming connections over IPv4, authenticates them and routes traffic through the IPv6 subnets you configure.

Key features

Feature Description
SOCKS5 / SOCKS5h Full protocol support, including remote DNS, which prevents DNS leaks
HTTP CONNECT HTTP(S) proxy mode via the CONNECT method, for browsers, curl and system proxy settings
Hybrid Mode Automatic protocol detection (SOCKS5 or HTTP) on a single port
UDP / QUIC Full UDP proxying, including QUIC (HTTP/3)
Multiple subnets Attach several IPv6 subnets and balance between them by weight
Lazy rotation The IP changes only on re-authentication; active sessions are never dropped
Smart DNS Failover Built-in resolver with balancing and automatic failover to backup servers
OS Fingerprinting (p0f) TCP/IP fingerprint masking as Linux, Android, Windows, iOS or macOS
Traffic Shaping Total bandwidth limiting with burst support
HTTP(S) API Instant credential generation, statistics and management over a REST API
IP Whitelist Manage allowed addresses and subnets from the dashboard; system entries are protected
Web dashboard Built-in monitoring panel with traffic charts, connection analytics and selectable themes
Blacklist Block domains and IP addresses through blacklist.txt
Service account A restricted account intended for checkers and monitoring

How it works

   Your software      OxidProxy Server           IPv6 subnets         Internet
  (SOCKS5h/HTTP)  →   Auth • DNS • Shaping   →  Subnet A (w:100)  →  Target
                                                 Subnet B (w:50)
ImportantFor maximum anonymity your client must use SOCKS5h (remote DNS). In that mode DNS queries are resolved on the OxidProxy server rather than on your own machine.
NoteOxidProxy routes traffic over IPv6 only. The target resource must support IPv6.

2. System requirements

Item Minimum Recommended
OS Debian 12 (Bookworm) or Debian 13 (Trixie) x64 Debian 12 (Bookworm) or Debian 13 (Trixie) x64
CPU 1 core 2 or more cores
RAM 512 MB 1 GB or more, depending on max_accounts
IPv6 a /64 subnet a /48 or /32 subnet
Privileges root, for installation The service itself runs as the oxidproxy user
Virtualization A KVM, VMware, Xen or Hyper-V virtual machine, or a dedicated server Containers such as OpenVZ or LXC are not supported: they cannot boot a kernel of their own
Boot loader GRUB The installer sets up the custom kernel through GRUB and stops without it
Disk About 1 GB free, and 150 MB in /boot if it is a separate partition 5 GB or more, to leave room for the journal and logs
NoteOS fingerprint masking (p0f) requires the custom kernel: 6.1.187-oxidproxy-p0f on Debian 12, 6.12.111-oxidproxy-p0f on Debian 13. The installer picks the right one. Without the custom kernel every other feature still works normally.

3. Installation

ImportantInstall OxidProxy on a clean, fully updated Debian 12 (Bookworm) or Debian 13 (Trixie). The custom kernel shipped with the server provides OS fingerprinting, but it is not universal: on some hardware the required drivers may be missing and the machine will not boot after the kernel is installed. The kernel is not signed, so Secure Boot must be off; the installer checks this before installing the kernel. If the server does not come back after a reboot, contact support at support@oxidproxy.com or on Telegram.

Run as root. If the server has no curl (a minimal Debian 13 image does not), install it first:

apt-get update && apt-get install -y curl

Checking the server before installing (optional). The check script looks at whether the server is suitable: the system, kernel, boot loader, IPv6 network, free ports, and whether your subnet reaches the server. It installs nothing and does not change any settings. At the end it prints a verdict — ready, ready with warnings, or not ready — and what to fix.

If you already have your IPv6 subnet, put it in place of the example; for several subnets, give one --subnet per subnet:

curl -fsSL -o v6check.sh https://oxidproxy.com/downloads/v6check.sh
bash v6check.sh --subnet 2a01:1234::/48

If you do not have a subnet yet, run it without one: bash v6check.sh. The result will be incomplete: the main requirement — whether the subnet reaches the server — stays untested, and the verdict will be at best "READY, with warnings". Run the check again once you have the subnet.

Then start the installer:

curl -fsSL https://oxidproxy.com/downloads/install.sh | bash

The script will:
1. Check that it runs as root on Debian 12 or 13 for x86_64, and refresh the package lists
2. Download and install the custom Linux kernel with the p0f patch, built for your Debian release, after checking that Secure Boot is off. A kernel the server already has is not downloaded again
3. Configure GRUB to boot the new kernel by default, and warn if another kernel would boot instead
4. Install logrotate and nftables, then the OxidProxy package. The package creates the oxidproxy system user and the systemd service, enabled at boot but not started yet
5. Ask whether to install the login banner. On every SSH login as root it shows the service state, the kernel, the networks and the dashboard address; Debian's own login text is turned off, and the original is kept as /etc/motd.debian-backup
6. Offer to reboot the server when needed: after a kernel was installed, or if the server runs a different kernel

Without a terminal (from a script, or an agent over SSH) the script answers "no" to both questions. To answer in advance, set OXID_MOTD for the banner and OXID_REBOOT for the reboot to y or n:

curl -fsSL https://oxidproxy.com/downloads/install.sh | OXID_MOTD=y OXID_REBOOT=n bash

3.2. Manual installation

  1. Download the .deb package from the downloads page
  2. Install the package together with its dependencies, logrotate and nftables, which minimal images may lack:
apt-get update
apt-get install -y logrotate nftables
dpkg -i oxidproxy_*.deb
# If dependencies fail:
apt-get install -f -y
  1. Optionally, install the custom kernel for p0f. The commands below are for Debian 12; on Debian 13 use the automatic installer (section 3.1): it installs 6.12.111-oxidproxy-p0f and keeps it first in GRUB, ahead of Debian's own kernels.
dpkg -i linux-image-6.1.187-oxidproxy-p0f_6.1.187-oxidproxy-p0f-1_amd64.deb
update-grub
NoteThe linux-headers package for 6.1.187-oxidproxy-p0f (Debian 12) is available on the downloads page. It is needed to build out-of-tree kernel modules.

3.3. What the installer creates

Path Purpose
/usr/bin/oxidproxy The executable
/etc/oxidproxy/config.yaml Main configuration file
/etc/oxidproxy/blacklist.txt List of blocked domains and addresses
/etc/oxidproxy/whitelist.txt Whitelist of allowed addresses and subnets
/etc/rc.local Boot script with a commented-out subnet route line for you to fill in (section 4, step 2); created if absent, an existing file gets the line added
/etc/sysctl.d/90-oxidproxy.conf Kernel settings (sysctl)
/etc/sysctl.d/89-oxidproxy-ra.conf Only on servers whose IPv6 default route comes from router advertisements: keeps that route working once forwarding is on
/usr/lib/systemd/system/oxidproxy.service systemd unit file
/etc/logrotate.d/oxidproxy Log rotation configuration
/usr/lib/systemd/system/oxidproxy-logrotate.timer Rotates the log files in /var/log/oxidproxy every hour, together with oxidproxy-logrotate.service
/var/log/oxidproxy/ Log directory
/var/lib/oxidproxy/ Data directory: exported account files, the server CA (ca/) and the HTTPS API certificate (api-tls/), created by the service
/usr/share/doc/oxidproxy/README.txt Short reference to the settings
/boot/vmlinuz-*-oxidproxy-p0f The custom kernel, package linux-image-<version>-oxidproxy-p0f
/etc/default/grub.d/99-oxidproxy-p0f.cfg Debian 13 only: keeps the newest custom kernel first in GRUB, ahead of Debian's own kernels
/etc/profile.d/99-oxidproxy-status.sh
/usr/local/sbin/oxidproxy-status
/usr/local/sbin/dashboard-url
The login banner and the dashboard-url command, which prints the full dashboard link; only if you agreed to the banner

3.4. Upgrading and removing

Upgrading. Run the installer again, with the same command as in section 3.1. It replaces the package and downloads a kernel only if the release has one this server does not have yet. It asks about the login banner again; answering "no" leaves an installed banner in place. To upgrade by hand, install the new package over the old one:

dpkg --force-confold -i oxidproxy_*.deb
# If dependencies fail:
apt-get install -f -y

Removing.

apt remove oxidproxy

stops the service, takes it out of autostart and removes the program. The configuration, account files, the CA and the logs stay: installing the package again picks them up, with the same logins and passwords. If the service was enabled at boot before, it is enabled again; start it with systemctl start oxidproxy.

apt purge oxidproxy

removes the rest as well: /etc/oxidproxy (the config with the license key and tokens, the lists), /var/lib/oxidproxy (account files, the CA), /var/log/oxidproxy, the files /etc/sysctl.d/90-oxidproxy.conf and 89-oxidproxy-ra.conf, and the profile delays (latency_base_ms) on the network interfaces. Account files and logs that the config put elsewhere (accounts_file, log.dir) are not touched.

Not removed even by purge:

CautionAfter apt purge a new installation starts from scratch: the config is the template again, and all logins, passwords and the server CA are new. Clients need new account files, and devices that use MITM ports must trust the new CA.

4. Initial setup

Right after installation the service is not running yet. It is enabled at boot and starts by itself after the reboot in step 4, so complete steps 1 to 3 first:

The server checks these values when it starts. If license_key, master_token or public_ip still hold the values from the template, the service says which one and does not start, so a half-configured server never goes live.

Step 1. Edit the configuration

nano /etc/oxidproxy/config.yaml

These must be filled in:
- license_key — your license key, obtained via Telegram
- public_ip — the external IPv4 address of your server
- ipv6_subnets — your IPv6 subnets
- master_token — the secret API token; replace the default

Step 2. Set up IPv6 routing

Edit /etc/rc.local and uncomment the route line:

nano /etc/rc.local

Replace the placeholder with your real subnet:

ip -6 route add local 2a01:xxxx::/32 dev lo
CautionWithout correct IPv6 routing the proxy cannot bind to addresses from the configured subnets.

Step 3. Validate the configuration

oxidproxy -t /etc/oxidproxy/config.yaml

It should print ✅ Syntax OK. followed by a summary of the main settings.

Step 4. Reboot the server

reboot

Required to activate the custom kernel and the routing rules.

Step 5. Check the service

After the reboot the service is already running. Check it:

systemctl status oxidproxy

The status should read active (running). If you did not reboot (for example, the custom kernel was already running), start it yourself: systemctl start oxidproxy.


5. Configuration (config.yaml)

Every setting lives in /etc/oxidproxy/config.yaml. Restart the service to apply changes.

TipAlways validate the configuration with oxidproxy -t before restarting.

5.1. Licensing

license_key: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

# license_servers: optional - see the note below
Parameter Description
license_key Required. The license key you received on purchase
license_servers Optional. The regular endpoints are built into the server, so there is nothing to set here. Fill it in only with an address given to you by support, for instance when the usual endpoints are unreachable from your network. Entries here are tried first.
Note

About the license server addresses. They no longer belong in the config: they are built into the server and spread across two independently registered domains, so that one becoming unreachable does not stop license checks.

The license_servers parameter is needed in one case only: support has given you a dedicated address. Such addresses are tried before the built-in ones, so a working endpoint takes effect immediately, without waiting through timeouts on unreachable ones.

license_servers:
  - "address.given.by.support:8000"

Order of attempts: addresses from the config first, if any, then the built-in ones. A server that answers successfully moves to the front of the list and is used for subsequent checks.

ImportantThe server will not start without a valid license. To check a specific endpoint, run oxidproxy --ping address:port — useful when support has given you a dedicated address and you want to confirm it is reachable from your network. The built-in endpoints need no manual checking; the server walks them itself.

5.2. Network settings

listen_ip: "0.0.0.0"
public_ip: "123.45.67.89"
# ext_iface: "eth0"  # optional
Parameter Description Default
listen_ip Required. IPv4 address the proxy ports accept connections on; 0.0.0.0 means all addresses, and the template sets it. IPv6 is not accepted here, and the server does not start with it: clients connect over IPv4. The API always listens on all interfaces —
public_ip Required. Public IPv4 address of the server, used by the API and in generated links —
ext_iface External interface used for QoS. Detected automatically when not set auto

5.3. OS fingerprinting

The listeners section defines several ports, each with its own TCP/IP fingerprint profile. Anti-fraud systems that inspect traffic with tools such as p0f will see the parameters characteristic of the chosen operating system.

Use ports 1024 and above. The packaged service runs as the oxidproxy user and cannot open a port below 1024: such a port stays closed, and oxidproxy -t warns about it. The same applies to mitm_listeners, api_port and api_port_https.

Every listener supports a mode field that selects the protocol:

Mode Description
socks SOCKS5 / SOCKS5h (default)
http HTTP CONNECT proxy, using the CONNECT method with Proxy-Authorization: Basic
hybrid Auto-detect: SOCKS5 and HTTP on one port. The protocol is determined from the first byte
listeners:
  - port: 1080
    os_preset: "linux"
    # mode: "socks"  # the default, may be omitted

  - port: 1081
    os_preset: "android"
    mode: "hybrid"    # accepts both SOCKS5 and HTTP CONNECT

  - port: 1082
    os_preset: "windows"
    mode: "http"

  - port: 1083
    os_preset: "ios"
    mode: "socks"

  - port: 1084
    os_preset: "macos"
    mode: "http"
Tiphybrid is the recommended mode. One port serves both protocols, so no extra ports need opening, and detection is instant, from the first byte.

Available profiles:

Profile TCP MSS Congestion Control Description
linux System default cubic A standard Linux server
android Common for the platform bbr A mobile Android client
windows Common for the platform cubic A Windows machine
ios Common for the platform cubic iPhone / iPad
macos Common for the platform cubic A macOS desktop
NoteTCP MSS is not a fixed number. For every profile that emulates an operating system, the value is chosen from those common on the networks that platform is normally used on, and tied to the specific outgoing IPv6 address. It does not change within a session, so behavior stays predictable. When the address rotates the value may change — exactly as it does on a real device moving between networks. The linux profile emulates nothing and keeps the system value.

Fine-tuning (for experienced users only):

Each profile sets these values by itself, and the configuration template does not list them under the ports. To change one, add the key under the port; without the key the profile's value applies.

Key What it sets Values Default
tcp_congestion Congestion control algorithm Any algorithm loaded in the kernel: cubic, bbr and so on. Case does not matter: BBR is read as bbr bbr for android, cubic for the rest
tcp_window_size Socket buffer in bytes, a speed ceiling for one connection A number. Values above 8388608 (8 MB) add nothing: the package sets the system limit for socket buffers to 8 MB 1 MB android, 2 MB ios, 4 MB windows and macos; not set for linux
latency_base_ms Extra delay of the access network, ms. Applies per profile, not per port: give every port of one os_preset the same value, counting a port without the key as 5. If the values differ, only one of them takes effect, and oxidproxy -t warns 0 and up; 0 means none 5 for android, ios, windows and macos; not applied to linux
tos IPv6 traffic class auto; a number from 0 to 255 pins one value on the whole port; a list such as [0, 32, 40] is your own mix auto: every outgoing address gets its own value, the way real devices differ by access network
  - port: 1081
    os_preset: "android"
    mode: "hybrid"
    tos: [0, 32, 40]      # your own mix instead of auto

The old template values tos: 10 (android) and tos: 8 (ios, macos) work as auto. Any other single number except 0 puts the same class on every connection of the port. Real devices do not behave like that, so oxidproxy -t warns about it. An empty list (tos: []) is an error. The two lowest bits of a value are not sent on TCP connections: the kernel controls them. UDP is marked only when a single number is set.

NoteAn unknown os_preset is a configuration error: that port is not opened, the other ports keep working, and oxidproxy -t reports it with exit code 1. Valid profiles: windows, macos, ios, android, linux. A port without os_preset gets linux.
CautionChanging the fine-tuning parameters without understanding them can destabilize the server or weaken the masking. If in doubt, leave the defaults alone.

5.4. IPv6 subnets and rotation

ipv6_subnets:
  - cidr: "2a01:xxxx::/32"
    weight: 427
  - cidr: "2a02:yyyy:yyyy:yyyy::/64"
    weight: 16

rotation_interval: 10
Parameter Description
cidr IPv6 subnet in CIDR notation. Any size is supported: /29, /32, /48, /64 and so on
weight Subnet weight, required for every subnet. The higher the value, the more often the subnet is used for outgoing addresses. A subnet with weight 0 is not used; at least one subnet needs a weight above 0
rotation_interval Rotation interval in minutes. 0 disables rotation, and the IP is kept until restart

How rotation works:
- The IP address is bound to a login and password pair (a sticky session)
- On every authentication the server checks whether rotation_interval has elapsed
- If it has, a new IPv6 is generated from the subnets, honoring their weights
- Active connections are never dropped — the IP changes only on a new connection
- A session idle for more than 5 minutes is closed; this is tunable via timeouts.idle

TipExample: if subnet A has weight 100 and subnet B has weight 50, subnet A will supply addresses twice as often.

5.5. API and user management

api_port: 8080
api_port_https: 8443  # optional - HTTPS API

master_token: "my-secret-token"

max_accounts: 100000

accounts_file: "/var/lib/oxidproxy/accounts_[os]_[mode]_[port].txt"

account_seed: 8312921461266748753  # written by the installer
Parameter Description Default
api_port Required. HTTP API port, 1024 or above; the template sets 8080 —
api_port_https HTTPS API port, 1024 or above. Optional: without it there is no HTTPS API; the template sets 8443 —
master_token Required. Secret token used to authenticate API requests. At least 12 bytes (12 Latin letters or digits), and the template value is refused: otherwise the server does not start. Use Latin letters, digits and - . _ ~. Any other character has to be URL-encoded in a ?token= link you write by hand, and oxidproxy -t warns about it. The dashboard and the links in the log encode the token themselves —
max_accounts Required. Maximum number of unique accounts generated, each with its own IPv6; the template sets 100000 —
accounts_file Path for exported accounts. Supports the [port], [os] and [mode] macros. When the configuration produces more than one file — several ports, a hybrid port or a MITM port — all three macros are required, otherwise the server does not start accounts.txt (a single file only). The path is relative, so the packaged service puts the file in /var/log/oxidproxy. Set an absolute path, as the template does
account_seed Generation seed: logins and passwords derive from it and stay the same across restarts. Without the key, every start draws new logins and passwords, and the ones already handed out stop working. Changing it regenerates all accounts set by the installer, unique per server
TipKeep account_seed as the installer set it: logins and passwords stay the same across restarts and updates, so external systems never need new credentials. When you copy the configuration to another server, give that server its own value.

5.6. DNS resolver

Names are resolved by the server itself. One cache is shared by all listeners, so a name looked up for one client is already resolved for the next. Servers carrying a weight share the load between them; those without one are used only when the weighted ones do not answer. The resolver keeps track of how each server behaves and stops sending queries to one that stopped answering, returning to it a little later. When a primary server has not answered within 300 ms, a failover server (one without a weight) is queried in parallel and the first answer wins, so a single slow server does not hold the client up. Keep at least one failover server: with weighted servers only there is nothing to fall back to.

timeout is what a single server is given to answer, not what the client waits: with the parallel query above the client normally gets an answer sooner. Its job is to decide how quickly a server that went quiet is taken out of rotation, so it is kept short. Raise it to 2 if healthy servers are being taken out. What each server is doing right now is visible in /stats, section 6.2.

dns:
  timeout: 1
  cache_size: 8000
  suppress_aaaa_not_found: true

  servers:
    # primary servers (balanced)
    - address: "[2606:4700:4700::1111]:53"
      weight: 53
    - address: "1.1.1.1:53"
      weight: 7

    # failover servers
    - address: "[2620:fe::fe]:53"
    - address: "9.9.9.9:53"
Parameter Description Default
timeout Time a single server is given to answer, in seconds 1
cache_size Number of cached DNS records 1024
suppress_aaaa_not_found Suppress warnings about domains that have no AAAA record false
servers Required, at least one server. address is an IP address and a port only, IPv6 in square brackets: [2606:4700:4700::1111]:53; a host name is not accepted. A server with a weight above 0 is a primary; one without a weight, or with weight: 0, is a failover —

How it works:
1. The query goes to one of the primary servers, chosen by weight — the higher the weight, the more often that server is used. There may be several primaries.
2. If a primary does not answer, whether by timeout or error, the query falls through to the failover pool. There may be several of those too; they are listed without a weight.
3. A domain that exists but has no AAAA record is not a connection error, so failover is not triggered

TipRecommended: add your hosting provider's resolvers with a high weight. Their addresses are usually in /etc/resolv.conf, or can be obtained from the provider's support. Provider resolvers typically answer in 1–2 ms, against 10–50 ms for Google or Cloudflare.

5.7. Traffic shaping

shaping:
  rate: 12500000   # 100 Mbit/s
  burst: 25000000  # 200 Mbit/s burst
Parameter Description
rate Maximum rate in bytes per second. 0 means unlimited
burst Burst allowance in bytes, letting transfers briefly exceed rate. Recommended: rate × 2. 0 means one second of rate. A value below 65536 is raised to 65536 (or to rate, if rate is lower). Ignored when rate is 0
NoteThe limit applies to the server as a whole — all users, both directions. The same figure is enforced separately for upload and download. The shaping section is optional; when it is present, both rate and burst are required.

Cheat sheet:

Speed rate value
1 Mbit/s 125 000
10 Mbit/s 1 250 000
100 Mbit/s 12 500 000
1 Gbit/s 125 000 000

5.8. Timeouts

timeouts:
  shutdown: 10    # how long to wait for sessions to finish on shutdown, sec
  idle: 300       # maximum session idle time, sec
Parameter Description Default
shutdown How long, in seconds, to wait for active connections to finish when the service stops or restarts. Keep it under 85: systemd kills the service if it is still running 90 seconds after the stop request 15
idle Maximum idle time of a TCP or UDP session before it is closed, in seconds. At least 1: with 0 the server does not start 300 (5 min)

5.9. Service account

service_account:
  enabled: true
  login: "checker"
  password: "strong_password"
  allowed_domain: "ifconfig.me"

The service account is meant for monitoring and health checks. Its properties:
- Static login and password: neither generated nor rotated
- Can reach only one domain, set by allowed_domain
- Does not consume rotation limits
- Never changes its IP

The section is required with all four keys, even when you do not need the account: set enabled: false to turn it off. Without the section or one of its keys the server does not start.

TipIdeal for external checkers: set allowed_domain: "ifconfig.me" and verify the outgoing IP with curl --proxy socks5h://checker:password@IP:PORT ifconfig.me. Use socks5h: the name has to reach the proxy, because the account only accepts allowed_domain by name.

5.10. Blacklist

blocked_file: "/etc/oxidproxy/blacklist.txt"

The file lists blocked domains (subdomains included), IP addresses and IPv4 or IPv6 networks in CIDR notation, one per line. Comments start with #. The list applies in every mode, including the browser mode.

The blocked_file key is required. If the file itself is missing, the server starts without a blacklist and reports it in the log.

# blocking ad domains
doubleclick.net
ads.google.com

# blocking private IPv6 networks
fc00::/7
fe80::/10
ImportantBlocking covers subdomains. If google.com is blocked, then mail.google.com and ads.google.com are unreachable as well.

5.11. Logging

By default the server logs to the systemd journal only; read it with journalctl -u oxidproxy. In the template the dir line is commented out:

log:
  # dir: "/var/log/oxidproxy"
  level: "info"
Level Description
error Critical errors only
warn Errors and warnings
info Main events: connections, DNS, rotation — recommended
debug Verbose debugging: every session, every DNS query

Uncomment dir to write files as well. Two files are produced:
- oxidproxy.log — the full log, at the verbosity set by level
- error.log — errors only, always

The package rotates files only in /var/log/oxidproxy: logrotate, installed together with the package, does it every hour and keeps the last three. Files in any other directory are not rotated and grow without limit; oxidproxy -t and the service at startup warn about it. Keep dir: "/var/log/oxidproxy", or set up logrotate for your directory yourself.

To check where the log goes: oxidproxy -t reports Log Dir: not set, logging to journal only or the directory in use.

5.12. MITM mode (mitm_listeners)

In MITM mode OxidProxy terminates the client TLS connection with its own certificate authority and rebuilds the request to the target with the network fingerprint of a real browser: JA4 in full, HTTP/2 and Akamai. The profile is chosen automatically from the client User-Agent, so one port serves every OS and browser — Chrome, Firefox and Safari on Windows, macOS, Linux, Android and iOS.

MITM listeners are declared in their own mitm_listeners section and run alongside the regular listeners:

mitm_listeners:
  - port: 9443
    mode: "hybrid"             # socks (default) / http / hybrid
    profiles:
      - "windows-chrome"
      - "windows-firefox"
      - "macos-safari"
      - "android-chrome"
      - "ios-safari"
    default: "windows-chrome"  # used when no listed profile matches the client's OS
    # strict: false            # true = refuse instead of substituting anything
    # allow_unknown_ua: false  # true = also serve clients with no browser UA

Profiles are named <os>-<browser>. The available ones are:

OS Profiles
Windows windows-chrome, windows-firefox, windows-edge
macOS macos-chrome, macos-firefox, macos-safari
Linux linux-chrome, linux-firefox
Android android-chrome, android-firefox
iOS ios-safari

Listener fields:

Parameter Description
port Port of the MITM listener
profiles List of allowed <os>-<browser> profiles. The right one is chosen from the client User-Agent
default Fallback profile. A recognized browser that is not in profiles first gets another profile of the same OS from the list; only when the list has none for that OS does it get default. Must be one of profiles, otherwise the server does not start
strict When true, refuse any browser that is not in profiles instead of substituting another profile or default. Defaults to false
allow_unknown_ua Clients with no recognizable browser User-Agent — curl, scripts, your own software — are refused by default with a short page explaining why. true serves them with the default profile (not with strict: true). Defaults to false
mode Listener protocol: socks (the default), http or hybrid, exactly as for regular listeners. When omitted, the port runs in socks mode.
ImportantBecause TLS is terminated with the server's own CA — each server generates its own on first start, and its key never leaves the server — the client must add the server CA certificate to its trust store once; otherwise the browser reports a certificate error. The CA can be downloaded from the server at the /ca endpoint.

5.13. Dashboard branding

The web dashboard can carry your own mark instead of ours. This exists for resellers: a customer's panel has no business advertising the vendor behind it.

brand:
  name:   "Acme"
  accent: "Proxy"
  url:    "https://acme.example"
Parameter Description
name First half of the wordmark, shown in the main text color. Omitted, it is Oxid
accent Second half, painted in the accent color of whichever theme is selected. Leave it empty (accent: "") for a single-color wordmark; omitted, it is Proxy
url Where the wordmark links to. An empty value (url: "") drops the link entirely; omitted, it links to https://oxidproxy.com. Only http:// and https:// are accepted

The setting reaches further than the header. It also renames the browser tab, the theme label in the selector, and the file name of the CA certificate the dashboard offers for download. The full name (name + accent) also goes into the certificates the server creates from then on: a new MITM CA reads AcmeProxy MITM Root CA with the example above, and the HTTPS API certificate carries the same name. Certificates that already exist keep their names: a renamed CA would be a new CA, and every client would have to install it again.

NoteThe section is optional. Omit it and the panel carries the OxidProxy mark, as it did before. The two halves of the name together may not exceed 64 characters, in any language.

6. HTTP API

The API is reachable at http://YOUR_IP:PORT or https://YOUR_IP:PORT, where PORT comes from api_port and api_port_https in the configuration — 8080 and 8443 in the template. Without api_port_https there is no HTTPS API.

Requests are authenticated with the token parameter, whose value is master_token.

Every endpoint answers with a standard HTTP status: 200 when the request succeeded, 401 when the token is missing or wrong, 400 when the request cannot be satisfied as written — for example a mode the listener does not serve, or a whitelist change that is refused (section 6.6) — and 500 when the server could not carry out a valid request, such as a whitelist change that could not be saved. 403 means the caller's address is not on the IP whitelist (section 7.4), which guards the API and the dashboard too. /ca answers 404 when the server has neither a MITM port nor the HTTPS API, so no CA was created (with api_port_https from the template the CA exists); /list answers 404 as described in section 6.5, whitelist changes can answer 409 (section 6.6), and /health answers 503 while the license is not confirmed. The explanation is in the body; the status is what a script should branch on.

6.1. New credentials — GET /allocate

Returns a connection string with a fresh login and password. The listener is selected by port — the same URL the dashboard produces (section 7.6):

curl "http://YOUR_IP:8080/allocate?token=YOUR_TOKEN&port=1080&mode=socks5"

Response:

socks5://user_AbCdEfGh:KlMnOpQrSt@YOUR_IP:1080

Query parameters:

Parameter Description Default
token Required. The secret master_token —
port Port of the listener the credentials are for, a MITM port included. Identifies the listener unambiguously —
mode Protocol: socks5 or http; socks is the same as socks5, https the same as http. Any other value gives socks5, with a warning in the log socks5
os OS profile: linux, android, windows, ios, macos. Selects the listener by profile; ignored when port is given linux

The port identifies the listener unambiguously. os selects it by profile; when several listeners share that profile, the first one in the configuration is returned. A call with neither port nor os takes the first linux listener, or the first listener in the configuration when there is no linux one. A port or profile that is not in the configuration is answered with 400 and a list of what is configured.

Examples:

# Credentials for the listener on port 1081
curl "http://YOUR_IP:8080/allocate?token=YOUR_TOKEN&port=1081&mode=socks5"
# response: socks5://user_xxx:pass_xxx@YOUR_IP:1081

# Same listener, HTTP proxy (listener in http or hybrid mode)
curl "http://YOUR_IP:8080/allocate?token=YOUR_TOKEN&port=1081&mode=http"
# response: http://user_xxx:pass_xxx@YOUR_IP:1081

# By OS profile
curl "http://YOUR_IP:8080/allocate?token=YOUR_TOKEN&os=android"
NoteA port that is not in the configuration, or a protocol the listener does not support, is rejected with 400:
Error: no listener on port 1090. Configured ports: 1080, 1081, 1082
Error: http mode is not enabled for 'linux' profile (port 1080). Listener mode: socks
TipIn hybrid mode both mode=socks5 and mode=http work on the same port.

6.2. Statistics — GET /stats

Returns full server statistics as JSON.

curl "http://YOUR_IP:8080/stats?token=YOUR_TOKEN"

Example response, abridged: the listeners and mitm_listeners fields are omitted, being an echo of the matching config sections, the DNS server list is cut to two and the hourly and daily arrays to one point.

{
  "version": "2.6.2",
  "uptime_seconds": 9980,
  "active_connections": { "tcp": 56, "udp": 4 },
  "mitm": { "active": 3, "total": 512 },
  "traffic": {
    "ingress_bytes": 264074969,
    "egress_bytes": 35524983
  },
  "dns": {
    "total_queries": 18905,
    "cache_hits": 17106,
    "cache_stale_served": 12,
    "cache_misses": 1799,
    "cache_entries": 1420,
    "inflight_joined": 63,
    "hedge_fired": 208,
    "hedge_won_by_backup": 41,
    "all_failed": 3,
    "bad_names": 0,
    "all_down_queries": 0,
    "servers": [
      {
        "address": "[2606:4700:4700::1111]:53",
        "weight": 58, "state": "healthy",
        "queries": 1502, "failures": 7,
        "timeouts": 6, "servfails": 1, "net_errors": 0,
        "consecutive_failures": 0,
        "last_failure_ago_s": 1840, "last_error": "Timeout"
      },
      {
        "address": "9.9.9.9:53",
        "weight": null, "state": "healthy",
        "queries": 36, "failures": 0,
        "timeouts": 0, "servfails": 0, "net_errors": 0,
        "consecutive_failures": 0,
        "last_failure_ago_s": null, "last_error": null
      }
    ]
  },
  "events": {
    "total_connections": 18963,
    "connection_errors": 12,
    "access_denied": 0,
    "blocked_domains": 0,
    "dns_no_ipv6": 4,
    "firewall_blocks": 0
  },
  "license": {
    "expires_at_unix": 1770484105,
    "days_left": 29,
    "seconds_left": 2530000,
    "is_active": true,
    "servers": { "node-1": "online", "node-2": "online", "node-3": "online" }
  },
  "telemetry": {
    "cpu_cores": 2,
    "total_mem_mb": 2048,
    "latest_cpu_usage": 4.8,
    "latest_mem_mb": 96,
    "open_fds": 412,
    "max_fds": 1000000,
    "hourly": [ { "ts": 1770480000, "rx": 1843200, "tx": 245760 } ],
    "daily": [ { "ts": 1770393660, "rx": 1650000, "tx": 231000 } ]
  },
  "kernel_p0f": true,
  "whitelist_fallback": null,
  "server_ip": "1.2.3.4",
  "shaper": {
    "enabled": false,
    "rate_bytes": 0,
    "burst_bytes": 0,
    "up_throttled": 0, "down_throttled": 0,
    "up_wait_ms": 0, "down_wait_ms": 0,
    "up_oversize": 0, "down_oversize": 0
  }
}

Key fields:

Field Description
mitm Active and cumulative connections through MITM ports since startup
dns Cache hit and miss counts, how often the parallel query to a backup was used, and a line per server: its state, weight, how many queries it served and how its failures break down into timeouts, SERVFAILs and network errors. A server that stopped answering shows up here first. all_down_queries counts queries sent while every server was marked down.
whitelist_fallback null while the IP whitelist from the file is in force; otherwise the reason the file could not be used
events.dns_no_ipv6 / firewall_blocks Domains without an AAAA record, and connections refused by the IP whitelist — the same categories as the dashboard chart in section 7.3
license.seconds_left Seconds until the license expires, by the server's clock: negative once it has expired, 0 while the expiry date is not yet known, right after startup. days_left gives whole days and is never negative: it is 0 after expiry, while the date is not yet known and during the last day
license.is_active Whether the license is currently confirmed. When false, the proxy ports stay closed until the next check
license.servers State of each built-in license node
kernel_p0f true when the server runs the OxidProxy kernel and OS fingerprint substitution works. false means traffic leaves with the host's own TCP signature — install the kernel and reboot
telemetry Server CPU and RAM usage — the data behind the hardware panel in section 7.1 — open file descriptors against the limit, and the traffic history behind the charts in section 7.2: hourly and daily, one point per minute (60 and 1440 points)
server_ip The public_ip value from the configuration
shaper Current shaping limits and how often and for how long the limit held traffic back, see section 5.7
listeners / mitm_listeners The port settings from the matching config.yaml sections, as the server applied them — handy for scripts that need the list of active ports and profiles
NoteStatistics are available without a token from localhost (127.0.0.1), so monitoring scripts running on the server itself need no credentials.

6.3. Health and version — GET /health, GET /version

Both answer without a token; the IP whitelist still applies. /health returns 200 while the license is confirmed and 503 when it is not, which suits an external monitor. /version returns the version as plain text.

curl "http://YOUR_IP:8080/health"
{"status":"ok","license_active":true,"uptime_seconds":9980,"version":"2.6.2"}

curl "http://YOUR_IP:8080/version"
2.6.2

6.4. Metrics — GET /metrics

Most counters from /stats in the Prometheus text format: one name value per line, lines starting with # are HELP and TYPE. Any Prometheus-compatible collector reads it. The token is required, except for requests from the server itself.

curl "http://YOUR_IP:8080/metrics?token=YOUR_TOKEN"

Names start with oxidproxy_: for example oxidproxy_active_tcp, oxidproxy_bytes_egress_total, oxidproxy_license_active, oxidproxy_kernel_p0f_active, oxidproxy_dns_queries_total. oxidproxy_api_unauthorized_total counts API requests refused for a wrong or missing token (401). Requests and connections refused by the IP whitelist are not exported; they are counted in events.firewall_blocks of /stats and in the Recent Blocks panel of the dashboard.

6.5. Account files — GET /list

Downloads the account file of a port for one protocol — the file the TXT buttons of the dashboard download (section 7.8). mode is socks5 or http. The answer is 404 for a port that is not in the configuration and until the file has been exported; without port, or with any other mode, it is 400.

curl -OJ "http://YOUR_IP:8080/list?token=YOUR_TOKEN&port=1081&mode=socks5"

6.6. IP whitelist — GET /whitelist

The list the dashboard panel manages (section 7.4), changed on the fly without a restart. IPv4 addresses and subnets only. GET returns the entries, totals and recent blocks as JSON; adding and removing answer OK: ….

curl "http://YOUR_IP:8080/whitelist?token=YOUR_TOKEN"
curl -X POST "http://YOUR_IP:8080/whitelist/add?token=YOUR_TOKEN&cidr=203.0.113.0/24"
curl -X DELETE "http://YOUR_IP:8080/whitelist/remove?token=YOUR_TOKEN&cidr=203.0.113.0/24"

A change the request itself rules out is refused with 400 and the reason in the body: no cidr given, the address is invalid or IPv6, the entry is already in the list, the entry to remove is not in it, it is a protected entry (lock icon in the dashboard), it is the last entry, removing it would block your own address as the server sees it, or the list already holds 1000 entries.

If the whitelist file was changed outside the service — by hand, by a script or by copying another file over it — after the service read it, adding and removing answer 409 and leave the file untouched. Run systemctl restart oxidproxy to load the file, then repeat the request. 500 means the change could not be saved: the file could not be written, or it could not be read when the service started (whitelist_fallback in section 6.2).


7. Web dashboard

The built-in dashboard is available at:

http://YOUR_IP:8080/dashboard?token=YOUR_TOKEN

7.1. Live monitoring

The dashboard lives at http://YOUR_IP:PORT/dashboard?token=YOUR_TOKEN, where PORT is the configured api_port. HTTPS is supported too, via api_port_https.

It refreshes every 10 seconds and shows:
- Hardware Health — CPU load, with the core count, RAM usage and Open Sockets: open file descriptors, mostly sockets, against the service's limit. That bar turns yellow above 70% and takes the theme's alert color above 90%; a busy proxy runs out of sockets long before it runs out of CPU
- Network Traffic — total traffic received and sent (Rx / Tx), total connections since startup, active TCP and UDP connections, MITM Sessions (active now and total since startup), Traffic Shaper — Disabled, or the rate in Mbit/s and the burst in MB (section 5.7) — and Shaper Throttle: how many times the limit held back traffic from the client (↑) and to the client (↓)
- Recent Blocks — section 7.5
- License & Status — the status (table below), Days Left, Expires (the expiry date) and the state of each license server: online, offline, or checking... right after startup. Days Left and Expires show — until the license server has answered; during the last day Days Left shows < 1 in yellow
- DNS Resolver — the cache hit rate (hover over it for the counts), a row per weighted server with its weight and state (healthy, probing — getting a trial query after failures — or down), one Backup Pool row with how many failover servers are healthy, Hedged Requests — how many times a failover server was queried in parallel and how many times it answered first — and Stale / Failed: answers served from expired cache entries, and queries no server answered (section 5.6)
- Version and uptime — in the header

Status Meaning
✅ Active The license is confirmed and the proxy ports are open. It stays Active during the last day too: the ports close only when the license server reports that the term is over
❌ Expired The license term is over; the proxy ports are closed
❌ BLOCKED / ERROR The proxy ports are closed for another reason: the license is not confirmed. The service log says why: journalctl -u oxidproxy
⛔ Your IP is not whitelisted The server now refuses this address with 403: it is not on the IP whitelist, for example after the list changed while the page was open. Hover over the status for the address as the server sees it — behind a router that rewrites addresses, the router's
🔑 Token rejected The token the page was opened with no longer matches master_token: it was changed and the service restarted. Open the dashboard with the new token
💀 OFFLINE / DEAD The dashboard got no data from the server: the service is stopped or the server cannot be reached

The title and icon of the browser tab follow the status.

A red ! sign warns about one of two degraded modes; hover over it, or move to it with Tab, for the cause and the fix:
- next to AUTO-ROTATION API LINKS, in place of "(p0f Profiled)" — the server is not running on the OxidProxy kernel, so connections leave with the server's own TCP fingerprint instead of the profile's (kernel_p0f in section 6.2). The output of uname -r must contain oxidproxy-p0f; if that kernel is installed but not booted, the startup log says how to fix the boot entry
- next to the IP Whitelist panel title — whitelist.txt could not be read, so every address is allowed, on the proxy ports and in the dashboard alike, and whitelist editing is off. The sign gives the reason; the file must be readable by the oxidproxy user. Fix it, then run systemctl restart oxidproxy

7.2. Traffic charts

Two throughput charts (Rx/Tx in Mbit/s) with average labels:
- Hourly — the last 60 minutes, one point per minute
- Daily — the last 24 hours, one point per minute

7.3. Connection Analytics

The doughnut chart breaks all connections down by category:

Category Color Description
Success green Successfully proxied connections
Auth Denied yellow Refused because authentication failed
Conn Errors purple Network errors while establishing the connection
Blocked (DPI) gray Blocked by the domain blacklist
No IPv6 blue Domains with no AAAA record, that is, no IPv6
Firewall Blocks red Refused by the IP whitelist

7.4. IP whitelist management

The IP Whitelist panel manages allowed addresses and subnets straight from the browser, without restarting the service:
- View — lists every entry, with a lock icon marking system entries that cannot be removed
- Add — the input accepts a single IPv4 address or an IPv4 subnet such as 192.168.1.0/24; IPv6 is not supported in the whitelist. Press + Add Entry or Enter
- Remove — the button next to each entry. Removing 0.0.0.0/0 asks for confirmation, to avoid locking yourself out. The last entry cannot be removed — its button is disabled, since an empty list would block everyone, the dashboard included — and neither can an entry without which your own address, as the server sees it, would be blocked: the dashboard shows the reason instead
- Import — the Import button loads a .txt file of addresses or CIDRs, one per line, with # comments supported

The list refreshes every 10 seconds, so changes made from another device or by another administrator appear without reloading the page. It is kept in the file set by whitelist_file, /etc/oxidproxy/whitelist.txt by default.

Changes made to the file itself — by hand, by a script or by copying another file over it — take effect after systemctl restart oxidproxy. Until then the dashboard does not save whitelist edits over them: it reports that the file was changed outside the dashboard and suggests restarting the service.

The list holds up to 1000 entries. While whitelist.txt could not be read, every address is allowed, a red ! appears next to the panel title (section 7.1) and editing is off: fix the file, then run systemctl restart oxidproxy.

CautionThe 0.0.0.0/0 entry allows connections from every address. Remove it and only the addresses explicitly listed will reach the proxy — make sure your own is among them first.

7.5. Recent Blocks

The Recent Blocks panel shows the last blocked connections, up to ten, with the address and the time. The Total counter gives the number of blocks since the service last started.

At the top of the dashboard a card is generated for every listener port, labeled with its OS profile. The API SOCKS5 and API HTTP buttons are disabled when the port does not serve that protocol.

Clicking a button copies the credential-request URL (GET /allocate?port=...&mode=...) to the clipboard and shows a toast in the corner.

Below them, the TXT SOCKS5 and TXT HTTP buttons download the account file for that profile and mode directly. A button is disabled when the port does not serve that mode; if the file has not been exported yet, the dashboard says so.

Each MITM listener gets its own card, first in the row, with the same buttons plus Download CA certificate. The client installs that CA once, after which all HTTPS traffic goes out under the selected browser profile. Without mitm_listeners a dimmed MITM card reads not configured.

7.7. Themes

The dashboard offers eight themes, switchable without reloading. The choice is remembered in the browser:

Theme Description
OxidProxy Dark The brand dark theme, used by default
Light Classic Classic light
Dracula Dark, purple and pink
Deep Ocean Deep navy
Teal Lagoon Dark teal
Gruvbox Retro Warm retro palette
Baroque Bling 💰 Baroque, with gold accents
Hacker Monochrome green on black

7.8. Downloading account files

The TXT buttons on each port's card download its account files. With the accounts_file path from the template they are named like this:

File Contents
accounts_linux_socks5_1080.txt SOCKS5 accounts for the Linux profile, port 1080
accounts_android_socks5_1081.txt SOCKS5 accounts for the Android profile
accounts_android_http_1081.txt HTTP accounts for the Android profile in hybrid mode
accounts_windows_http_1082.txt HTTP accounts for the Windows profile

Each file holds lines of the form protocol://login:password@IP:PORT, ready to import anywhere.

TipWhich files exist depends on the listeners and accounts_file settings. Hybrid listeners produce two files, SOCKS5 and HTTP.

8. Service management

OxidProxy installs as a systemd service and runs as the oxidproxy user.

# start
systemctl start oxidproxy

# stop, gracefully - waits for active sessions to finish
systemctl stop oxidproxy

# restart
systemctl restart oxidproxy

# status
systemctl status oxidproxy

# enable at boot
systemctl enable oxidproxy

# follow the logs
journalctl -u oxidproxy -f
CautionDo not run OxidProxy directly as root. Doing so leaves the log and data files owned by root, which then causes permission errors when systemd starts the service as its own user. Always start it with systemctl start oxidproxy: systemd switches to the oxidproxy user declared in the unit file.

9. Logging and monitoring

Log files

By default the server logs to the systemd journal only: journalctl -u oxidproxy. Files are written when log.dir is set (section 5.11):

File Description
/var/log/oxidproxy/oxidproxy.log Main log, at the verbosity set in the config
/var/log/oxidproxy/error.log Errors only

Log rotation

File logs in /var/log/oxidproxy are rotated every hour by logrotate, installed together with the package; the package's own oxidproxy-logrotate.timer starts it. The last three generations are kept, older ones are compressed with gzip. Files in any other directory set by log.dir are not rotated, and oxidproxy -t warns about it.

System telemetry

Every 10 seconds a [SYS_STATS] line is written to the log:

[SYS_STATS] System Mem: 524/2048 MB | App Mem: 85 MB | CPU: 12.3% | Conns: 42 / 4096

10. Command line (CLI)

oxidproxy [OPTIONS]

Options:
  -c, --config <PATH>      Path to the configuration file (default: /etc/oxidproxy/config.yaml)
  -t, --test [<PATH>]      Validate the configuration syntax
  -g, --generate           Write a sample config.yaml.example
  -b, --blacklist          Write a sample blacklist.txt.example
  -H, --hwid               Show the server HWID
  -p, --ping <HOST:PORT>   Check that a license server is reachable
  -V, --version            Show the version
  -h, --help               Show this help

oxidproxy -t without a path checks the installed configuration, prints the same warnings the server writes to the log at startup, and exits with code 1 on any configuration error.

Without -t, -g, -b, -H, -p, -V or -h, oxidproxy starts the proxy itself, with the config given by -c or the default one. On an installed server that is the service's job (section 8): systemd runs oxidproxy -c /etc/oxidproxy/config.yaml as the oxidproxy user, with the network permissions the proxy needs. Do not start the proxy from the shell. The oxidproxy commands below (-t, -g, -p, -H) do not start it and are safe to run as root.

Examples:

# start the proxy - through systemd, not by hand
systemctl start oxidproxy

# validate the config before deploying
oxidproxy -t /etc/oxidproxy/config.yaml

# write a fresh sample config, config.yaml.example, to the current directory
oxidproxy -g

# check that a license server is reachable
oxidproxy --ping address.given.by.support:8000

# show the HWID
oxidproxy -H

11. Troubleshooting

The service will not start

  1. Validate the configuration:
    oxidproxy -t /etc/oxidproxy/config.yaml

  2. Check the logs:
    journalctl -u oxidproxy --no-pager -n 50

  3. Check the license server:
    oxidproxy --ping <HOST:PORT>
    Use an address given to you by support. The regular endpoints are built into the server and need no manual checking.

No connection through the proxy

  1. Make sure the client speaks a supported protocol: SOCKS5, SOCKS5h or HTTP CONNECT. SOCKS5h is recommended for maximum anonymity, as DNS is then resolved on the server.
  2. Check IPv6 routing — inspect /etc/rc.local and the output of:
    ip -6 route show
  3. Confirm the port is reachable from outside:
    ss -tlnp | grep oxidproxy
  4. Check the firewall (iptables or nftables)

Some sites do not open

OxidProxy works over IPv6 only. A target site without an AAAA record simply cannot be reached.

To check for an AAAA record:

dig AAAA example.com

The IP does not change

OxidProxy uses sticky sessions: the IP is bound to a login and password pair. To change it:
- Request new credentials via GET /allocate
- Or wait for rotation_interval to elapse and reconnect

The wrong kernel booted

At startup the journal says Native kernel mimicking DISABLED, and uname -r does not end in -oxidproxy-p0f: the server booted another kernel, and OS fingerprinting does not work.

  1. See which kernel is running and which are installed:
    uname -r
    ls /boot/vmlinuz-*
  2. Check that /etc/default/grub has GRUB_DEFAULT=0. On Debian 12 that is enough: our kernel is numbered above Debian's own and comes first, unless a newer kernel, for example from backports, is installed next to it.
  3. On Debian 13, Debian's own kernels follow the upstream numbering and can sort above ours after an update, so the installer keeps ours first with the file /etc/default/grub.d/99-oxidproxy-p0f.cfg. If the file is missing, run the installer again (section 3.1): it writes the file back.
  4. Apply the change and reboot:
    update-grub
    reboot

12. FAQ

Q: What is SOCKS5h, and how does it differ from SOCKS5?

A: With SOCKS5h, DNS queries are resolved on the proxy server. Plain SOCKS5 resolves them on the client, which can leak DNS traffic and expose the real address. Always prefer SOCKS5h.


Q: Do logins and passwords change after a restart or an update?

A: No. They derive from account_seed, which the installer sets once, unique for every server. Changing account_seed regenerates all accounts; when you copy the configuration to another server, give that server its own value.


Q: Can I use several IPv6 subnets?

A: Yes. Add them to ipv6_subnets with different weights, and traffic is distributed in proportion.


Q: How do I find my server HWID?

A: Run oxidproxy -H. The HWID binds the license to the machine.


Q: How much does OxidProxy consume?

A: With 100,000 accounts and around 50 active connections, roughly 80–120 MB of RAM and about 5% of one CPU core. Being written in Rust gives it high throughput and predictable memory use.


Q: Is the API secure?

A: With api_port_https enabled the API is served over HTTPS. The certificate is signed by the server's own CA, the one served at /ca; until that CA is trusted, curl needs -k (or --cacert with the file from /ca). Access is protected by master_token; of the API endpoints, without it only /health, /version and /ca (the CA certificate, which holds no secret) answer, and /stats and /metrics only to the server itself. With the IP whitelist on, the API and the dashboard answer only to listed addresses.


Q: What is HTTP CONNECT mode?

A: HTTP CONNECT is the standard way to proxy HTTPS. The client sends CONNECT host:443 HTTP/1.1 with a Proxy-Authorization: Basic header, and an encrypted tunnel is established. Every browser supports it, as do curl, system proxy settings and most software.

Example with curl:

curl -x http://user_xxx:pass_xxx@YOUR_IP:1081 https://ifconfig.me

Q: How does hybrid differ from separate socks and http ports?

A: In hybrid mode one port accepts both. The protocol is detected from the first byte of the connection: 0x05 means SOCKS5, the first letter of an HTTP method (CONNECT, GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS) means HTTP, and anything else is dropped. It saves opening extra ports, and every feature — p0f, shaping, DNS, limits — behaves identically for both.


Q: What does the [mode] macro do in accounts_file?

A: On export, [mode] is replaced by the protocol, either socks5 or http. Hybrid listeners produce two files: one with socks5:// links and one with http://. For example accounts_android_socks5_1081.txt and accounts_android_http_1081.txt.


Q: The browser reports a certificate error on a MITM port.

A: A MITM port terminates TLS with its own certificate authority, so the client device must add the server CA to its trust store once; otherwise the browser treats the connection as insecure. The certificate is available from the /ca endpoint, or via the download button on the MITM card in the dashboard, described in section 7.6.


Q: What happens if the client User-Agent matches none of the mitm_listeners profiles?

A: It depends on the client. A recognized browser that is not in profiles gets another profile of the same OS from the list, or the default profile when the list has none for that OS; with strict: true it is refused instead. A client with no browser User-Agent at all — curl, a script — is refused with a short page explaining why; allow_unknown_ua: true serves it with the default profile.


13. Changelog

v2.6.2 — October 2026

v2.6.1 — October 2026

v2.6.0 — October 2026

v2.5.1 — September 2026

When updating:

v2.4.1 — September 2026

v2.4.0 — September 2026

v2.3.7 — September 2026

v2.3.6 — September 2026

v2.2.0 — September 2026

v2.1.2 — August 2026

v2.1.0 — August 2026

v2.0.0 — June 2026

v1.8.1 — May 2026

v1.7.1 — April 2026

v1.7.0 — March 2026

v1.6.0 — January 2026