Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

UFW Firewall Rules for Web Servers

By Kokil Thapa | Last reviewed: August 2026

Leaving a production server exposed without a host-based firewall is a risk no amount of application-level patching can fix. Configuring UFW firewall rules for web servers correctly blocks automated scanners, restricts database access, and ensures only intended traffic reaches your Laravel, WordPress, or Node.js applications. This guide provides the exact commands and verification steps I use on Ubuntu 24.04 LTS systems hosting client projects in Nepal and abroad.

How Do You Safely Enable UFW Firewall Rules for Web Servers Without Locking Yourself Out?

The most common disaster when configuring server security in Nepal or anywhere else is enabling UFW without first allowing SSH. The firewall defaults to denying all incoming connections, and if port 22 isn't whitelisted, you lose access immediately. On remote VPS instances where console access requires a provider dashboard, this turns a five-minute task into an hour-long recovery.

I follow this exact sequence on every new Ubuntu 22.04 or 24.04 server before deploying any application:

  1. Verify current SSH connection: Confirm you're connected via SSH and know the port. If you've changed SSH from port 22 to something like 2222, adjust commands accordingly.
  2. Set default policies: Run sudo ufw default deny incoming and sudo ufw default allow outgoing. This establishes a secure baseline without activating enforcement yet.
  3. Allow SSH explicitly: Run sudo ufw allow 22/tcp (or your custom SSH port). Use /tcp to avoid opening UDP unnecessarily.
  4. Enable UFW: Run sudo ufw enable. You'll see a warning that existing SSH sessions may be disrupted; since SSH is already allowed, confirm with y.
  5. Test immediately: Open a new terminal window and SSH into the server. Don't close your existing session until the new one connects successfully.

This order matters because UFW applies rules atomically when enabled. Setting defaults first, then adding exceptions, then enabling ensures there's never a window where SSH is blocked. On legal-tech portals like Court Marriage In Nepal or Notary Nepal, where uptime directly affects clients needing urgent document processing, I've seen this discipline prevent costly outages during initial hardening.

1. Verify SSHConfirm port & session2. Set Defaultsdeny in / allow out3. Allow SSHufw allow 22/tcp4. Enable UFWufw enable + test⚠ NEVER enable without SSH ruleResults in immediate lockout on remote servers
Safe UFW enablement sequence: always allow SSH before activating firewall rules for web servers

Which UFW Firewall Rules for Web Servers Are Essential for Laravel and PHP Applications?

Laravel applications running on Nginx or Apache with PHP-FPM need more than just ports 80 and 443. Queue workers, scheduled tasks, and debugging tools each have specific network requirements that must be reflected in your UFW configuration. Based on deployments for projects like Nepal Gift Card and Adventure Third Pole Trek, these are the rules I apply consistently:

# Core web traffic
sudo ufw allow 80/tcp    # HTTP (redirects to HTTPS)
sudo ufw allow 443/tcp   # HTTPS (primary application traffic)

# SSH management
sudo ufw allow 22/tcp    # Or custom SSH port

# Laravel-specific services (restrict by IP in production)
sudo ufw allow from 127.0.0.1 to any port 6379 proto tcp  # Redis (local only)
sudo ufw allow from 127.0.0.1 to any port 3306 proto tcp  # MySQL (local only)

# Optional: Laravel Telescope/Horizon (development/staging only)
sudo ufw allow from 103.69.124.0/24 to any port 8080 proto tcp  # Office IP range

A critical pattern here is binding database and cache ports to localhost. Redis and MySQL should never accept connections from arbitrary IPs. Even if your application server and database are on the same machine, using from 127.0.0.1 prevents external exploitation if a service misconfiguration accidentally binds to 0.0.0.0. For multi-server setups where the database lives elsewhere, replace 127.0.0.1 with the specific private IP of the application server.

