Skip to content
← All articles

How to Find a Server Port: Your Own, Remote and Proxy

In short. The port your own service listens on is shown by ss -tulpn on Linux, netstat -ano on Windows and lsof -nP -iTCP -sTCP:LISTEN on macOS. Which port a host answers on can only be learned by connecting to it — with a port scanner. Standard numbers live in /etc/services and the IANA registry. A proxy port comes from your access string, never from a list of "typical" values.

"How do I find a server port" hides two different questions that need different tools and produce different answers. The first is which port my process grabbed: you answer that from inside the machine, where you have root and the kernel socket table. The second is which port a host at some address responds on: you answer that from outside, by attempting a connection. Mixing them up is the single most common reason people stare at correct output for half an hour without finding what they need. Both are covered below, plus proxy ports and the classic trap: a port that listens but is invisible from the network.

A service port and a host port are two different questions

A port is not a property of a server. It is a number a specific process asked the kernel for so it could accept connections. That means "the server port" only exists as a pair: the address the process listens on, plus the number. A single server holds dozens of such pairs at once, and roughly half of them are unreachable from outside by design.

QuestionVantage pointOS access neededToolWhat you get
Which port does my service listen onInside the serverYes, root preferredss, lsof, netstatFull list: address, port, protocol, process, PID
Which process took port 8080Inside the serverYes, root requiredss -tlnp, lsof -i, fuserProcess name and PID
Which port does a host answer onOutside, over the networkNoPort scanner, nc, curlExactly three outcomes: answers, refuses, silence
Which port should a protocol useReferenceNo/etc/services, IANA registryA registered number is a convention, not a fact

The difference matters. From inside you see ground truth: the kernel hands over the socket table and there is nothing to guess. From outside you only see a reaction to a connection attempt, and that reaction can lie — a firewall can refuse on behalf of someone else's closed port, and it can stay silent in front of an open one.

Diagram: two ways to find a port — from inside the server via the socket table, and from outside via a connection attempt
From inside you read the socket list; from outside you only read the reaction to a connection. Different sources of truth.

How to find which port your service listens on in Linux

ss is the primary tool

ss ships in the iproute2 package and is present on every modern distribution by default. It reads straight from the kernel and is noticeably faster than the old netstat on machines with thousands of connections.

# every listening TCP and UDP socket, with processes, no name resolution
ss -tulpn

# same thing, TCP only
ss -tlnp

Typical output looks like this:

Netid State  Recv-Q Send-Q  Local Address:Port  Peer Address:Port Process
udp   UNCONN 0      0           127.0.0.1:323        0.0.0.0:*    users:(("chronyd",pid=1644,fd=5))
tcp   LISTEN 0      128           0.0.0.0:22         0.0.0.0:*    users:(("sshd",pid=2498,fd=7))
tcp   LISTEN 0      128              [::]:22            [::]:*    users:(("sshd",pid=2498,fd=8))
tcp   LISTEN 0      65535       127.0.0.1:3000       0.0.0.0:*    users:(("dockerd",pid=2489,fd=115))
tcp   LISTEN 0      65535         0.0.0.0:443        0.0.0.0:*    users:(("dockerd",pid=2489,fd=111))

Read the Local Address:Port column, and read all of it. 0.0.0.0:443 means listening on every IPv4 address — reachable from the network. [::]:22 is the same for IPv6. But 127.0.0.1:3000 listens on the loopback interface only: the port works from the machine itself and does not exist from the network. That is not a detail — it accounts for roughly half of all "the port is open but nothing answers" tickets.

FlagWhat it does
-tTCP only
-uUDP only
-lListening sockets only, no established connections
-pShow the process and PID holding the socket
-nDo not resolve ports to names or addresses to hostnames — faster and unambiguous
-aAll sockets, including established connections

ss has its own filter language, which spares you from grep and its substring mistakes (grep :22 also matches ports 2222 and 8022):

