---
title: "Allow TCP connections in Ruby"
language: "en"
documentation_version: "3.x"
channel: "development"
release_context: null
source_revision: "252018d861d008b380109414280f99e51433e238"
working_tree_clean: true
source_path: "docs/guides/tcp.md"
source_sha256: "ce41f0268f758cbad5821a654dd5e9a3cb9e4e0f1d1d9d3fb208c103803b5b4d"
source_at_revision: true
source_url: "https://github.com/matsumotory/ngx_mruby/blob/252018d861d008b380109414280f99e51433e238/docs/guides/tcp.md"
html_url: "https://ngx.mruby.org/guides/tcp/"
markdown_url: "https://ngx.mruby.org/markdown/guides/tcp.md"
last_updated: "2026-10-29"
---

# Allow TCP connections in Ruby

nginx's stream module proxies TCP connections, and UDP sessions with
`listen ... udp`. In a `server` of the `stream` block,
[`mruby_stream`](/reference/directives/#stream-module-directives) runs a
Ruby script in the access phase of the stream module, once per session,
before nginx connects to the upstream. The script reads the addresses of
the session through `Nginx::Stream::Connection`, and closes the session by
setting `stream_status` to `Nginx::Stream::ABORT`.

## The sample

The script lets the session through only from the addresses of its list.
nginx then proxies the connection to the upstream, here an HTTP server of
the test configuration on port 18168. The request and the response are the
bytes that the client sends and receives over the TCP connection.

```nginx title="test/conf/conf.d/stream/42-guide-tcp.conf"
upstream guide_tcp_backend {
    server 127.0.0.1:18168;
}

server {
    listen 127.0.0.1:12364;
    mruby_stream build/nginx/html/guide_tcp.rb;
    proxy_pass guide_tcp_backend;
}
```

```ruby title="test/html/guide_tcp.rb"
allowed = ["127.0.0.1", "::1"]
c = Nginx::Stream::Connection
c.stream_status = Nginx::Stream::ABORT unless allowed.include?(c.remote_ip)
```

```http title="test/html/guide_tcp.request: the request that the case sends"
GET /status HTTP/1.1
Host: 127.0.0.1:12364
Connection: close
```

```http title="test/html/guide_tcp.response: the response that the case compares"
HTTP/1.1 200 OK
Content-Type: text/plain

the backend on port 18168 answered
```

<p class="caption sample-caption">On every CI run, the case <code>test/t/cases/guides.rb</code> sends this request and compares the status line, the headers shown on this page and the whole body. The five files last changed on <span class="nowrap">2026-10-29</span> in commit <code class="nowrap">32219e53</code>.</p>

## What to keep in mind

- The stream module runs the script for UDP sessions too; the test suite
  sends TCP only.
- The script runs before nginx reads the client's data, so it decides on
  the addresses and ports of the session, not on what the client sends.
- The `stream` block has its own mruby state, separate from that of the
  `http` block, and its own directives for the code at startup
  (`mruby_stream_init`, `mruby_stream_init_worker`).
