UFW Firewall Rules for Web Servers: What to Open, What to Block, and Why
A newly installed Ubuntu server has every port open by default. This is acceptable for testing on a private network, but when the server is accessible from the internet, it creates an unnecessary risk. Unused services listening on open ports are one of the most common vectors for initial access in server compromises. Configuring a firewall is not optional. It is one of the first tasks you complete after provisioning a new server, before deploying any applications or services.
UFW, which stands for Uncomplicated Firewall, is the default firewall configuration tool on Ubuntu. It wraps iptables in a simpler command structure that handles the most common web server use cases correctly without requiring you to write raw iptables rules. This guide covers which ports to open, which to keep closed, the correct setup sequence, and the specific scenarios where the standard recommendations may not apply.
What UFW Actually Does
UFW is a front-end for iptables, which is netfilter's user-space interface that controls packet filtering on Linux. When you open a port with UFW, it inserts rules into the iptables filter table that either ACCEPT, REJECT, or DROP traffic for that port and protocol.
The difference between REJECT and DROP matters in practice. REJECT sends back an ICMP packet telling the client the port is unreachable. DROP simply ignores the packet without any response. Dropping is generally preferable because it gives an attacker no information about whether a port is filtered or whether nothing is listening at all. For certain protocols like SSH, REJECT is sometimes preferred to avoid long connection timeouts from clients that do not handle dropped packets gracefully.
UFW has a default deny policy for incoming traffic, which means anything not explicitly allowed is blocked. This is the correct starting point. You open only what you need, and everything else is silently dropped. The default allow policy for outgoing traffic is also appropriate for most servers. You rarely want to block outbound traffic from the server itself because it is needed for package updates, DNS resolution, and communication with backend services.
Before making any changes, verify that no firewall rules are currently active and that you have console access to the server in case you accidentally lock yourself out:
sudo ufw status verbose
If the status shows inactive, you are starting with a clean slate. If you are configuring UFW remotely over SSH, the critical first step is to make sure SSH is explicitly allowed before you enable the firewall. Enabling UFW with the default incoming policy of deny while SSH is not explicitly allowed will lock you out of the server immediately. This is one of the most common mistakes when setting up a firewall on a remote server.
The Minimum Ports to Open for a Web Server
The ports you need open depend entirely on what the server is doing. A standard web server that serves only HTTPS traffic needs fewer open ports than most people expect.
For a server running Nginx or Apache serving HTTPS traffic only, the minimum list is short:
- 22/tcp: SSH. Always needed for remote administration. This is the one port you cannot afford to lose access through.
- 443/tcp: HTTPS. If your application serves HTTPS traffic, port 443 must be open.
That is it for a basic HTTPS-only setup. If you are not serving HTTP, you do not need port 80. If you are redirecting all HTTP to HTTPS at the application or Nginx level, the redirect works even if port 80 is closed. The client receives the redirect response from port 443, which they are already connecting to, so the redirect mechanism does not depend on port 80 being accessible.
If you need port 80 open as well, for example to handle HTTP-to-HTTPS redirects at the Nginx level or to serve a mixed HTTP and HTTPS site, open the ports in this order:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
Notice that SSH comes first. Port 22 is your lifeline to the server. Make sure you are not using the default root login or a password-only SSH configuration. Key-based authentication is a minimum requirement for any production server. For more details on securing SSH access, see the guide on securing SSH on Ubuntu.
Configuring UFW for IPv6
UFW handles both IPv4 and IPv6 by default. If your server has an IPv6 address and UFW is configured for IPv4 only, you may inadvertently leave IPv6 traffic unfiltered. This is a common oversight because many administrators test only IPv4 connectivity and forget that the server may also be reachable via its IPv6 address.
Verify that UFW is configured for both protocols by checking the default UFW configuration file:
sudo nano /etc/default/ufw
Ensure IPV6=yes is set. After making any configuration change, reload UFW to apply the updated rules:
sudo ufw reload
Rate Limiting SSH to Prevent Brute Force
Port 22 being open to the internet means SSH brute force attacks will happen constantly. Every public server receives thousands of automated login attempts per day from bots scanning the internet for vulnerable SSH services. UFW has a built-in rate limit rule specifically for SSH that blocks an IP address after six failed connection attempts within 30 seconds:
sudo ufw limit 22/tcp
This is better than simply allowing SSH from a fixed IP if you need to access the server from multiple locations or from IP addresses that change frequently. The rate limit blocks the automated scanners while allowing legitimate multiple connection attempts from the same IP within a reasonable window. Your connections from coffee shops, co-working spaces, or different offices will not be blocked unless the same IP makes six failed attempts in 30 seconds.
If your IP address is fixed and you only ever connect from one location, you can restrict SSH to your specific IP address:
sudo ufw allow from 192.168.1.100 to any port 22
Replace 192.168.1.100 with your actual fixed IP address. This blocks all SSH access from any other IP and eliminates brute force attacks entirely. The downside is you cannot connect from any other location, which can be problematic if your ISP changes your IP address or you need to connect while traveling.
For a production server with a static IP, restricting SSH to your specific address is worth doing. For servers accessed from varying locations, rate limiting provides a practical balance between security and accessibility. Complementing UFW rate limiting with tools like Fail2Ban adds another layer of protection against repeated login attempts, as covered in the guide on setting up Fail2Ban on Ubuntu.
Application Profiles in UFW
UFW includes predefined application profiles for common services. These are stored in /etc/ufw/applications.d/ and include profiles for Nginx Full, Nginx HTTP, Nginx HTTPS, Apache, and OpenSSH. Using the application name rather than the port number is slightly safer because the profile specifies both the port and the protocol correctly:
sudo ufw allow 'Nginx Full'
sudo ufw allow 'OpenSSH'
The Nginx Full profile opens both port 80 and port 443. Nginx HTTP opens only port 80. Nginx HTTPS opens only port 443. Apache Full does the same. Check what a profile contains before applying it to understand exactly which rules will be added:
sudo ufw app info 'Nginx Full'
Application profiles make it easier to configure the correct rules without remembering port numbers and protocols. They also reduce the chance of typos that could accidentally open the wrong port.
WebSocket Support Through the Firewall
If your application uses WebSockets, the upgrade from HTTP to WebSocket happens over the same port 80 or 443 that HTTP uses. The connection is established on the same port, so no additional firewall rules are needed for basic WebSocket support. The only requirement is that your reverse proxy or application server is configured to handle WebSocket connections, which is a configuration matter separate from the firewall.
Database Ports and When Not to Open Them
A common mistake on servers running application code and a database on the same machine is opening the database port to the world. MySQL listens on port 3306 by default. PostgreSQL on port 5432. Redis on port 6379. MongoDB on port 27017. If any of these are open to 0.0.0.0/0, and the database has a weak or default configuration, you are handing an attacker direct database access.
Databases should only accept connections from the application server itself, never from the internet. For a single-server setup where the database and application are on the same machine, there is no reason for the database port to be accessible from outside the server at all. UFW's default incoming deny policy handles this automatically without you needing to add any rules.
If your database needs to accept connections from a remote application server, use UFW to allow access only from the specific IP address of that application server:
sudo ufw allow from 10.0.0.5 to any port 3306
Never open database ports to the world. Additionally, configure the database itself to bind to localhost only, not 0.0.0.0, so it is not even listening on the external interface regardless of what the firewall allows. This defence in depth means the database is protected even if the firewall rule is accidentally removed.
The Correct Order of Operations
The order in which UFW rules are applied matters. UFW evaluates rules top to bottom and uses the first match. More specific rules should come before general ones. When you enable UFW, the default incoming policy of deny is applied first, and then your specific allow rules are evaluated in order.
For a web server, the correct setup sequence is:
- Allow SSH:
sudo ufw allow 22/tcporsudo ufw limit 22/tcpor restrict to your specific IP address. - Allow web traffic:
sudo ufw allow 443/tcpandsudo ufw allow 80/tcpif needed. - Enable the firewall:
sudo ufw enable. Confirm withywhen prompted. - Verify the rules:
sudo ufw status verbose
Step 3 must only happen after steps 1 and 2. If you enable UFW before allowing SSH, you lock yourself out of the server. The ufw enable command will warn you if SSH is not explicitly allowed, but the warning is easy to override with y if you are in a hurry, which is exactly how people lock themselves out. Always verify SSH access works after enabling the firewall before ending your current SSH session.
Deleting Rules You No Longer Need
As your server's role changes over time, you will need to remove rules that are no longer applicable. List the current rules with their numbered positions:
sudo ufw status numbered
You will see output like:
[ 1] 22/tcp ALLOW IN Anywhere
[ 2] 443/tcp ALLOW IN Anywhere
[ 3] 22/tcp (v6) ALLOW IN Anywhere (v6)
[ 4] 443/tcp (v6) ALLOW IN Anywhere (v6)
Delete a rule by its number:
sudo ufw delete 2
Using the numbered reference is safer than trying to specify the rule text when deleting, because the numbered reference is unambiguous. When you specify a rule by text, small differences in syntax can cause the deletion to fail silently.
Resetting UFW to Start Fresh
If you have made mistakes with your UFW configuration and want to start over, reset everything to defaults:
sudo ufw reset
This disables UFW and removes all rules. After a reset, you are back to the default state of inactive with no custom rules, and you can start the configuration process from the beginning. This is useful when inheriting a server with an unknown or complicated firewall configuration that is difficult to untangle.
What About Ping and ICMP?
UFW's default configuration handles ICMP, which is the protocol used for ping, in a specific way. By default, UFW does not block ICMP packets entirely, but it limits ICMP rate to prevent ping floods. This is the correct configuration for most servers.
Completely blocking ICMP prevents legitimate network diagnostics from working and can cause issues with path MTU discovery, which is how routers determine the maximum packet size that can traverse a particular network path without fragmentation. Leave ICMP handling at the default UFW settings unless you have a specific reason to change it and understand the implications.
Logging: What UFW Logs and How to Read It
UFW logs to /var/log/ufw.log by default. The log fills up quickly on a public server with constant port scan and SSH brute force activity. Reviewing it occasionally is useful to understand what traffic is being blocked, particularly when debugging connectivity issues:
sudo tail -f /var/log/ufw.log
If you see blocked connections to ports you expect to be open, check whether the rule was actually applied with ufw status verbose. Most connectivity issues after setting up UFW are simply a case of the expected port not being in the allowed list.
Firewall Setup as Part of a Broader Security Strategy
A properly configured firewall is a critical component of server security, but it is not the only component. A firewall controls which traffic can reach your server, but it does not protect against compromised credentials, vulnerable application code, or misconfigured services.
For a comprehensive approach to securing an Ubuntu server, consider the steps covered in the Ubuntu 22.04 security hardening guide and the related guide on hardening Ubuntu server after install. These cover server setup, user permissions, software updates, and additional hardening steps that complement your firewall configuration.
Firewall rules should be reviewed periodically as your server's purpose changes. A rule that was appropriate when you first set up the server may no longer be necessary, and open ports that are no longer in use should be closed. Regular review keeps your attack surface minimal.