NGINX comes in two editions: NGINX Open Source, the free BSD-licensed web server and reverse proxy from nginx.org, and NGINX Plus, a commercial build from F5 that adds load balancing, monitoring and API features on top of the same core. This guide compares the two with configuration you can read side by side, shows how to cover the most common gaps in the open source edition, and ends with a practical recommendation.

The open source examples were checked on Ubuntu 24.04 LTS. The NGINX Plus examples require a Plus subscription or trial and will not load in open source NGINX.

Prerequisites

To try the open source examples you need:

  • A server running Ubuntu 24.04 LTS, for example a CubePath VPS.
  • A non-root user with sudo privileges.
  • One or more backend applications to proxy to. The examples use 10.0.0.11:8080 and 10.0.0.12:8080; replace them with your own addresses.

Install NGINX from the Ubuntu repositories:

sudo apt update
sudo apt install nginx
nginx -v
nginx version: nginx/1.24.0 (Ubuntu)

Ubuntu 24.04 ships the 1.24 branch. Some features mentioned below (HTTP/3, the resolve upstream parameter) need a newer release, available from the official nginx.org repository.

What both editions share

NGINX Plus is not a different server. It is built from the open source code base plus closed modules, so these capabilities are identical in both:

  • The event-driven architecture and raw performance per worker.
  • Reverse proxying for HTTP, gRPC, WebSocket, and TCP/UDP through the stream module.
  • TLS termination, HTTP/2, caching, gzip, rate limiting (limit_req) and connection limiting (limit_conn).
  • Load balancing methods: round robin, weight, least_conn, ip_hash, generic hash and random.
  • Passive health checks through max_fails and fail_timeout.
  • The configuration language itself. A working open source configuration runs unchanged on NGINX Plus.

Feature comparison

FeatureNGINX Open SourceNGINX Plus
Reverse proxy, TLS, HTTP/2, cachingYesYes
HTTP/3 (QUIC)Yes, from 1.25.0Yes
Round robin, least_conn, ip_hash, hash, randomYesYes
least_time load balancingNoYes
Passive health checks (max_fails)YesYes
Active health checks (health_check)NoYes
Cookie-based session persistence (sticky)NoYes
Basic metrics (stub_status)YesYes
Detailed metrics, REST API and dashboardNoYes
Add or remove upstream servers without reloadNoYes, through the API
Native JWT validation (auth_jwt)NoYes
Key-value store (keyval)NoYes
Commercial support from F5NoYes

The following sections show what the most important rows look like in practice.

Health checks

Open source NGINX only marks a backend as down after real client requests fail against it. With the configuration below, three failed requests within 30 seconds take a server out of rotation for 30 seconds, and the backup server only receives traffic when both primaries are unavailable.

Create a site file:

sudo nano /etc/nginx/conf.d/app.conf
upstream app_backend {
    server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.12:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.13:8080 backup;
}

