SSD Nodes Learn 🎉 VPS từ $5.50/tháng
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-08-21

Kiểm tra port có mở trên Linux bằng ss, nc, nmap

Dùng ss để biết process nào đang listen, rồi thử từ máy ngoài bằng nc hoặc nmap. Phân biệt port bị firewall drop nên treo với port đóng từ chối ngay.

Kiểm tra một cổng có mở trên Linux: trước tiên hãy xác định đúng câu hỏi

Để kiểm tra một cổng có mở trên Linux hay không, trước tiên hãy xác định bạn đang đặt câu hỏi nào, vì “mở” có ý nghĩa khác nhau tùy vào vị trí kiểm tra. Trên chính server, cổng mở nghĩa là có một process bind vào cổng đó và đang chờ kết nối. Từ một máy khác, cổng mở nghĩa là packet đến được process đó và nhận được phản hồi. Khi không có phản hồi, câu hỏi thực sự là thiết bị nào đã drop packet. sudo ss -ltnp trả lời câu hỏi đầu tiên. nc -z hoặc nmap trả lời câu hỏi thứ hai. Bộ đếm của firewall và tcpdump trả lời câu hỏi thứ ba.

Chạy sai kiểu kiểm tra là nguyên nhân khiến nhiều người mất cả buổi chiều. Một bài test chạy trên server không bao giờ đi qua network firewall của nhà cung cấp, vì bộ lọc đó nằm bên ngoài máy. Nếu port vẫn còn là khái niệm mới với bạn, cách port và socket hoạt động trên Linux giải thích mô hình được dùng trong phần còn lại của hướng dẫn này.

Có tiến trình nào đang listen trên máy này? Đọc output của ss

ss được cài cùng iproute2, nên có mặt trên mọi distribution hiện nay. netstat thuộc net-tools, nhưng Ubuntu đã không cài sẵn gói này trong nhiều năm, vì vậy netstat -tulpn thường trả lời netstat: command not found. Hãy dùng ss để tránh mất thời gian.

sudo ss -ltnp

-l chỉ hiển thị các socket đang listen. -t giới hạn danh sách ở TCP. -n in số thay vì phân giải tên, nên command trả kết quả ngay. -p hiển thị process sở hữu socket và cần quyền root: nếu thiếu sudo, cột Process sẽ trống với mọi process không thuộc tài khoản của bạn. Thay -t bằng -u để xem UDP.

State  Recv-Q Send-Q Local Address:Port  Peer Address:Port Process
LISTEN 0      4096   127.0.0.53%lo:53    0.0.0.0:*   users:(("systemd-resolve",pid=612,fd=14))
LISTEN 0      128    0.0.0.0:22          0.0.0.0:*   users:(("sshd",pid=921,fd=3))
LISTEN 0      511    127.0.0.1:8080      0.0.0.0:*   users:(("node",pid=1442,fd=19))
LISTEN 0      128    [::]:22             [::]:*      users:(("sshd",pid=921,fd=4))

Cột Local Address quyết định mọi thứ, nhưng đây là cột nhiều người thường bỏ qua.

  • 0.0.0.0:22 có nghĩa là mọi địa chỉ IPv4 trên máy, nên có thể truy cập từ bên ngoài nếu firewall cho phép.
  • [::]:22 có ý nghĩa tương tự với IPv6.
  • 127.0.0.1:8080 chỉ có nghĩa là loopback. Không có máy nào bên ngoài máy này truy cập được.
  • 10.20.0.5:5432 có nghĩa là chỉ bind vào một địa chỉ của interface đó, không bind vào địa chỉ nào khác. Cấu hình này thường dùng trong private network.
  • Cột Process trống thường là do thiếu sudo, không phải do không có process.

Để kiểm tra một port, hãy filter ngay trong ss thay vì grep toàn bộ danh sách:

sudo ss -ltnp 'sport = :8080'
sudo lsof -nP -iTCP:8080 -sTCP:LISTEN
sudo fuser 8080/tcp

Nếu cả 3 command đều không trả output, không có process nào đang giữ port đó. Service có thể đã dừng, khởi động thất bại hoặc đang listen ở nơi khác. Hãy đọc systemctl status <unit>journalctl -u <unit> -n 50 trước khi chỉnh bất kỳ firewall rule nào.

Vì sao 127.0.0.1 trong Local Address khiến nhiều người mất cả buổi chiều

Một socket bind vào 127.0.0.1 không thể được truy cập từ host khác, và không có firewall rule nào thay đổi được điều đó. Kernel chỉ route 127.0.0.0/8 qua loopback interface. Một packet có destination address này khi đi vào từ network card thật sẽ bị loại bỏ vì là martian. Vì vậy process vẫn chạy, ss vẫn hiển thị socket đang listening, ufw allow 8080 vẫn báo thành công, nhưng connection từ laptop của bạn vẫn fail. Nó fail ngay với Connection refused, vì packet đến public address của bạn nhưng không tìm thấy socket nào bind tại đó, nên kernel trả về TCP reset.

Nhiều chương trình bind vào loopback có chủ đích. Với database hoặc admin interface, đây là thiết lập mặc định phù hợp. Bạn có 2 lựa chọn rõ ràng. Thay đổi bind address trong config riêng của chương trình (listen_addresses trong postgresql.conf, bind trong redis.conf, hoặc host argument mà application nhận), sau đó mở firewall. Hoặc giữ service trên loopback và truy cập qua một thành phần khác, chẳng hạn nginx reverse proxy hoặc SSH tunnel từ laptop:

ssh -L 8080:127.0.0.1:8080 user@203.0.113.10

Docker cũng phân biệt tương tự trong publish flag. -p 8080:8080 bind vào 0.0.0.0 và expose container ra internet. -p 127.0.0.1:8080:8080 bind vào loopback và giữ container chỉ ở local.

Cách kiểm tra một port có mở trên Linux từ máy khác hay không

Chạy bài kiểm tra này từ một network khác. Kiểm tra từ chính server chỉ chứng minh rằng đường loopback hoạt động. Ngay cả khi kết nối đến public IP của chính server từ server đó, bạn cũng bỏ qua firewall network của nhà cung cấp, vì bộ lọc này chạy bên ngoài VPS.

nc -zv -w 3 203.0.113.10 443

-z kết nối rồi đóng kết nối mà không gửi dữ liệu. -w 3 dừng sau 3 giây; flag này rất quan trọng: nếu không đặt timeout, một packet bị drop sẽ khiến client tiếp tục retry SYN hơn 2 phút trước khi kernel dừng lại. Khi thành công, kết quả sẽ như sau:

Connection to 203.0.113.10 443 port [tcp/https] succeeded!

Nếu thiếu tool (nc: command not found), hãy cài netcat-openbsd trên Debian hoặc Ubuntu, hoặc dùng network redirection tích hợp của bash, không cần cài package:

timeout 3 bash -c '</dev/tcp/203.0.113.10/443' && echo open || echo "no answer"

Cú pháp này là một tính năng của bash, nên hãy chạy bằng bash. /bin/sh trên Debian và Ubuntu là dash; shell này không có /dev/tcp và sẽ báo rằng path không tồn tại. Để kiểm tra một dải port, hoặc khi muốn biết trạng thái cụ thể, hãy dùng nmap với những host bạn có trách nhiệm quản lý:

sudo nmap -Pn -p 22,80,443 203.0.113.10
sudo nmap -Pn -p 1-1024 203.0.113.10

-Pn bỏ qua bước host discovery. Phần lớn VPS host drop ICMP echo, nên nếu không có -Pn, nmap sẽ cho rằng host đang down và không scan gì cả. nmap in open khi có phản hồi và kết nối được chấp nhận, closed khi có phản hồi kèm reset, và filtered khi hoàn toàn không có phản hồi. Với web service, curl -sS -o /dev/null -w '%{http_code}\n' https://example.com giúp phân biệt lỗi network với lỗi application, vì status code chứng minh toàn bộ đường kết nối đã hoạt động.

Vì sao port bị chặn bị treo còn port đóng thì từ chối ngay

Từ chối ngay lập tức. Packet đã đến máy chủ và có thành phần phản hồi.

nc: connect to 203.0.113.10 port 443 (tcp) failed: Connection refused

Hai nguyên nhân khác nhau có thể tạo ra đúng hiện tượng này. Hoặc không có process nào bind vào địa chỉ và port đó, nên kernel trả lời bằng TCP reset; hoặc một firewall rule từ chối packet bằng reset hoặc thông báo ICMP port unreachable. Từ chối là phản hồi xác định và quay về sau một round trip.

Tạm dừng, rồi timeout. Có thành phần đã drop packet và không phản hồi.

nc: connect to 203.0.113.10 port 443 (tcp) timed out: Operation now in progress

Đó là tác dụng của rule DROP, đồng thời cũng là cách provider firewall hoặc cloud security group xử lý. Im lặng là dấu hiệu của việc drop, vì bên gửi không thể phân biệt packet bị drop với host đã ngừng hoạt động.