For Laravel queue workers using Redis or database drivers, no additional inbound ports are needed since queues are internal processes. However, if you're running Laravel Horizon on a non-standard port for monitoring, restrict it to trusted IPs only. I've audited Laravel applications in Nepal where Horizon was publicly accessible without authentication—a single misconfigured UFW rule away from exposing sensitive job data.

Handling Let's Encrypt Certificate Renewals

Certbot HTTP-01 challenges require port 80 to be open. Since we already allow HTTP for redirects, this works automatically. If you're using DNS-01 challenges instead (common for wildcard certificates), no additional UFW rules are needed since validation happens via DNS records, not inbound HTTP.

How Do UFW Firewall Rules Differ Between Nginx, Apache, and Node.js Web Servers?

While the core principles remain the same, each web server has distinct operational characteristics that affect firewall configuration. Understanding these differences prevents both over-permissive rules and accidental service breakage.

CriteriaNginx + PHP-FPMApache + mod_phpNode.js (Express/Nest)
Primary ports80, 44380, 44380, 443 (reverse proxy) or 3000/8080 (direct)
Internal process portsPHP-FPM socket or 9000 (local)None (embedded)None if reverse-proxied; app port if direct
Static file servingNginx handles directlyApache handles directlyTypically reverse-proxied through Nginx
WebSocket supportRequires proxy_pass upgrade headersmod_proxy_wstunnelNative; ensure proxy preserves Upgrade header
Common mistakeExposing PHP-FPM port 9000 externallyLeaving mod_status publicly accessibleRunning Node directly on 80/443 without reverse proxy
Recommended setupNginx as frontend, PHP-FPM on Unix socketApache with mpm_event + PHP-FPMNginx reverse proxy → Node on localhost:3000

For Node.js applications, I strongly recommend always placing Nginx or Caddy in front rather than exposing Node directly. This lets UFW manage only standard web ports while Node listens solely on localhost. Direct Node exposure means managing TLS termination, static files, and compression at the application level—all better handled by a dedicated reverse proxy. On eCommerce platforms like Quick And Easy Nepalese Grocery, this architecture simplified both security and performance tuning.

Nginx + PHP-FPMInternetPorts 80, 443 OPENNginxTLS terminationPHP-FPMUnix socket / localhost:9000✓ Only web ports exposedNode.js + Reverse ProxyInternetPorts 80, 443 OPENNginx ProxyTLS + static filesNode.js Applocalhost:3000 ONLY✓ Node never exposed directly⚠ Anti-pattern: Node on port 80/443 without proxyRequires root privileges, exposes app to direct attacks, complicates UFW rules
UFW port exposure comparison: Nginx+PHP-FPM versus Node.js reverse proxy patterns for web servers

How Do You Restrict Database and Cache Ports Using UFW on Production Servers?

Databases and caches are frequent attack vectors when accidentally exposed to the internet. MySQL, PostgreSQL, Redis, and MongoDB should never accept connections from untrusted networks. UFW makes this straightforward with source-address restrictions, but the syntax trips people up.

# MySQL - local application only
sudo ufw allow from 127.0.0.1 to any port 3306 proto tcp comment 'MySQL local'

# PostgreSQL - allow from specific app server in private network
sudo ufw allow from 10.0.1.50 to any port 5432 proto tcp comment 'PostgreSQL app-server'

# Redis - strictly local (never expose remotely without TLS + auth)
sudo ufw allow from 127.0.0.1 to any port 6379 proto tcp comment 'Redis local'

# MongoDB - bind to localhost in mongod.conf AND restrict via UFW
sudo ufw allow from 127.0.0.1 to any port 27017 proto tcp comment 'MongoDB local'

Note the proto tcp specification. Omitting it opens both TCP and UDP, which is unnecessary for these services and widens the attack surface. The comment flag is optional but invaluable when auditing rules months later—I can immediately see why each rule exists without checking documentation.