# who listens on one specific port
ss -tlnp 'sport = :22'

# every established connection with its process
ss -tnp state established

# connections with one peer only
ss -tnp 'dst 203.0.113.10'
Without root the Process column comes back empty. Run as an ordinary user, ss -tulpn honestly lists the ports but not who holds them: the kernel does not expose that mapping without privileges. If processes are missing, you are not looking at a bug — add sudo.

Why netstat says "command not found"

netstat lives in the net-tools package, which many distributions stopped installing by default long ago. On current RHEL-family systems (AlmaLinux, Rocky, CentOS Stream) and in minimal Debian and Ubuntu images it is simply absent — not broken, but deliberately dropped: net-tools is considered legacy and gets no new features.

# AlmaLinux / Rocky / RHEL / Fedora
sudo dnf install net-tools

# Debian / Ubuntu
sudo apt install net-tools

Installing it for one command is rarely worth it — everything has an ss equivalent:

Tasknetstat (net-tools)ss (iproute2)
Listening TCP and UDP with processesnetstat -tulpnss -tulpn
All TCP connectionsnetstat -tanss -tan
Established onlynetstat -tan | grep ESTABLISHEDss -tn state established
Per-protocol statisticsnetstat -sss -s
Routing tablenetstat -rnip route show

The -tulpn flags are identical in both, so there is almost nothing to relearn: swap one word.

lsof, when you need the process-side view

lsof treats a socket as an open file. That helps when the question is not "who is on this port" but "what has this process opened".

# every listening TCP socket
lsof -nP -iTCP -sTCP:LISTEN

# everything happening on one port, established connections included
lsof -nP -i :443

# network activity of one process
lsof -nP -p 2498 -a -i

-n and -P disable resolution of addresses and port numbers into names. Without them lsof prints ssh instead of 22 and issues a DNS lookup per address, which turns an instant command into a minute of waiting on a busy server.

How to find the process by port

The classic case: a port is taken and your application will not start. Three ways, pick any:

# 1. via ss — exact filter, no false matches
ss -tlnp 'sport = :8080'

# 2. via lsof — shows the listener and active connections
lsof -nP -i :8080

# 3. via fuser — shortest, prints PIDs only
fuser -n tcp 8080

With a PID in hand, find out what it is and who manages it:

ps -p 2498 -o pid,user,comm,args --no-headers
# 2498 root sshd sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups

# which systemd unit owns the process
systemctl status 2498

That last command saves the most time: systemctl status <PID> accepts a process number and prints the unit name. From there you know what to restart and where the config lives.

How to find the port by process

The reverse case: the service runs, but on which port is unclear.

# find the PID by name
pgrep -a nginx

# list its network sockets
lsof -nP -p 1234 -a -i

# or filter ss output by PID
ss -tulpn | grep 'pid=1234'
If the process runs inside a container, the host shows you dockerd or containerd instead — whichever runtime published the port. That is expected: the runtime publishes the port, not the application. To look inside, run docker exec <name> ss -tulpn, assuming the image ships iproute2.

How to find a server port on Windows and macOS

Windows

On Windows netstat is alive and well. The -o switch adds the PID column — without it the output is nearly useless.

:: all connections and listening ports with process IDs
netstat -ano

:: listening only
netstat -ano | findstr LISTENING

:: who took a specific port
netstat -ano | findstr :8080

:: what that PID actually is
tasklist /FI "PID eq 1234"

PowerShell solves the same problem without text parsing — the cmdlets return objects you can filter:

# every listening port
Get-NetTCPConnection -State Listen | Sort-Object LocalPort

# who took port 8080, with the process name resolved
Get-NetTCPConnection -LocalPort 8080 |
  Select-Object LocalAddress, LocalPort, State, OwningProcess,
    @{Name='Process'; Expression={(Get-Process -Id $_.OwningProcess).ProcessName}}

Addresses read the same way as on Linux: 0.0.0.0 is every interface, 127.0.0.1 is local only, [::] is IPv6.