Triệu chứng cho biết bước tiếp theo cần kiểm tra ở đâu. Nếu bị từ chối, packet vẫn đi qua network bình thường, nên quay lại ss -ltnp và kiểm tra bind address cùng port number. Nếu bị timeout, packet đang bị drop, nên kiểm tra các firewall từ ngoài vào trong. refused và timed out trên SSH phân tích rõ cùng trường hợp này với port 22, là nơi hầu hết mọi người gặp vấn đề này.

ufw cố ý cung cấp cả hai cách xử lý: ufw deny 8080 thực hiện drop, còn ufw reject 8080 gửi phản hồi từ chối. Trong nftables, hai target này là dropreject; trong iptables, chúng là -j DROP-j REJECT. Default policy gần như luôn là drop, nên một rule bị thiếu sẽ làm kết nối bị treo thay vì trả về thông báo lỗi.

Ai đang chặn cổng? Kiểm tra từ bên ngoài vào trong

sudo ufw status verbose
sudo iptables -L INPUT -n -v --line-numbers
sudo nft list ruleset

Các bộ đếm -v mới là phần hữu ích. Chạy bài kiểm tra nc từ bên ngoài, chạy lại lệnh iptables và xem bộ đếm nào đã thay đổi: rule có số gói tăng là rule đang xử lý lưu lượng của bạn. Cách này thay việc đoán bằng bằng chứng.

Bài kiểm tra quyết định được thực hiện trên server. Hãy theo dõi network traffic trong khi bạn kết nối từ bên ngoài:

sudo tcpdump -ni any tcp port 8080

SYN đến nhưng không có SYN-ACK trả về nghĩa là packet đã tới VPS và host đã drop nó. Khi đó firewall của provider hoạt động bình thường, còn các rule cục bộ thì không. Hoàn toàn không có output nghĩa là packet chưa tới server. Nguyên nhân là firewall của provider, security group hoặc địa chỉ IP sai. Chỉ cần phân biệt được hai trường hợp này là bạn loại bỏ được phần lớn công việc kiểm tra.

Có 2 lớp tạo ra những kết quả tưởng như không thể xảy ra. Thứ nhất là IPv6: nếu hostname có bản ghi AAAA, client có thể kết nối qua IPv6 trong khi rule của bạn chỉ áp dụng cho IPv4. Vì vậy, hãy kiểm tra từng address family bằng nc -4nc -6 trước khi tin vào một trong hai kết quả. ufw rule và cổng IPv6 trên VPS giải thích trường hợp không khớp này. Thứ hai là Docker: một cổng của container được publish vẫn phản hồi từ Internet dù ufw status liệt kê cổng đó là bị deny, vì các packet này được xử lý trước khi chain của ufw nhìn thấy chúng. vì sao Docker publish cổng vượt qua ufw trực tiếp giải thích cơ chế và cách sửa. các ufw rule cần đặt trên VPS mới là bộ rule cơ bản nên thiết lập trước.

Vì sao phản hồi UDP vốn không rõ ràng

UDP không có handshake, nên một probe không có điều kiện nào để xác nhận thành công. nc -zu 203.0.113.10 53 thoát với mã 0 ngay khi packet rời máy, chỉ chứng minh máy của bạn đã gửi packet và không chứng minh được gì về đầu bên kia. Khi một UDP port đóng, host thường phản hồi bằng thông báo ICMP port unreachable. Kernel chỉ báo lỗi đó cho connected socket ở lần write tiếp theo, nên probe chỉ gửi một packet sẽ bỏ sót lỗi này. Firewall thường drop ICMP theo mặc định, khiến ngay cả dấu hiệu đó cũng biến mất. Vì vậy, nmap in open|filtered cho hầu hết UDP port: không có phản hồi chính xác là kết quả mà cả service đang mở nhưng im lặng và port bị filter đều tạo ra.

Hãy kiểm tra UDP bằng cách gửi đúng protocol mà bạn cần dùng. DNS server phản hồi dig +short @203.0.113.10 example.com bằng một địa chỉ hoặc không trả về gì. Một WireGuard peer hiển thị dòng latest handshake gần đây trong sudo wg show. Sau đó, xác nhận packet đã đến ở phía server:

sudo tcpdump -ni any udp port 51820

Nếu packet xuất hiện trong lúc client gửi, nghĩa là packet đã đến nơi, nên vấn đề nằm ở service hoặc input chain. Nếu hoàn toàn không có packet, nghĩa là chúng chưa đến được server.