For servers where the database runs on the same host as the application, consider skipping UFW rules for database ports entirely and relying solely on the service's own bind-address configuration (e.g., bind-address = 127.0.0.1 in MySQL's my.cnf). Defense in depth is good, but if the service only listens on localhost, UFW rules for that port are redundant. Reserve UFW restrictions for cases where the service must listen on a non-loopback address for legitimate reasons, such as replication or multi-server architectures.

IPv6 Considerations for Modern Deployments

Ubuntu enables IPv6 in UFW by default when /etc/default/ufw contains IPV6=yes. Rules specified without an explicit version apply to both IPv4 and IPv6. However, from 127.0.0.1 only matches IPv4 loopback. For dual-stack localhost restrictions, add a companion rule:

sudo ufw allow from ::1 to any port 3306 proto tcp comment 'MySQL local IPv6'

If your server doesn't use IPv6 externally, you can safely leave IPv6 enabled in UFW for localhost services without exposing anything. Disabling IPv6 entirely (IPV6=no) is unnecessary and can break local service communication on modern Ubuntu systems.

What Is the Correct Way to Manage UFW Rules During Deployment and Maintenance?

Firewall rules shouldn't be managed ad-hoc on live servers. Treat them as infrastructure code. On projects using Deployer 7 with GitLab CI—including sister sites like notarykathmandu.com and translationnepal.com—I include UFW configuration in the provisioning phase, separate from application deployment.

  • Provisioning (Ansible/cloud-init): Define base rules (SSH, HTTP, HTTPS) during server creation. This ensures every server starts with a known-good firewall state.
  • Application deployment: Avoid modifying UFW during deploys unless absolutely necessary. If a new service requires a port, add it to the provisioning playbook first, test in staging, then apply to production.
  • Rule auditing: Run sudo ufw status numbered verbose monthly. Numbered output makes deletion precise (sudo ufw delete 3). Verbose shows hit counts, revealing unused rules.
  • Emergency access: Maintain a documented procedure for temporary IP whitelisting during incidents. sudo ufw prepend allow from 203.0.113.45 to any port 22 adds a rule at the top without disrupting existing ordering.
ProvisioningBase rules via AnsibleSSH, HTTP, HTTPSDeploymentApp code onlyNo UFW changes normallyMonthly Auditufw status numberedCheck hit countsEmergencyufw prepend allowTemporary IP whitelistStaging Test Required Before Production Rule ChangesNew port needed? Add to playbook → test staging → deploy to prod✓ Infrastructure-as-code prevents drift across Nepal & global serversEvery rule reproducible, auditable, version-controlled
UFW rule management lifecycle: provisioning, deployment, audit, and emergency access for production web servers

One gotcha I've encountered repeatedly: after deleting a rule by number, remaining rule numbers shift. Always re-run ufw status numbered before subsequent deletions. Better yet, delete by specification (sudo ufw delete allow 80/tcp) to avoid numbering confusion entirely.

Securing Your Server Starts With Correct UFW Firewall Rules for Web Servers

Getting UFW firewall rules for web servers right is foundational infrastructure work that pays dividends in security, compliance, and peace of mind. The commands and patterns here reflect real configurations running on production systems today—from legal-tech portals handling sensitive client documents to eCommerce platforms processing payments via eSewa and Khalti. Start with the safe enablement sequence, layer in application-specific restrictions, and treat your firewall as code rather than afterthought. If you need help auditing your server's firewall configuration or implementing these rules on an existing deployment, reach out to discuss your infrastructure needs.

Frequently Asked Questions

Allow SSH on port 22, HTTP on port 80, and HTTPS on port 443. Enable the firewall only after adding these rules to prevent locking yourself out of the server.

Run sudo ufw allow 22/tcp before enabling. Verify with sudo ufw status numbered. Only then run sudo ufw enable. Never skip verifying the SSH rule exists first.

Yes, for network-level protection. UFW blocks unauthorized ports but does not replace application security like input validation, CSRF protection, or rate limiting within Laravel itself.

