LEMP Field Notes
Security & TLS

UFW Firewall Setup for Nginx on Ubuntu: 5 Essential Rules

UFW Firewall Setup for Nginx on Ubuntu: 5 Essential Rules
In briefTo set up a UFW firewall for Nginx on Ubuntu, run five commands: sudo ufw default deny incoming, sudo ufw default allow outgoing, sudo ufw allow OpenSSH, sudo ufw allow 'Nginx Full', then sudo ufw enable. Allowing SSH before enabling prevents locking yourself out of a remote server. The 'Nginx Full' application profile opens ports 80 and 443, which HTTPS redirects and Let's Encrypt renewals both require. Verify the result with sudo ufw status verbose.

How to Set Up a UFW Firewall for Nginx on Ubuntu

UFW (Uncomplicated Firewall) is Ubuntu's built-in front end for the kernel's netfilter firewall, and it ships installed but disabled on Ubuntu Server. Securing an Nginx web server with UFW takes five rules: set default policies to deny incoming and allow outgoing traffic, allow SSH before enabling the firewall, allow web traffic with the Nginx Full application profile, rate-limit SSH with ufw limit, and keep every internal service — MySQL, PHP-FPM, Redis — off the public rule list entirely.

The short version looks like this:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

The rest of this guide explains what each rule does, why the order matters, and how to verify and manage the result. If you're starting from a bare server, our LEMP stack install guide covers getting Nginx running in the first place; this article assumes Nginx is installed and you're connected over SSH.

A public server gets probed by automated scanners within minutes of coming online. The firewall is not a finishing touch — it belongs in the same session as the install.

Before You Start: Check UFW's Status

First, confirm UFW is present and see its current state:

sudo ufw status verbose

On a fresh Ubuntu Server install you'll see Status: inactive. That means no UFW rules are being enforced yet — every port your services listen on is reachable from the internet.

If the command isn't found (some minimal images omit it), install it:

sudo apt update
sudo apt install ufw

Installing UFW does not enable it. Nothing changes until you run ufw enable, which is exactly the safety margin you want: you can stage all five rules first, then switch the firewall on knowing SSH is already allowed.

Rule 1: Set Default Policies (Deny In, Allow Out)

A firewall is only as good as its default posture. The standard policy for a web server is: refuse everything inbound unless explicitly allowed, and permit everything outbound so the server can fetch packages, renew certificates, and make DNS queries.

sudo ufw default deny incoming
sudo ufw default allow outgoing

These are actually UFW's defaults on Ubuntu, but set them explicitly anyway. It costs two commands, it makes your setup reproducible on servers where someone changed the defaults, and it documents your intent.

With deny incoming as the baseline, every later rule is an exception you consciously carve out. That's the right mental model: your public rule list should be a short, complete inventory of what the world is allowed to reach.

Rule 2: Allow SSH Before You Enable UFW

This is the step that locks people out of their own servers. If you enable UFW with a default-deny inbound policy and no SSH rule, your current session may survive, but the next connection attempt will be refused — and on a remote VPS with no console access, that's a genuine emergency.

So allow SSH first:

sudo ufw allow OpenSSH

OpenSSH is an application profile registered by the openssh-server package; it opens TCP port 22. If you've moved SSH to a non-standard port, allow that port explicitly instead:

sudo ufw allow 2222/tcp

Only after the SSH rule exists should you enable the firewall:

sudo ufw enable

UFW will warn that enabling may disrupt existing SSH connections — that warning is precisely why this rule comes before enable, not after. Confirm with y, then verify from a second terminal that you can still open a fresh SSH session before you close the one you're in. UFW also persists across reboots once enabled, so this is one-time setup, not something to script into cron.

Rule 3: Allow Web Traffic with 'Nginx Full'

On Ubuntu, the Nginx package registers UFW application profiles — named bundles of ports defined in /etc/ufw/applications.d/. List them:

sudo ufw app list

You should see three Nginx entries:

Profile Ports opened Use when
Nginx HTTP 80/tcp Serving plain HTTP only
Nginx HTTPS 443/tcp Serving HTTPS only (rare — you almost always need 80 too)
Nginx Full 80/tcp and 443/tcp Serving HTTPS with HTTP available for redirects and renewals

For any real site, use Nginx Full:

sudo ufw allow 'Nginx Full'

The quotes matter — the profile name contains a space. Why open port 80 even on an HTTPS-only site? Two reasons. First, your Nginx config should redirect HTTP requests to HTTPS, and that redirect can only happen if port 80 is reachable. Second, Certbot's default HTTP-01 challenge validates domain ownership over port 80, so Let's Encrypt certificate issuance and renewal depends on it staying open.

To see exactly what a profile opens before you trust it:

sudo ufw app info 'Nginx Full'

Prefer profiles over raw port numbers where they exist. Allow Nginx Full in your status output tells a future admin what the rule is for; 80,443/tcp makes them go look it up.

If you're hosting several sites on one server, the firewall rule doesn't change — ports 80 and 443 serve all of them, and Nginx server blocks handle routing each domain to the right site.

Rule 4: Rate-Limit SSH Against Brute Force

An open port 22 collects password-guessing attempts around the clock. UFW has a built-in mitigation: the limit rule, which denies a source IP that opens 6 or more connections within 30 seconds.

Replace the plain allow rule with a limit rule:

sudo ufw delete allow OpenSSH
sudo ufw limit OpenSSH