server {
    listen 80;
    server_name your_domain;

    location / {
        proxy_pass http://app_backend;
        proxy_next_upstream error timeout http_502 http_503;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

proxy_next_upstream retries the request on the next server, so most users never see the failure. The downside is that some requests still hit a broken server first, and a server that answers 200 with an error page is never detected.

NGINX Plus adds active checks: NGINX itself polls each server on an interval and can inspect the response. The upstream needs a shared memory zone, and the check is declared in the location that proxies to it:

upstream app_backend {
    zone app_backend 64k;
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
}

match app_ok {
    status 200;
    header Content-Type ~ "application/json";
}

server {
    listen 80;
    server_name your_domain;

    location / {
        proxy_pass http://app_backend;
        health_check uri=/health interval=5s fails=3 passes=2 match=app_ok;
    }
}

A server is removed after three failed probes and returns after two successful ones, before any user request reaches it. If you need active checks without paying for Plus, HAProxy and Envoy both include them in their free editions.

Monitoring and the live API

Open source NGINX exposes a handful of global counters through stub_status. Add a status endpoint that only listens on localhost:

sudo nano /etc/nginx/conf.d/status.conf
server {
    listen 127.0.0.1:8081;

    location = /nginx_status {
        stub_status;
    }
}

Test the configuration, reload and query it:

sudo nginx -t
sudo systemctl reload nginx
curl http://127.0.0.1:8081/nginx_status
Active connections: 1
server accepts handled requests
 12 12 15
Reading: 0 Writing: 1 Waiting: 0

That is all you get: no per-upstream, per-server or per-status-code numbers. To graph these values, scrape them with the official nginx-prometheus-exporter, and derive per-upstream data from access logs (for example by logging $upstream_addr and $upstream_response_time).

NGINX Plus replaces this with the api module, which returns JSON for every server zone, upstream peer, cache and health check, and serves a built-in dashboard:

server {
    listen 127.0.0.1:8080;

    location /api {
        api write=on;
    }

    location = /dashboard.html {
        root /usr/share/nginx/html;
    }
}

With write=on, the same API changes the running configuration. For example, adding a server to the app_backend upstream shown earlier (which must have a zone) takes effect immediately, without a reload:

curl -X POST -d '{"server":"10.0.0.14:8080"}' http://127.0.0.1:8080/api/9/http/upstreams/app_backend/servers

The number in the path is the API version; check the NGINX Plus documentation for the version your release supports. Always restrict this endpoint to localhost or a management network.

Session persistence

Open source NGINX can keep a client on the same backend by hashing an attribute of the request. Hashing the client address is the simplest option:

upstream app_backend {
    hash $remote_addr consistent;
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
}

This breaks down when many users share one IP address (offices, mobile carriers) or when a user's address changes. You can hash an application cookie instead, for example hash $cookie_sessionid consistent;, but the first request without the cookie can still land on any server.

NGINX Plus sets its own cookie on the first response and routes on it afterwards:

upstream app_backend {
    zone app_backend 64k;
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    sticky cookie srv_id expires=1h domain=.your_domain path=/;
}

Plus also offers sticky route and sticky learn, which reuse a session identifier your application already issues.

JWT authentication

NGINX Plus validates JSON Web Tokens itself, so a backend only receives requests that carry a valid, unexpired token:

location /api/ {
    auth_jwt "api";
    auth_jwt_key_file /etc/nginx/jwks.json;
    proxy_pass http://app_backend;
}

By default the token is read from the Authorization: Bearer header. Instead of a local key file, auth_jwt_key_request can fetch the keys from your identity provider's JWKS URL through an internal location.

Open source NGINX has no JWT module. The standard workaround is auth_request, which sends a subrequest to a small authentication service and allows the request only if that service returns 2xx:

location /api/ {
    auth_request /_auth;
    proxy_pass http://app_backend;
}

location = /_auth {
    internal;
    proxy_pass http://127.0.0.1:9000/verify;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header Authorization $http_authorization;
}

This works with any token format, at the cost of running and maintaining the verification service.

Load balancing methods

Both editions share the classic methods. Only one line changes between them:

upstream app_backend {
    least_conn;
    server 10.0.0.11:8080 weight=2;
    server 10.0.0.12:8080;
}

NGINX Plus adds least_time, which prefers the server with the lowest average response time combined with the fewest active connections:

upstream app_backend {
    zone app_backend 64k;
    least_time header;
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
}

least_time header measures time to the first byte of the response; least_time last_byte measures time to the full response. For backends with similar hardware, least_conn in open source gets close to the same result.

Licensing and installation

NGINX Open Source is free under the 2-clause BSD license. Install it from your distribution, or from the nginx.org repository if you need the latest stable or mainline release.

NGINX Plus is sold as an annual subscription per instance, priced by quote from F5, with a free 30-day trial. Installation differs from the open source packages:

  • You download a certificate and key (nginx-repo.crt and nginx-repo.key) from the MyF5 customer portal and place them in /etc/ssl/nginx/. The package repository at pkgs.nginx.com authenticates you with them.
  • Current releases also require a license file (license.jwt) in /etc/nginx/ and report usage back to F5 periodically.
  • The package is called nginx-plus and replaces the nginx package, so the two cannot be installed side by side on the same server.

Follow F5's installation documentation for the exact repository lines for your distribution, since they change between releases.

Which one should you choose

Choose NGINX Open Source when:

  • You run a web server, reverse proxy or TLS terminator for one or a few applications.
  • Passive health checks with proxy_next_upstream are enough, or a separate load balancer handles health checks.
  • You already monitor through logs and Prometheus, and reloads to change upstreams are acceptable (a reload is graceful and does not drop connections).

Choose NGINX Plus when:

  • Backends change often (autoscaling, container platforms) and you want to update upstreams through an API without reloads.
  • You need active health checks, cookie-based session persistence or JWT validation and prefer to keep everything in NGINX.
  • Your organization requires a vendor support contract for the edge proxy.

If you only miss one or two Plus features, compare the cost with alternatives first: HAProxy and Envoy include active health checks, detailed statistics and runtime APIs at no cost.

Conclusion

NGINX Open Source and NGINX Plus share the same core and configuration language, so the choice comes down to operational features: active health checks, the live API and dashboard, sticky cookies and JWT validation. Start with the open source edition, measure where it falls short, and only then decide whether Plus or another proxy is the better fit. Good next steps are adding TLS with Let's Encrypt to your NGINX site, exporting stub_status metrics to Prometheus, or trying HAProxy with SSL termination if you need active health checks for free.