Use sudo ufw allow from YOUR_IP to any port 22 proto tcp. Delete the generic allow 22 rule afterward with sudo ufw delete allow 22/tcp. This prevents brute-force attacks from unknown sources while maintaining your administrative access through whitelisted IPs only.

UFW is a frontend wrapper for iptables designed for simplicity. On Ubuntu 22.04 and 24.04 servers I manage, UFW handles 95% of web server firewall needs without complex syntax. Use raw iptables only for advanced NAT, packet mangling, or custom chain logic that UFW cannot express through its simplified interface.

Add sudo ufw allow 3000/tcp for development or your specific production port. If behind Nginx reverse proxy, keep the Node port closed externally and only allow 80/443. In my experience deploying Laravel and Node applications on shared infrastructure, exposing backend ports directly bypasses SSL termination and creates unnecessary attack surface.

Not effectively. UFW operates at the network layer and cannot inspect HTTP content or user agents. For bot mitigation, use fail2ban to parse logs and dynamically add UFW deny rules, or implement application-level rate limiting. I regularly pair fail2ban with UFW on client servers to automatically ban IPs triggering excessive 404s or authentication failures.

Whitelist the gateway's documented IP ranges using sudo ufw allow from GATEWAY_IP to any port 443 proto tcp. Do not open all inbound HTTPS for webhook endpoints. Payment providers like eSewa and ConnectIPS publish static IP lists; verify these annually as they change. Blocking non-whitelisted sources prevents forged webhook attacks targeting your order processing endpoints.

All incoming connections are blocked by default, including SSH. You will lose remote access immediately. Always define allow rules for required services before running sudo ufw enable. Recovery requires console access through your hosting provider. I have seen this lockout happen during rushed deployments when engineers skip the verification step.

Set logging level with sudo ufw logging medium or high. Logs appear in /var/log/ufw.log. Medium logs denied packets matching default policies; high logs everything including allowed traffic. Rotate logs via logrotate to prevent disk exhaustion. On production legal-tech portals I maintain, medium logging provides sufficient audit trails without filling disks during traffic spikes.

Rarely. Built-in profiles like Apache Full or Nginx HTTP assume default configurations that may not match your setup. Explicit port rules are more predictable and auditable. When managing multiple client servers across different PHP versions and web server configs, I avoid profiles entirely to prevent accidental exposure of unintended services or mismatched port assumptions during deployment automation.

Run sudo ufw disable to stop filtering immediately. Re-enable with sudo ufw enable once resolved. Alternatively, use sudo ufw reload to apply config changes without stopping the firewall. Never leave UFW disabled in production beyond active debugging sessions. On servers I manage, I document every temporary disable event and set reminders to re-enable within the same maintenance window.

Yes. UFW rules are stored in /etc/ufw/user.rules and load automatically via systemd. No cron jobs or startup scripts needed. Verify persistence after major upgrades by checking sudo ufw status post-reboot. During Ubuntu 22.04 to 24.04 migrations on client infrastructure, I have occasionally seen rules reset when ufw package configuration was overwritten; always backup /etc/ufw before dist-upgrades.

Use sudo ufw limit 22/tcp instead of allow. This permits six connections per thirty seconds from new sources before temporarily blocking. Combine with fail2ban for persistent bans based on failed login attempts. Rate limiting alone slows attackers but does not stop determined ones. On Nepal-based client servers targeting local admin panels, I always pair UFW limits with key-only SSH authentication and fail2ban.

None beyond allowing outbound HTTPS. Certbot initiates outbound connections to ACME servers; UFW defaults allow all outbound traffic. If you customized outbound policies, ensure sudo ufw allow out 443/tcp exists. HTTP-01 challenges require inbound port 80 during validation windows, which should already be open for web traffic. DNS-01 challenges need no inbound rules at all.

Share this article

Quick Contact Options
Choose how you want to connect me: