
Most firewall “problems” on a VPS aren’t real security issues. They’re self-inflicted outages. One rule tweak and SSH stops responding. AutoSSL can’t validate, or mail clients can’t connect.
This VPS firewall troubleshooting tutorial shows how to diagnose and fix common breakages on Ubuntu/Debian and AlmaLinux/Rocky. You’ll do it without guessing, and without locking yourself out.
Before you touch anything: get a safe way back in
If you’re already locked out, stop poking at random rules. Start by confirming a recovery path.
On most VPS platforms, that means a web console (VNC/serial) or a rescue environment.
- Confirm you have out-of-band access: provider console or rescue mode.
- Open a second SSH session (keep it connected) before changing rules.
- Know your active IP (your office/home VPN egress). If you whitelist the wrong one, you’ll self-lock.
If you manage client sites, treat this as standard operating procedure.
Use snapshots, controlled change windows, and a clear rollback plan.
A managed VPS hosting plan from HostMyCode is built around those basics. It also includes recovery help when a rule change goes sideways.
VPS firewall troubleshooting tutorial: identify what’s actually blocking traffic
Don’t assume UFW is to blame. Many VPS setups enforce rules in more than one place.
Common layers include UFW, nftables, iptables-legacy, firewalld, provider security groups, and control-panel tooling.
Your first job is to find the active layer. Then confirm the default policy.
Step 1 — Check whether the firewall is local or upstream
From your laptop, probe a few ports. Replace SERVER_IP:
nc -vz SERVER_IP 22
nc -vz SERVER_IP 80
nc -vz SERVER_IP 443
If nc times out on every port, suspect upstream filtering. That can mean a provider firewall, the wrong IP, or a routing issue.
If some ports connect and others time out, you’re likely dealing with local rules.
Step 2 — On the server, confirm what’s managing rules
SSH in (or use console) and run:
sudo ufw status verbose || true
sudo systemctl is-active firewalld || true
sudo nft list ruleset 2>/dev/null | head -n 40 || true
sudo iptables -S 2>/dev/null | head -n 60 || true
sudo iptables -L -n -v 2>/dev/null | head -n 60 || true
- Ubuntu/Debian hosting VPS: often UFW + nftables backend.
- AlmaLinux/Rocky: commonly firewalld (nftables) unless you installed something else.
- cPanel servers: may use CSF/LFD or custom iptables/nft rules layered with WHM security settings.
Next, confirm the service is actually listening. A firewall can’t block a daemon that never bound to the port.
sudo ss -lntp
sudo ss -lnup | head -n 60
If you’re tightening SSH at the same time, also verify you didn’t break access in sshd_config.
This guide walks through keys, MFA, and safer access patterns: SSH hardening tutorial.
Fix #1: “SSH connection timed out” after a firewall change
A timeout usually means packets aren’t reaching sshd. Work in this order.
First confirm sshd is running. Then confirm port 22 (or your custom port) is allowed from your source IP.
Diagnose quickly
sudo systemctl status ssh || sudo systemctl status sshd
sudo ss -lntp | grep -E ':22\b|:2222\b' || true
If sshd is listening, focus on the default policy.
Also look for explicit drop/reject rules.
Ubuntu/Debian (UFW) recovery pattern
From console, allow SSH first. Then reload and review the rule order:
sudo ufw allow 22/tcp comment 'SSH - recovery'
sudo ufw reload
sudo ufw status numbered
If you use a nonstandard port (example 2222):
sudo ufw allow 2222/tcp comment 'SSH on 2222'
sudo ufw reload
Pitfall: If you set ufw default deny incoming and forgot an SSH allow rule, SSH drops as soon as UFW is enabled.
Keep a second session open while you test.
AlmaLinux/Rocky (firewalld) recovery pattern
sudo firewall-cmd --state
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --zone=public --add-service=ssh
sudo firewall-cmd --zone=public --add-service=ssh --permanent
sudo firewall-cmd --reload
If SSH is on a custom port:
sudo firewall-cmd --zone=public --add-port=2222/tcp
sudo firewall-cmd --zone=public --add-port=2222/tcp --permanent
sudo firewall-cmd --reload
Advanced check: are you blocking by IP?
If you whitelisted an old IP, you’ll often see drops in logs (assuming logging is enabled). On Ubuntu:
sudo journalctl -k --since "30 min ago" | grep -i -E 'ufw|drop|reject' | tail -n 80
On RHEL-family with firewalld:
sudo journalctl -k --since "30 min ago" | grep -i -E 'final reject|drop' | tail -n 80
Fix #2: Let’s Encrypt HTTP-01 fails because port 80 is blocked
Let’s Encrypt HTTP-01 validation requires inbound HTTP on port 80. That’s true even if you redirect everything to HTTPS.
If 80 is blocked, you’ll see “timeout” or “connection refused.” Open the path first. Then re-run issuance.
Checklist: confirm the ACME path is reachable
- Port 80 open from the internet.
- Web server listening on 80.
/.well-known/acme-challenge/not blocked by redirects/WAF.
Open port 80 and 443 (UFW)
sudo ufw allow 80/tcp comment 'HTTP for ACME'
sudo ufw allow 443/tcp comment 'HTTPS'
sudo ufw reload
Open port 80 and 443 (firewalld)
sudo firewall-cmd --zone=public --add-service=http
sudo firewall-cmd --zone=public --add-service=https
sudo firewall-cmd --zone=public --add-service=http --permanent
sudo firewall-cmd --zone=public --add-service=https --permanent
sudo firewall-cmd --reload
Quick reachability test from outside
From your laptop or another server:
curl -I http://YOUR_DOMAIN/
curl -I https://YOUR_DOMAIN/
If renewals keep failing, don’t “solve” it by blindly opening ports and moving on.
Check issuance logs. Then confirm your web server routes ACME challenges correctly.
This guide covers the common failure modes: TLS certificate renewal troubleshooting tutorial.
Fix #3: SMTP/IMAP/POP suddenly stop working after “hardening”
Mail breaks often after firewall tightening because email uses several ports.
The website still loads, so the server “looks fine.” Meanwhile inboxes quietly stop syncing.
If you host mail, write explicit rules for submission and retrieval.
Know the ports you actually need
- SMTP submission (recommended): 587/tcp (STARTTLS)
- SMTPS (sometimes used): 465/tcp
- IMAP: 143/tcp, IMAPS: 993/tcp
- POP3: 110/tcp, POP3S: 995/tcp
- Server-to-server SMTP: 25/tcp (only if you run inbound mail)
Many providers restrict outbound 25 by default. That isn’t your firewall.
Opening it locally won’t change anything. For app mail, use authenticated submission on 587.
Open mail ports safely (UFW example)
sudo ufw allow 587/tcp comment 'Mail submission'
sudo ufw allow 993/tcp comment 'IMAPS'
sudo ufw allow 995/tcp comment 'POP3S'
# If you host inbound mail:
sudo ufw allow 25/tcp comment 'SMTP inbound'
sudo ufw reload
Open mail ports safely (firewalld example)
sudo firewall-cmd --zone=public --add-port=587/tcp
sudo firewall-cmd --zone=public --add-port=993/tcp
sudo firewall-cmd --zone=public --add-port=995/tcp
sudo firewall-cmd --zone=public --add-port=25/tcp
sudo firewall-cmd --zone=public --add-port=587/tcp --permanent
sudo firewall-cmd --zone=public --add-port=993/tcp --permanent
sudo firewall-cmd --zone=public --add-port=995/tcp --permanent
sudo firewall-cmd --zone=public --add-port=25/tcp --permanent
sudo firewall-cmd --reload
Verify end-to-end mail connectivity
From an external machine:
openssl s_client -connect mail.YOUR_DOMAIN:587 -starttls smtp -servername mail.YOUR_DOMAIN
openssl s_client -connect mail.YOUR_DOMAIN:993 -servername mail.YOUR_DOMAIN
If you want a more methodical way to sort SMTP timeouts vs TLS negotiation vs auth failures, use this companion guide: SMTP troubleshooting tutorial.
Fix #4: DNS doesn’t respond (or only works locally)
DNS issues after a migration or firewall update often look like “random downtime.”
Browsers and resolvers cache answers, which can hide patterns.
If you host DNS on the VPS (BIND, PowerDNS, Knot, or a minimal authoritative setup), allow both UDP and TCP on 53.
Confirm if you’re actually hosting DNS on this server
sudo ss -lnup | grep ':53\b' || true
sudo ss -lntp | grep ':53\b' || true
Open DNS ports (UFW)
sudo ufw allow 53/udp comment 'DNS UDP'
sudo ufw allow 53/tcp comment 'DNS TCP'
sudo ufw reload
Open DNS ports (firewalld)
sudo firewall-cmd --zone=public --add-service=dns
sudo firewall-cmd --zone=public --add-service=dns --permanent
sudo firewall-cmd --reload
Test with dig from outside
dig @YOUR_SERVER_IP yourdomain.com A +time=2 +tries=1
dig @YOUR_SERVER_IP yourdomain.com SOA +tcp +time=2 +tries=1
If you’re moving DNS between providers, symptoms can overlap with propagation and stale NS records.
This migration flow keeps those variables separated: DNS migration tutorial.
Fix #5: “It works from the server, but not from the internet” (hairpin tests and false positives)
It’s easy to run curl localhost and declare victory. That only proves the daemon works on loopback.
The public path can still fail due to firewall rules, IP binding, or a reverse proxy that isn’t forwarding.
Do three tests, in this order
- Local loopback:
curl -I http://127.0.0.1 - Local IP:
curl -I http://SERVER_PUBLIC_IP - External: from another host:
curl -I http://SERVER_PUBLIC_IP
If #1 succeeds but #2 fails, the service may be bound to 127.0.0.1 only.
If #2 succeeds but #3 fails, you’re almost certainly blocked upstream or by a local firewall.
Make your firewall changes auditable (so you can undo them)
Firewalls are easier to manage when rules are readable and reversible. You don’t need a heavyweight change system.
You do need consistency.
UFW: use numbered rules and comments
sudo ufw status numbered
sudo ufw delete 3
When you add rules, include comments (supported in modern UFW).
That habit saves time during the next 2 a.m. incident.
firewalld: prefer services where possible
Named services are easier to audit than raw port lists:
sudo firewall-cmd --get-services | grep -E 'http|https|ssh|dns|smtp' | head -n 50
Add the rule temporarily first. Test it. Then commit with --permanent.
This pattern prevents “it worked until reboot” outages.
Emergency rollback patterns (use with care)
If you need access back immediately, you can temporarily disable the local firewall. Then repair the rules and re-enable it.
Only do this from console or a trusted session. Keep the open window short.
UFW emergency rollback
sudo ufw disable
# Fix rules
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
firewalld emergency rollback
sudo systemctl stop firewalld
# Fix or add required rules
sudo systemctl start firewalld
sudo firewall-cmd --list-all
Tip: Before changing firewall rules on a production server, take a snapshot.
It’s fast, and it turns recovery into a routine task.
If you’re on HostMyCode, pair that with a restore test process like the one in our VPS snapshot backup tutorial.
Hardening without breaking hosting: a practical “minimum open ports” baseline
These baselines cover a typical web hosting VPS. Adjust for your actual stack (WordPress, cPanel, mail, DNS).
Web-only VPS (common for WordPress)
- 22/tcp (SSH)
- 80/tcp (HTTP, mainly for redirects + Let’s Encrypt)
- 443/tcp (HTTPS)
Web + mail hosting VPS
- All web-only ports
- 587/tcp, 465/tcp (submission)
- 993/tcp (IMAPS), 995/tcp (POP3S)
- 25/tcp (inbound mail, only if you accept mail directly)
cPanel/WHM considerations
cPanel exposes additional admin ports (WHM/cPanel/Webmail). Many teams prefer not to expose those publicly.
A cleaner approach is to firewall them to a small admin allowlist.
Another option is to reach them through an SSH tunnel.
This walkthrough shows how to reach cPanel/WHM without opening extra ports to the internet: SSH port forwarding tutorial.
Quick diagnostics: map “symptom” to “next command”
- SSH times out →
ss -lntp | grep :22, thenufw status numberedorfirewall-cmd --list-all - Let’s Encrypt HTTP-01 fails →
nc -vz YOUR_IP 80, confirm web server listens on 80 - Mail client can’t connect →
openssl s_clientto 587/993, check provider outbound 25 policy - DNS queries time out →
dig @SERVER_IP domain A, ensure UDP+TCP 53 allowed - Only your IP works → look for IP whitelist rules or geo blocks
Summary: keep access, prove the path, then lock it down again
Firewall troubleshooting works best as a sequence.
Secure a recovery channel. Identify which layer is enforcing rules. Confirm the service is listening. Then test from outside the box.
Once traffic flows, keep only the minimum rules. Make them permanent, and document the reason for each one.
If you want a platform where changes are less risky—snapshots, predictable networking, and optional admin help—start with a HostMyCode VPS and move into managed VPS hosting as client workloads grow.
On production servers, firewall edits show up during migrations, SSL renewals, and incident response. HostMyCode reduces the blast radius with dependable routing and VPS options for both hands-on admins and teams that want backup. If you manage client sites, consider managed VPS hosting; if you want full control, start with a HostMyCode VPS.
FAQ
How do I tell if my VPS provider is blocking a port instead of my firewall?
If the service listens locally (ss -lntp shows the port) and your local firewall allows it, but external probes time out, suspect provider filtering. Check provider portal firewalls and outbound SMTP policies.
Should I close port 80 after I install SSL?
Usually no. Keep 80 open so Let’s Encrypt renewals and HTTP-to-HTTPS redirects work reliably. If you must close it, switch to DNS-01 validation and confirm your tooling supports it.
Why does opening port 25 not fix outbound email from my VPS?
Many hosting networks restrict outbound 25 to reduce abuse. Use authenticated submission on 587 (or a mail relay), and validate with openssl s_client plus mail logs.
What’s the safest way to manage cPanel/WHM access without exposing admin ports?
Restrict admin ports to a small IP allowlist, or tunnel access through SSH port forwarding. That keeps the public attack surface smaller while preserving usability.
Can I troubleshoot firewall issues without disabling the firewall?
Yes. Add temporary allow rules for your IP first, validate access, then make them permanent. Disabling is a last resort for emergency recovery from console.