macOS

macOS ships both netstat and lsof, but netstat here inherits the BSD flag set. The main trap: on macOS -p means "protocol", not "process".

# the primary approach — lsof
lsof -nP -iTCP -sTCP:LISTEN

# who took a specific port
lsof -nP -i :5000

# netstat: -p selects the PROTOCOL here, not the process
netstat -an -p tcp | grep LISTEN

# -v adds a process:pid column
netstat -anv -p tcp

So the muscle-memory netstat -tulpn from Linux will not do what you expect on macOS. lsof is the working tool here.

Diagram mapping commands across systems: ss and lsof on Linux, netstat -ano and Get-NetTCPConnection on Windows, lsof on macOS
One question, three command sets. On macOS the netstat -p flag selects a protocol, not a process.

Which port nginx, Apache and other services use — reading the config

Sometimes you need intent rather than fact: which port the service is supposed to come up on. Then you read the configuration.

# nginx: full config dump with every include resolved
nginx -T | grep -n 'listen'

# where the config lives, if the path is non-standard
nginx -V 2>&1 | tr ' ' '\n' | grep conf-path

# crude, but always works
grep -RnE '^[[:space:]]*listen' /etc/nginx/
# Apache: port and virtual host summary
apachectl -S
grep -RnE '^[[:space:]]*Listen' /etc/httpd/ /etc/apache2/ 2>/dev/null

# OpenSSH: effective configuration after all includes
sshd -T | grep -iE '^port|^listenaddress'
# port 22
# listenaddress [::]:22
# listenaddress 0.0.0.0:22

# Docker: what is published to the host
docker ps --format '{{.Names}}\t{{.Ports}}'
docker port <container>

The Ports column in docker ps has an important subtlety. An entry like 0.0.0.0:8080->80/tcp means a published port: host port 8080 is forwarded into the container. A bare 80/tcp with no arrow is only an EXPOSE in the image — a declaration of intent. Such a port is reachable by other containers on the same network, but it does not exist on the host, and ss -tulpn will not show it. Running docker port <container> against that container returns nothing, which is exactly the check.

The config states intent; ss states fact. They diverge constantly: the service never reloaded, started against a different file, or failed to bind and stayed on the old port. The rule is simple — read the config to understand the plan, but trust the socket table for what is actually happening.

How to find which port a host answers on

With no access to the operating system, one method remains: try to connect. There are exactly three answers and they must not be conflated.

OutcomeHow it looksWhat it means
OpenConnection established immediatelySomething is listening and accepting connections
ClosedImmediate refusal, Connection refusedThe host is reachable, but nothing listens on that port
FilteredSilence until the timeout expiresA firewall dropped the packet; no conclusion about the port itself

Checking a single port from the command line:

# nc: exit code 0 means connected, 1 means it did not
nc -z -w2 example.com 443 && echo OPEN || echo CLOSED

# curl: shows both the status code and the failure reason
curl -sS --connect-timeout 5 -o /dev/null -w '%{http_code}\n' http://example.com:8080/

curl exit code 7 means "failed to connect". It appears both on refusal and on timeout, so tell them apart by timing: a refusal is instant, filtering hangs until --connect-timeout runs out.

Do not scan hosts you do not own without the owner's permission. That is not diagnostics, it is unauthorised probing: in most jurisdictions it falls under computer-misuse law, and with virtually every provider it violates the acceptable use policy. Test your own servers and those you have written authorisation for. Everything in this article is written for your own infrastructure.

How to interpret an open-port result and which ports are worth closing is covered separately in how to check open ports. The point here is different: a scan answers "does the host respond on this port", but never "which process is behind it". Service banners can be forged, any port can host anything, and the number by itself guarantees nothing.

Standard ports and where to look them up

The number-to-protocol mapping is maintained by IANA in the Service Name and Transport Protocol Port Number Registry, and the registration procedure is defined in RFC 6335. A copy of the registry ships with every system.

