{"id":4359,"date":"2026-05-04T21:26:17","date_gmt":"2026-05-04T18:26:17","guid":{"rendered":"https:\/\/demensdeum.com\/blog\/2026\/05\/04\/port-forwarding-chisel\/"},"modified":"2026-05-04T21:48:08","modified_gmt":"2026-05-04T18:48:08","slug":"port-forwarding-chisel","status":"publish","type":"post","link":"https:\/\/demensdeum.com\/blog\/2026\/05\/04\/port-forwarding-chisel\/","title":{"rendered":"Port forwarding between clients via Chisel: a stripped-down tunnel without L3"},"content":{"rendered":"<p>When two devices are behind NAT or strict firewalls and cannot \u201csee\u201d each other directly, a VPN seems to be the standard solution. But a full-fledged L3 tunnel (like WireGuard or OpenVPN) is often redundant: it requires root rights, setting up virtual interfaces, and can conflict with existing routes.<\/p>\n<p>In such cases, it is convenient to use <b>Chisel<\/b> &#8211; a TCP\/UDP tunnel that runs on top of HTTP and uses WebSockets for data transfer. In this note I will show how to \u201cforward\u201d a port from one client to another through an intermediate server.<\/p>\n<h2>How does it work?<\/h2>\n<p>Imagine the situation: you have <b>Client A<\/b> (for example, your home server), <b>Client B<\/b> (your work laptop) and a <b>VPS<\/b> with a public IP address. Clients A and B can access the VPS, but not each other.<\/p>\n<p>The forwarding scheme will look like this:<br \/>\n1. <b>Client A<\/b> connects to the VPS and opens a \u201creverse\u201d port on the server. Now everything that comes to port X of the server goes to port Y of Client A.<br \/>\n2. <b>Client B<\/b> connects to the VPS and forwards port Z from its local machine to port X of the server.<br \/>\n3. As a result, Client B accesses <b>localhost:Z<\/b> and ends up on <b>Client A:Y<\/b>.<\/p>\n<p>This approach is one of the solutions in cases where communication with a VPS does not implement the L3 layer or there is no ability to configure routing between clients. We work exclusively at the application and port level.<\/p>\n<h2>Step 1: Start the server<\/h2>\n<p>On your VPS, just run Chisel in server mode. The <code>--reverse<\/code> flag is required to allow clients to open ports on the server side.<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>chisel server --port 8080 --reverse\n<\/code><\/pre>\n<\/div>\n<h2>Step 2: Connecting Client A (Source)<\/h2>\n<p>Let&#8217;s say Client A wants to open access to his local web server on port 3000. He connects to the VPS and says: \u201creserve port 2000 on the server and forward it to me to 3000.\u201d<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>chisel client vps-ip:8080 R:2000:127.0.0.1:3000\n<\/code><\/pre>\n<\/div>\n<p>Now port 2000 on the VPS (on the loopback interface) leads to Client A.<\/p>\n<h2>Step 3: Connecting Client B (Consumer)<\/h2>\n<p>Now Client B wants to access this resource. It connects to the same VPS and forwards its local port 8080 to port 2000 of the server.<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>chisel client vps-ip:8080 8080:127.0.0.1:2000\n<\/code><\/pre>\n<\/div>\n<p>Ready! Now, when you open <code>http:\/\/localhost:8080<\/code> on Client B, you will see the service running on Client A.<\/p>\n<h2>Safety and nuances<\/h2>\n<p>Chisel supports authentication via the <code>--auth<\/code> flag, which is highly recommended when working through public servers. You can also use TLS certificates to encrypt traffic.<\/p>\n<p>The main advantage of this approach is that there is no need for TUN\/TAP devices and complex routing tables. This is a stripped-down tunnel that does exactly one thing: binds ports via a WebSocket connection. This even works through corporate proxies if you configure Chisel to work through port 443.<\/p>\n<h2>Output<\/h2>\n<p>Chisel is a utility for specific network tasks. When you need to forward ports between isolated nodes without setting up a full-fledged VPN, a combination of forward and reverse tunnels through a relay server turns out to be a completely viable solution.<\/p>\n<h2>Links<\/h2>\n<p><a href=\"https:\/\/github.com\/jpillora\/chisel\" rel=\"noopener\" target=\"_blank\">https:\/\/github.com\/jpillora\/chisel<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>When two devices are behind NAT or strict firewalls and cannot \u201csee\u201d each other directly, a VPN seems to be the standard solution. But a full-fledged L3 tunnel (like WireGuard or OpenVPN) is often redundant: it requires root rights, setting up virtual interfaces, and can conflict with existing routes. In such cases, it is convenient<a class=\"more-link\" href=\"https:\/\/demensdeum.com\/blog\/2026\/05\/04\/port-forwarding-chisel\/\">Continue reading <span class=\"screen-reader-text\">&#8220;Port forwarding between clients via Chisel: a stripped-down tunnel without L3&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[61],"tags":[],"class_list":["post-4359","post","type-post","status-publish","format-standard","hentry","category-techie","entry"],"translation":{"provider":"WPGlobus","version":"3.0.2","language":"en","enabled_languages":["en","ru","zh","de","fr","ja","pt","hi"],"languages":{"en":{"title":true,"content":true,"excerpt":false},"ru":{"title":true,"content":true,"excerpt":false},"zh":{"title":true,"content":true,"excerpt":false},"de":{"title":true,"content":true,"excerpt":false},"fr":{"title":true,"content":true,"excerpt":false},"ja":{"title":true,"content":true,"excerpt":false},"pt":{"title":true,"content":true,"excerpt":false},"hi":{"title":true,"content":true,"excerpt":false}}},"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/posts\/4359","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/comments?post=4359"}],"version-history":[{"count":1,"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/posts\/4359\/revisions"}],"predecessor-version":[{"id":4360,"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/posts\/4359\/revisions\/4360"}],"wp:attachment":[{"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/media?parent=4359"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/categories?post=4359"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/wp-json\/wp\/v2\/tags?post=4359"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}