
A single firewall tweak can turn a healthy server into a black box in seconds. SSH stops responding. HTTPS hangs. Monitoring starts screaming. Now you have to figure out whether the culprit is the firewall, the web stack, DNS, or something upstream. This firewall troubleshooting tutorial gives you a safe, repeatable recovery process for Ubuntu/Debian and AlmaLinux/Rocky hosts used for web workloads.
The goal isn’t “turn it off and hope.” You’ll pinpoint where traffic dies, regain access without making things worse, and add guardrails so the same change doesn’t bite you twice.
What this firewall troubleshooting tutorial covers (and what it assumes)
This guide focuses on the failure modes you’ll hit most often on hosting servers:
- You ran a command (UFW / iptables / firewalld) and now SSH is unreachable.
- SSH works, but HTTP/HTTPS, mail, or control-panel ports are failing.
- Only some networks can reach you (office works; mobile doesn’t).
- You’re behind a reverse proxy/CDN and can’t tell what to allowlist.
Assumptions:
- You have some out-of-band option: provider console, rescue mode, IPMI/KVM, or at least a second open SSH session.
- You can identify what should be open (typical: 22/SSH, 80/HTTP, 443/HTTPS; plus SMTP/IMAP if you host email).
Step 0: Stabilize before you touch anything
Start here, even if you’re under pressure. These steps reduce the chance of a follow-up outage.
The most common mistake is locking yourself out again while “fixing” the first lockout.
- Open a second admin session if you still have SSH. Keep the first session idle as a fallback.
- Start a root shell that won’t die if your SSH drops:
sudo -i
# optional but smart if installed
command -v tmux && tmux new -s fwfix
- Schedule an automatic firewall rollback before you change rules. On systemd-based distros, a simple timer works:
# Create a one-shot rollback job in 10 minutes (Ubuntu/Debian)
cat > /root/fw-rollback.sh <<'EOF'
#!/bin/sh
# adjust to your environment
ufw --force disable 2>/dev/null
systemctl stop firewalld 2>/dev/null
iptables -F 2>/dev/null
ip6tables -F 2>/dev/null
EOF
chmod +x /root/fw-rollback.sh
systemd-run --on-active=10m /root/fw-rollback.sh
If you fix the issue quickly, cancel the rollback. List and stop the transient unit:
systemctl list-units --type=service | grep fw-rollback || true
# then
systemctl stop run-*.service
If you don’t have SSH at all, jump to the “No SSH access” section below.
Step 1: Prove whether the server is alive (or if it’s DNS / routing)
Run these from your laptop (not from the server). They separate firewall drops from dead listeners and DNS issues.
# Replace with your server IP
IP=203.0.113.10
ping -c 3 $IP
# Is the port reachable at the TCP level?
# macOS/Linux: nc is usually available
nc -vz -w 3 $IP 22
nc -vz -w 3 $IP 80
nc -vz -w 3 $IP 443
# For HTTPS specifically
curl -vk --connect-timeout 5 https://$IP/ 2>&1 | head -n 30
- Ping fails: ICMP may be blocked (common) or the host may be down. Don’t assume either way.
- nc says “timed out”: a firewall drop is likely (server-side or provider-side).
- nc says “refused”: the firewall let the SYN through, but nothing is listening on that port.
- IP works but domain fails: DNS issue. Don’t burn time rewriting firewall rules.
If you need a DNS-first workflow, use HostMyCode’s domain management to keep records tidy. You can also test changes quickly via HostMyCode Domains.
Step 2: Identify what firewall stack you’re actually using
Modern Linux hosts often have more than one layer. UFW or firewalld sits on top, with nftables/iptables underneath.
First, confirm what’s active right now.
# Ubuntu/Debian often uses UFW
command -v ufw && sudo ufw status verbose
# RHEL/AlmaLinux/Rocky often uses firewalld
systemctl is-active firewalld && sudo firewall-cmd --state
# Under the hood, check nftables/iptables
command -v nft && sudo nft list ruleset | head -n 80
sudo iptables -S | head -n 80
sudo ip6tables -S | head -n 80
The common gotcha: you “disabled UFW,” but nftables rules are still loaded. The reverse can happen too.
Fix the layer that’s actually enforcing policy.
Step 3: If SSH is down — recover access safely
If you can’t SSH in, use your provider console or a rescue environment. This is why console access matters on VPS and dedicated servers.
If you’re running production sites, consider keeping them on a managed VPS hosting plan. It helps during lockouts, migrations, and urgent firewall changes.
Option A: Provider console (preferred)
Log in via the web console. Then confirm sshd is running and listening:
systemctl status ssh --no-pager || systemctl status sshd --no-pager
ss -lntp | grep -E ':22\s'
If sshd isn’t listening, fix the service first. That’s not a firewall issue:
sudo systemctl enable --now ssh || sudo systemctl enable --now sshd
sudo journalctl -u ssh -u sshd -n 80 --no-pager
If sshd is listening, you likely blocked port 22. Move on to the firewall rules.
Option B: Rescue mode (if the firewall blocks even the console network path)
In rescue, mount the root filesystem and inspect persistent firewall configs.
For UFW, persistent rules live under /etc/ufw/. For nftables, common persistence points include /etc/nftables.conf or distro-specific units. For firewalld, look under /etc/firewalld/.
If you need a quick way to regain access on the next boot, prevent the firewall front-end from auto-starting. Do this until you can log in and rebuild rules cleanly.
# on the mounted root (example assumes /mnt is your root mount)
chroot /mnt /bin/bash
systemctl disable ufw 2>/dev/null || true
systemctl disable firewalld 2>/dev/null || true
Reboot, SSH in, then rebuild rules cleanly.
Step 4: If SSH works but web/mail is broken — use “listen vs allow” checks
This is where people waste the most time. A firewall can’t fix a service that isn’t listening.
A service can’t help you if the firewall drops traffic before it arrives.
4.1 Check services are listening on expected ports
# Web
ss -lntp | grep -E ':(80|443)\s'
# Mail (if applicable)
ss -lntp | grep -E ':(25|465|587|110|143|993|995)\s'
If nothing is listening on 80/443, your “site down” problem is probably Nginx/Apache/PHP-FPM, not the firewall.
For a structured approach to slow or failing web responses, see this VPS troubleshooting workflow.
4.2 Confirm firewall is allowing the ports (UFW example)
sudo ufw status numbered
sudo ufw show listening
If 80/443 aren’t allowed, add them explicitly and reload:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
4.3 Confirm firewall is allowing the ports (firewalld example)
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --zone=public --list-all
sudo firewall-cmd --zone=public --add-service=http --permanent
sudo firewall-cmd --zone=public --add-service=https --permanent
sudo firewall-cmd --reload
Step 5: Find the exact rule that drops your traffic
If you’re still unsure what’s wrong, stop changing rules blindly. Collect evidence first.
Then make the smallest possible change.
5.1 Look for a “default deny” that’s too aggressive
These are the usual foot-guns:
- Default policy set to DROP without allowing established connections.
- Allowing SSH only from an old IP (your ISP changed, or you’re on mobile).
- Blocking ephemeral return traffic due to incorrect state tracking rules (rare on modern stacks, common on hand-rolled iptables).
- Forgetting IPv6 entirely (clients try AAAA first, and your IPv6 firewall blocks 443).
5.2 Use logs: kernel drops, UFW logs, and firewalld logs
On Ubuntu/Debian with UFW logging enabled:
sudo ufw logging on
sudo tail -f /var/log/ufw.log
On systems relying on kernel log/journald:
sudo journalctl -k -f
On firewalld, enable logging of denied packets (use sparingly):
sudo firewall-cmd --set-log-denied=all
sudo journalctl -f | grep -i denied
Once you see the blocked destination port and the source IP range, write a minimal allow rule. Avoid opening broad ranges “just to test.”
Step 6: Fix the “IPv6 makes it look random” problem
In 2026, many client networks prefer IPv6. If your domain publishes an AAAA record, browsers will try IPv6 first.
If IPv6 is misconfigured or blocked, the outage looks inconsistent. Some users connect (IPv4). Others time out (IPv6).
6.1 Check whether you publish AAAA
dig +short AAAA example.com
dig +short A example.com
6.2 Check IPv6 firewall rules
sudo ip6tables -S | head -n 120
# or nft
sudo nft list ruleset | grep -n 'ip6' | head -n 40
If you don’t intend to serve IPv6 yet, fix it in DNS. Remove the AAAA record.
If you do intend to serve IPv6, mirror your IPv4 policy. Allow 80/443, and keep stateful established/related traffic permitted.
Step 7: Avoid self-lockout with a safe “SSH allowlist” pattern
Allowlisting SSH is good practice. It becomes risky when your IP changes.
Build an approach that survives normal day-to-day work.
- Keep a break-glass path: provider console access and at least one emergency user key.
- Allowlist a small set of stable egress IPs (office VPN, static IP, bastion).
- Optionally keep port 22 open but rate-limit and harden SSH aggressively.
If you haven’t standardized SSH yet, follow the SSH key setup tutorial. Then lock it down with allowlists and auditing.
Step 8: Validate from both sides (server and client) before you close the ticket
After you change rules, verify you fixed the right layer. Test locally and from outside.
8.1 On the server: confirm counters and listening sockets
# Web is listening
ss -lntp | grep -E ':(80|443)\s'
# If using nftables, you can add temporary counters by listing ruleset
sudo nft list ruleset | sed -n '1,200p'
8.2 From an external host: test TCP and HTTP
IP=203.0.113.10
nc -vz -w 3 $IP 22
nc -vz -w 3 $IP 443
curl -sS -o /dev/null -w '%{http_code} %{time_connect} %{time_starttransfer}\n' https://$IP/
If time_connect is high, suspect packet loss or upstream filtering. If connect is fast but time_starttransfer is high, the app stack is slow (PHP, backend, database).
Step 9: Common hosting-specific pitfalls (and the exact fixes)
These show up constantly on VPS and dedicated servers hosting multiple sites, mail, or control panels.
9.1 You opened 80/443 but HTTPS still fails (SNI/cert chain issues)
If TCP 443 connects but browsers show SSL errors, the firewall isn’t the issue. Check the certificate chain and confirm the right server block answers for the hostname.
For Nginx on Ubuntu, start here:
sudo nginx -t
sudo tail -n 120 /var/log/nginx/error.log
# Inspect cert served
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -issuer -dates
For cPanel systems, use the dedicated guide: cPanel SSL certificate management.
9.2 Your firewall blocks ACME challenges (Let’s Encrypt renewals fail)
HTTP-01 validation needs inbound 80. Even if you redirect to HTTPS, port 80 has to be reachable during issuance and renewal.
- Allow inbound
80/tcp(recommended). - Or switch to DNS-01 validation (requires DNS API support).
If you’re using Nginx and Certbot, the full workflow is covered here: SSL certificate setup guide for Ubuntu VPS.
9.3 Your provider requires reverse DNS for mail reputation (not firewall, but looks like “mail down”)
Outbound SMTP may be blocked by provider policy. Your IP may also have reputation issues.
First confirm your firewall allows inbound/outbound mail ports. Then verify rDNS and SPF/DKIM/DMARC.
Use these companion tutorials if email is part of your stack:
9.4 You allowed only your CDN IPs and locked out real health checks
If you use a CDN or external uptime monitoring, remember the monitor’s IP ranges too.
A safer pattern is usually:
- Allow 80/443 from everywhere.
- Protect the origin with app-level controls (rate limiting, WAF rules, auth for admin paths).
- If you must restrict the origin, keep a second hostname without CDN for monitoring (locked down with basic auth or IP allowlist).
For practical bot and abuse controls at the web layer (without bricking the site), see: Nginx rate limiting for WordPress and APIs.
Step 10: Build a “known-good” baseline ruleset for typical hosting ports
Once you’re back in, standardize. A baseline beats improvisation during the next incident.
10.1 Baseline inbound ports checklist
- SSH: 22/tcp (prefer allowlist; otherwise open + rate-limit + keys only)
- Web: 80/tcp, 443/tcp
- Optional mail (only if you host it): 25, 465, 587, 143, 993, 110, 995
- Control panels (only if used): cPanel/WHM 2083/2087; DirectAdmin 2222; Plesk 8443 (restrict by IP)
10.2 Example: minimal UFW policy for a single web server
sudo ufw default deny incoming
sudo ufw default allow outgoing
# SSH (tighten later with an allowlist)
sudo ufw allow 22/tcp
# Web
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
If you want a production-grade hand-crafted ruleset with logging and safe defaults, use an iptables-first approach tailored for hosting.
HostMyCode has a full guide here: IPTables firewall ruleset for production web hosting.
Step 11: Add guardrails so you don’t repeat this incident
Most firewall outages are process failures, not technical limits. The fixes aren’t glamorous, but they work.
- Change management: record every firewall change with date, reason, and rollback note (even in a simple text log in
/root/CHANGELOG.txt). - Automation: run a post-change script that checks ports externally (22/80/443) before you log out.
- Monitoring: alert on port reachability, not just HTTP status.
- Backups: keep snapshots before risky maintenance. If you need a clean backup workflow, follow this VPS backup setup guide.
Summary: a fast incident checklist you can paste into your runbook
- Confirm it’s not DNS: test by IP with
ncandcurl. - Identify the active firewall layer: UFW vs firewalld vs nftables/iptables.
- Check “listen vs allow”:
ss -lntpfirst, then firewall rules. - Use logs to find the drop rule: UFW log, journald kernel, firewalld denied logs.
- Don’t forget IPv6 if you publish AAAA.
- After recovery, standardize a baseline ruleset and add rollback guardrails.
If you’re hosting revenue-critical sites, use infrastructure with console access, snapshots, and predictable networking. Those are table stakes for safe firewall work.
A HostMyCode VPS gives you the control you need for clean firewall policies, and it scales nicely as you add domains and services.
If firewall changes keep turning into outages, use a platform where you can test safely and recover quickly. Start with a HostMyCode VPS for full root control, or choose managed VPS hosting if you want an expert to sanity-check security changes and help during incidents.
FAQ
Why does my browser time out instead of showing “connection refused”?
A timeout usually means a firewall (server-side or upstream) is dropping packets. “Connection refused” typically means the firewall allowed traffic, but the service isn’t listening.
Should I disable the firewall to recover quickly?
Only as a last resort, and only if you have console access and a plan to re-enable it with a known-good baseline. Prefer fixing the single missing allow rule and validating externally.
How do I avoid locking myself out when allowlisting SSH?
Keep console access, use SSH keys, and allowlist stable egress IPs (VPN/static IP). If your IP changes often, rely on rate limiting and strong SSH hardening rather than strict allowlists.
My site works on IPv4 but not on some networks. What’s the quickest check?
Run dig AAAA yourdomain. If you publish AAAA, test curl -6 https://yourdomain from a network with IPv6. Fix by removing AAAA or properly opening 80/443 on IPv6.
Which is better for hosting servers: UFW or firewalld?
Both are fine if you standardize and document. UFW is simple on Ubuntu/Debian; firewalld fits RHEL-family systems. The real win is having one owner, one baseline, and external validation after every change.