# Linux and macOS
grep -wE '443/tcp' /etc/services
getent services 443/tcp
getent services https

# Windows
type %SystemRoot%\System32\drivers\etc\services | findstr 443

The entries you will meet most often:

PortName in /etc/servicesUsually behind it
22/tcpsshSSH
25/tcpsmtpServer-to-server mail transfer
53/tcp, 53/udpdomainDNS
80/tcphttpPlain HTTP
110/tcp, 143/tcppop3, imapMail retrieval without TLS
443/tcphttpsHTTP over TLS
587/tcpsubmissionMail submission by a client
993/tcp, 995/tcpimaps, pop3sMail over TLS
1080/tcpsocksSOCKS proxy
3128/tcpsquidCaching HTTP proxy
3306/tcpmysqlMySQL and MariaDB
8080/tcpwebcache, http-altAlternative HTTP, proxies, backends

RFC 6335 splits the space into three ranges: 0–1023 are system ports, and on Unix-like systems only a privileged process may bind them; 1024–49151 are registered to specific services; 49152–65535 are dynamic, and the kernel draws a source port from there for every new outbound connection. That is why the local ports of your outgoing connections in ss -tn look like random high numbers — they are supposed to. The actual bounds on Linux are set by the kernel rather than the RFC: on many systems /proc/sys/net/ipv4/ip_local_port_range holds a considerably wider interval.

A name in /etc/services is a label, not a fact. The file translates a number into a caption and verifies nothing. A real example: nc -z 127.0.0.1 3000 prints tcp/hbci because that name is registered for port 3000 — while what actually runs there is a web application returning an HTTP redirect. Never infer the service from the file's caption.

Mail ports are a special case: there the numbers really do imply different behaviour rather than just a label, and confusing 25, 465 and 587 costs you undelivered mail. That is unpacked in ports 25, 465 and 587 explained.

How to find a proxy port and check that it is alive

Proxies follow the same rule as everything else: the authoritative source is the configuration, not a "typical" number. A proxy provider hands you an access string shaped like host:port:user:password, and the port in it is the only trustworthy value. Guessing from lists of "standard proxy ports" is pointless: an operator may bind the service to any number, and usually does.

Where the proxy port is written down

# environment variables — read by curl, wget, apt, pip, docker
env | grep -i proxy
# http_proxy=http://10.0.0.5:3128
# https_proxy=http://10.0.0.5:3128
# no_proxy=localhost,127.0.0.1,.internal

# per-client settings
git config --get http.proxy
npm config get proxy

# macOS: system proxy settings, PAC file included
scutil --proxy

# Windows: proxy for system HTTP clients
netsh winhttp show proxy

In graphical settings the port sits next to the address: on Windows under Settings, Network and Internet, Proxy; on macOS under System Settings, Network, the selected interface, Proxies; in browsers either the system settings or a dedicated section. If a PAC file is configured instead of a plain address, the port is computed by a script — you find it inside the .pac file, in the FindProxyForURL function.

If the proxy is your own, the question collapses into the first half of this article: run ss -tulpn on the proxy host and read its config. The port number is set there and is not predetermined by anything.

How to check the proxy port is alive

# 1. does TCP respond at all
nc -z -w3 10.0.0.5 3128 && echo TCP_OK || echo TCP_FAIL

# 2. does it actually proxy
curl -x http://10.0.0.5:3128 --connect-timeout 5 \
     -sS -o /dev/null -w '%{http_code}\n' https://example.com/

# 3. with authentication
curl -x http://10.0.0.5:3128 -U user:password --connect-timeout 5 \
     -sS -o /dev/null -w '%{http_code}\n' https://example.com/

# 4. SOCKS5 with hostname resolution on the proxy side
curl --socks5-hostname 10.0.0.5:1080 --connect-timeout 5 \
     -sS -o /dev/null -w '%{http_code}\n' https://example.com/

Reading the result:

What came backDiagnosis
200The proxy accepts connections and proxies traffic
407Alive but demands authentication — add -U
403Alive, but your address or the target is denied by policy
curl: (7) instantlyPort closed or wrong, or the proxy is not running
curl: (7) after a timeoutPackets are filtered: firewall, IP allowlist, network block
000 in %{http_code}No response at all — read the curl exit code
A successful TCP handshake is not a working proxy. nc -z only proves that something accepted a connection on that port. A different service may sit behind it, the proxy may reject you on authentication or an access list, and a CONNECT tunnel to 443 can be forbidden separately from plain HTTP. Test with the same protocol you intend to use.

The simplest way to confirm traffic really flows through the proxy is to compare your public address with and without it — an IP address check does that in one step. If the address is unchanged, the proxy is not being applied, no matter how alive its port looks. How public addressing works and why a machine has several addresses at once is covered in how to find an IP address.

Diagram of proxy verification: TCP connection, proxy response code and public IP comparison with and without the proxy
TCP reachability, a proxy response and a changed public address are three independent checks, not one.

Why a port is "open" but the service does not answer

The most common complaint and the most underrated cause. Here is a live example. On the server, ss shows the port listening:

tcp LISTEN 0 65535 127.0.0.1:3000 0.0.0.0:* users:(("dockerd",pid=2489,fd=115))

From the machine itself everything works:

curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3000/
# 302

From the same server's public address it does not:

curl -sS -o /dev/null -w '%{http_code}\n' http://203.0.113.10:3000/
# curl: (7) Failed to connect to 203.0.113.10 port 3000: Connection refused

Nothing is broken. The service fundamentally does not listen on the public address — it is bound to 127.0.0.1. A scanner from outside will report the port as closed, and it will be right.

SymptomWhat you seeLikely causeHow to check
Instant refusalConnection refused, immediate curl: (7)Nothing listening, or bound to 127.0.0.1ss -tulpn on the host; read the address left of the port
Hangs until timeoutSilence, then curl: (28) or (7)OS firewall or cloud security group drops the packetsfirewall-cmd --list-ports, ufw status, nft list ruleset, provider console
Works over IPv4, not IPv6Behaviour differs per addressSocket opened only on 0.0.0.0 or only on [::]Both rows in ss -tlnp output
Connects but never answersnc succeeds, curl hangs or resetsWrong protocol: plain HTTP against a TLS port or the reversecurl -v, openssl s_client -connect host:port
In the config but not in ssNothing in the socket tableService never started, failed to bind, or it is EXPOSE without publishingsystemctl status, service logs, docker port

Checking the firewall takes seconds:

# firewalld (RHEL, AlmaLinux, Rocky, Fedora)
firewall-cmd --list-ports
firewall-cmd --list-services

# ufw (Ubuntu, Debian)
sudo ufw status verbose

# nftables and iptables directly
sudo nft list ruleset
sudo iptables -L -n

# which addresses exist to bind to in the first place
ip -br a

Keep in mind one layer that is invisible from inside the machine entirely: cloud security groups and provider network ACLs. Those rules live outside the operating system, so firewall-cmd reports a clean configuration while packets still never arrive. If everything looks right inside and it is silent outside, go to the hosting control panel.

One more situation: NAT or a reverse proxy sits between the internet and the server. Then the port you knock on from outside and the port the application listens on are different numbers, and they need not match. Compare the whole chain rather than the numbers; a route trace helps establish how far traffic actually gets.

How to check ports online

When you need to look at your own server from the outside — from a different address, a different network, without touching the local firewall — an online check is more convenient. It answers immediately the one question you cannot answer from inside: is the port visible from the internet?