Legitimate use is unaffected — you'd have to reconnect several times in half a minute to trip it — but it blunts automated brute-force scripts considerably.

Two honest caveats. First, limit is a mitigation, not a substitute for key-based SSH authentication with passwords disabled; do both. Second, if you use tools that open many rapid SSH connections (some deployment scripts, rsync loops, parallel Ansible runs), you can trigger the limit yourself — in that case keep the plain allow rule for a trusted admin IP:

sudo ufw allow from 203.0.113.10 to any port 22 proto tcp

Do not apply limit to the Nginx ports. Web browsers legitimately open multiple connections in quick succession, and CDNs or proxies funnel many users through one IP — rate limiting for web traffic belongs in Nginx itself, not the packet filter.

Rule 5: Keep Internal Services Off the Public List

The most important rules on a LEMP server are the ones you don't add. MySQL (port 3306) and PHP-FPM should never appear in your UFW rule list, because nothing outside the server should reach them:

If you genuinely need remote database access — say, from a separate application server — allow it only from that server's specific IP:

sudo ufw allow from 203.0.113.20 to any port 3306 proto tcp

That still requires changing MySQL's bind-address, which is a deliberate architectural decision, not a default. For everyone else, the correct number of database rules is zero.

The test is simple: run sudo ufw status and read every line aloud. If you can't say what a rule is for and who needs it, delete it.

Verify and Manage Your Rules

After setup, confirm the full picture:

sudo ufw status verbose

A healthy Nginx web server firewall reads like this:

Status: active
Default: deny (incoming), allow (outgoing), disabled (routed)

To                         Action      From
--                         ------      ----
OpenSSH                    LIMIT       Anywhere
Nginx Full                 ALLOW       Anywhere
OpenSSH (v6)               LIMIT       Anywhere (v6)
Nginx Full (v6)            ALLOW       Anywhere (v6)

The (v6) entries are automatic — UFW mirrors rules for IPv6 when it's enabled in /etc/default/ufw, which it is by default.

Deleting a Rule

Use numbered mode to remove rules precisely:

sudo ufw status numbered
sudo ufw delete 2

UFW shows you the rule and asks for confirmation before deleting. Note that numbers shift after each deletion, so re-run status numbered between deletes rather than firing off several numbers in a row.

You can also delete by repeating the original rule with delete prefixed, as in sudo ufw delete allow 'Nginx Full' — useful in scripts where numbering is unreliable.

Resetting or Disabling

Two commands worth knowing and using carefully:

sudo ufw disable   # turns enforcement off, keeps your rules
sudo ufw reset     # disables AND wipes all rules back to defaults

If you reset, you're back to square one — re-add the SSH rule before enabling again.

Common Mistakes to Avoid

Five rules, two of which are "don't add rules you don't need" — that's genuinely all a standard Nginx server requires from UFW. The best firewall configuration is a short one you fully understand.

FAQ

What is the difference between Nginx Full, Nginx HTTP, and Nginx HTTPS in UFW?

They are UFW application profiles registered by the Nginx package on Ubuntu. Nginx HTTP opens port 80 only, Nginx HTTPS opens port 443 only, and Nginx Full opens both. For a real site you almost always want Nginx Full: port 80 stays open for HTTP-to-HTTPS redirects and for Certbot's HTTP-01 challenge, while port 443 serves the encrypted traffic. Inspect any profile with sudo ufw app info 'Nginx Full'.

Why do I need to allow SSH before enabling UFW?

UFW's recommended default policy denies all incoming connections. If you enable the firewall on a remote server without first allowing SSH, your next connection attempt will be refused, and on a VPS without console access that means you are locked out. Run sudo ufw allow OpenSSH (or allow your custom SSH port) before sudo ufw enable, then confirm from a second terminal that a fresh SSH session still works.

Should I open port 3306 for MySQL in UFW?

No, not on a standard single-server LEMP setup. MySQL on Ubuntu binds to 127.0.0.1 by default, and your PHP application connects over loopback, which the firewall does not restrict. Opening 3306 to the internet only exposes the database to brute-force attempts. If a separate application server genuinely needs access, allow only that server's IP with a specific rule and deliberately change MySQL's bind-address.

What does sudo ufw limit do, and should I use it for SSH?

The limit rule allows a connection but blocks a source IP that opens six or more connections within 30 seconds, which blunts automated SSH brute-force attempts. Applying it to SSH with sudo ufw limit OpenSSH is a sensible default for most servers. Do not apply it to web ports, since browsers and proxies legitimately open many rapid connections, and keep using key-based authentication — rate limiting is a mitigation, not a substitute.

How do I check which UFW rules are active?

Run sudo ufw status verbose to see whether the firewall is active, the default policies, and every rule including automatic IPv6 mirrors. Use sudo ufw status numbered when you want to delete a rule: it prefixes each rule with a number you can pass to sudo ufw delete. Rule numbers shift after each deletion, so re-run the numbered listing between deletes rather than removing several numbers in one pass.

Does UFW stay enabled after a reboot?

Yes. Once you run sudo ufw enable, UFW is configured to start at boot and reapply your rules automatically, so it is one-time setup rather than something to re-run. Your rules persist until you change them: sudo ufw disable turns enforcement off but keeps the rule set, while sudo ufw reset disables the firewall and wipes all rules back to defaults, meaning you must re-add SSH access before enabling it again.