Checklist theo thứ tự giúp tìm lỗi nhanh nhất

  1. Trên server, chạy sudo ss -ltnp 'sport = :8080'. Không có output nghĩa là không có tiến trình nào đang listen, vì vậy hãy sửa service trước.
  2. Nếu có output, đọc cột Local Address. 127.0.0.1 nghĩa là không thể truy cập từ bên ngoài cho đến khi bạn bind lại hoặc đặt một proxy phía trước.
  3. Từ một network khác, chạy nc -zv -w 3 <public ip> 8080.
  4. Kết nối bị từ chối thì quay lại bước 1. Địa chỉ, port hoặc máy có thể không phải là đối tượng bạn nghĩ.
  5. Timeout nghĩa là lưu lượng bị drop. Chạy sudo tcpdump -ni any tcp port 8080 trên server rồi lặp lại bài test.
  6. SYN đến nhưng không có phản hồi: lỗi nằm ở firewall trên host. Tìm rule có counter tăng trong sudo iptables -L INPUT -n -v.
  7. Không có SYN đến: lỗi nằm ở firewall của provider, security group hoặc địa chỉ IP không đúng.

FAQ

Làm thế nào để kiểm tra các cổng đang mở trên máy chủ Linux của chính tôi?

Chạy sudo ss -ltnp cho TCP và sudo ss -lunp cho UDP. Mỗi dòng là một listening socket. Cột Local Address cho biết những địa chỉ nào có thể kết nối: 0.0.0.0[::] chấp nhận kết nối từ mọi nơi mà firewall cho phép, còn 127.0.0.1 chỉ chấp nhận kết nối từ chính máy đó. Cột Process cần quyền root, vì vậy hãy chạy bằng sudo, nếu không cột này sẽ trống. ss thuộc iproute2 và luôn được cài đặt. netstat thuộc net-tools và thường không có sẵn.

Tại sao ss hiển thị service đang listening nhưng tôi vẫn không thể kết nối?

Có 2 nguyên nhân phổ biến. Một lệnh có thể phân biệt chúng. Nếu Local Address là 127.0.0.1, service đã bind vào loopback nên không thể truy cập từ host khác, vì kernel chỉ định tuyến dải địa chỉ đó đến loopback interface. Nếu là 0.0.0.0 mà kết nối vẫn lỗi, hãy chạy sudo tcpdump -ni any tcp port <port> trên server rồi kết nối từ bên ngoài. Nếu có SYN đến nhưng không có phản hồi, rule của firewall local đang drop gói tin. Nếu không có gói nào đến, gói tin đã bị chặn trước khi tới VPS, thường là bởi firewall của nhà cung cấp hoặc security group.

Kết nối bị từ chối khác gì với kết nối bị timeout?

Từ chối là một phản hồi. Gói tin đã tới host và nhận TCP reset hoặc ICMP port unreachable trả về. Điều này có nghĩa là không có tiến trình nào listening trên địa chỉ và cổng đó, hoặc một rule đã reject kết nối. Timeout là im lặng: một rule đã drop gói tin mà không gửi phản hồi, nên client tiếp tục retry cho đến khi bỏ cuộc. Lỗi refused hướng bạn kiểm tra service và địa chỉ bind của nó. Lỗi timed out hướng bạn kiểm tra firewall. Hãy kiểm tra firewall gần phía bên ngoài nhất trước.

Làm thế nào để kiểm tra một cổng UDP có mở hay không?

Bạn không thể nhận được câu trả lời đáng tin cậy từ một probe thông thường, vì UDP không có handshake và một service không phản hồi trông giống hệt một gói tin bị drop. nc -zu trả về success ngay sau khi gửi gói tin. nmap cũng báo open|filtered vì cùng lý do. Hãy kiểm tra bằng cách gửi dữ liệu đúng theo protocol: dig +short @<host> example.com cho DNS, hoặc sudo wg show cho một WireGuard peer có recent handshake. Để xác nhận gói tin đã tới server, chạy sudo tcpdump -ni any udp port <port> trên server trong khi client gửi dữ liệu.

Tôi vẫn có thể dùng telnet host port để kiểm tra cổng không?

Có thể dùng telnet cho TCP. Escape character is '^]' có nghĩa là kết nối đã được chấp nhận. Thoát bằng Ctrl+] rồi chạy quit. Có 2 lý do khiến nc -z là công cụ tốt hơn: telnet không được cài trên hầu hết server image hiện nay, còn nc cho phép đặt timeout bằng -w và thiết lập exit status để kiểm tra trong script. Khi cả 2 công cụ đều không có, timeout 3 bash -c '</dev/tcp/<host>/<port>' không cần cài thêm package nào.