A working sequence looks like this:

  1. IP check — confirm which address your server actually presents to the internet. Scanning the wrong address is the most frustrating way to waste time, especially with a CDN in front of the origin.
  2. Ping and availability — make sure the host responds at all and latency is sane. A blocked ICMP does not mean TCP ports are blocked too.
  3. Port check — see which of your host's ports answer from the internet, then compare that list with what ss -tulpn reported on the server itself. The difference is the answer: what the firewall blocks versus what merely listens on loopback.
  4. Monitoring — if a port is critical, put it under continuous checks. A crashed service and a port silently closed by a firewall rule update look identical, and both get noticed equally late.

Only test hosts you own or are authorised to test.

Diagram of failure layers: service bound to 127.0.0.1, OS firewall dropping packets, cloud security group blocking from outside
Three layers where a connection is lost: the bind address, the OS firewall, and the provider's network rules.

FAQ

Why does netstat say "command not found" while ss works

netstat belongs to the net-tools package, which many modern distributions no longer install by default because it is considered legacy. ss from iproute2 is essentially always present and does the same job, and the -tulpn flags are identical. Installing net-tools out of habit is usually unnecessary.

Why does ss -tulpn show no processes

The kernel exposes the socket-to-process mapping only to a privileged user. As an ordinary user the Process column stays empty even though the ports are listed. Re-run the command with sudo.

Can I find a port without connecting to the host

No. From outside, a port only reveals itself through its reaction to a connection attempt. No DNS record, WHOIS entry or HTTP header tells you which ports a server listens on. The one exception is a port written explicitly in a URL after the colon — and that belongs to that particular link, not to the host as a whole.

Why do my outgoing connections always use different ports

Those are ephemeral ports. For each new outbound connection the kernel allocates a free number from the dynamic range (RFC 6335 reserves 49152–65535 for this, but Linux defaults to a wider range — read the exact bounds from /proc/sys/net/ipv4/ip_local_port_range). A service's listening port and a client's local port are different things.

I see both 0.0.0.0:443 and [::]:443 — is that two services

Usually not: it is one process with separate sockets for IPv4 and IPv6. Confirm it by checking that the PID in the Process column matches. The worrying case is the opposite — if only one address family is listed, some clients will never reach the service.

The port is allowed in the firewall but a scanner still reports it closed

Check the bind address in ss -tulpn. If it reads 127.0.0.1, the firewall rule is irrelevant: packets from the network never reach the socket at all. The second candidate is a cloud security group, which operates outside the operating system and never appears in firewall-cmd.

Checklist

  • Decide which of the two questions you are answering: which port my process listens on, or which port a host responds on.
  • From inside Linux: ss -tulpn under sudo. Without root the processes stay hidden.
  • A missing netstat is normal, not a fault — install net-tools only if you genuinely need it.
  • Read the address to the left of the port: 0.0.0.0 and [::] face the network, 127.0.0.1 is local only.
  • Process by port: ss -tlnp 'sport = :N', lsof -nP -i :N or fuser -n tcp N, then systemctl status <PID>.
  • On Windows use netstat -ano plus tasklist; on macOS use lsof -nP -iTCP -sTCP:LISTEN, never netstat -tulpn.
  • The config shows intent, ss shows fact. When they disagree, trust the socket table.
  • From outside, separate three outcomes: refusal is instant, filtering times out, an open port answers immediately.
  • Take the proxy port from your access string or config, never from a list of "typical" numbers.
  • Verify a proxy with the protocol you will actually use: nc -z only proves TCP reachability.
  • A name in /etc/services is a caption, not proof of what runs behind the number.
  • Scan only hosts you own, and only with the owner's permission.

Check your website right now

Check if your site is reachable →
More articles: Networking
Networking
IP Geolocation Accuracy: How It Works and Where It Fails
11.03.2026 · 622 views
Networking
Cloudflare in Russia 2026: Blocks, Risks, and What Site Owners Should Do
20.07.2026 · 540 views
Networking
ERR_CONNECTION_REFUSED: Causes and Fix
23.06.2026 · 483 views
Networking
ERR_CONNECTION_TIMED_OUT Fix
23.06.2026 · 419 views