Configure NGINX features with F5 WAF

This document shows examples of how to modify your NGINX configuration to enable F5 WAF for NGINX features.

It is intended as a reference for small, self-contained examples of how F5 WAF for NGINX can be configured.

Important constraints when F5 WAF for NGINX is enabled:

  • Subrequest-based modules (NGINX modules that create internal HTTP subrequests) are not inspected in any scope block where app_protect_enable on is set. F5 WAF for NGINX inspects only direct, client-facing HTTP requests.
  • Modules that require the HTTP Range header are not supported in the same configuration scope as app_protect_enable on. Place Range-dependent configuration in a server or location block without F5 WAF for NGINX enabled.

For additional information on configuring NGINX, you should view the NGINX documentation.

Subrequest-based modules

F5 WAF for NGINX inspects direct client-facing requests, but does not inspect internal subrequests generated by subrequest-based modules.

Examples of subrequest-based modules:

  • Slice
  • Mirror
  • Client authorization
  • njs

Slice module example

nginx
load_module modules/ngx_http_app_protect_module.so;

http {
    server {
        listen 127.0.0.1:8080;
        server_name localhost;

        location / {
            app_protect_enable on;
            proxy_pass        http://127.0.0.1:8081$request_uri;
        }
    }

    server {
        listen 127.0.0.1:8081;
        server_name localhost;

        location / {
            proxy_pass        http://1.2.3.4$request_uri;
            slice 2;
            proxy_set_header Range $slice_range;
        }
    }
}

Mirror module example

nginx
load_module modules/ngx_http_app_protect_module.so;

http {
    log_format test $uri;

    server {
        listen       127.0.0.1:8080;
        server_name  localhost;

        location / {
            app_protect_enable on;
            mirror /mirror;
        }

        location /mirror {
            log_subrequest on;
            access_log test$args.log test;
        }
    }
}

Client authorization module example

nginx
load_module modules/ngx_http_app_protect_module.so;

http {
    server {
        listen       127.0.0.1:8080;
        server_name  localhost;

        location / {
            auth_request /scan;
            proxy_pass        http://localhost:8888;
        }
        location /scan {
            proxy_pass        http://localhost:8081$request_uri;
       }
    }

    server {
        listen       127.0.0.1:8081;
        server_name  localhost;

        location /scan {
            app_protect_enable on;
            proxy_pass        http://localhost:8888;
        }
    }
}

njs module example

nginx
load_module modules/ngx_http_app_protect_module.so;
load_module modules/ngx_http_js_module.so;

http {
    js_include service.js

    server {
        listen       127.0.0.1:8080;
        server_name  localhost;

        location / {
            app_protect_enable on;
            proxy_pass        http://127.0.0.1:8081$request_uri;
        }
    }

    server {
        listen       127.0.0.1:8081;
        server_name  localhost;

        location / {
            js_content foo;
        }
    }
}

Enable WAF on an njs module using the subrequest mechanism

This configuration example shows how to enable WAF on an njs module that relies on the subrequest mechanism.

nginx
user nginx;
worker_processes  auto;

events {
    worker_connections  1024;
}

load_module modules/ngx_http_app_protect_module.so;
load_module modules/ngx_http_js_module.so;

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    js_import main from example.js;

    server {
        listen       80;
        server_name  localhost;
        proxy_http_version 1.1;
        app_protect_enable on;

        location / {
            proxy_pass        http://127.0.0.1:8080/foo/$request_uri;
        }
    }
    server {
        listen       127.0.0.1:8080;
        server_name  localhost;
        proxy_http_version 1.1;

        location /foo {
            js_content main.fetch_subrequest;
        }

        location / {
            internal;
            return 200  "Hello! I got your URI request - $request_uri\n";
        }
    }
}
js
async function fetch_subrequest(r) {
    let reply = await r.subrequest('/<script>');
    let response = {
        uri: reply.uri,
        code: reply.status,
        body: reply.responseText,
    };
    r.return(200, JSON.stringify(response));
}

export default {join};

If the njs handler triggers an internal subrequest to /<script>, it is not inspected by F5 WAF for NGINX and succeeds:

curl "localhost/"  
{"uri":"/<script>","code":200,"body":"Hello! I got your URI request - /foo//\n"}

However, if a direct, client-facing request attempts to trigger the same URL, it is inspected by F5 WAF for NGINX and is blocked according to the security policy.

curl "localhost/<script>"
text
<html><head><title>Request Rejected</title></head><body>The requested URL was rejected. Please consult with your administrator.

Your support ID is: 123456789

<a href='javascript:history.back();'>[Go Back]</a></body></html>

Range header–dependent modules

Features that add or depend on the HTTP Range header are unsupported in the same scope as app_protect_enable on. Place Range-dependent logic in a separate scope that does not enable F5 WAF for NGINX, and have the F5 WAF for NGINX enable frontend proxy to that backend.

Examples of Range-dependent features:

  • Static location
  • Range

Static location example

nginx
load_module modules/ngx_http_app_protect_module.so;

http {
    server {
        listen       127.0.0.1:8080;
        server_name  localhost;

        location / {
            app_protect_enable on;
            proxy_pass        http://127.0.0.1:8080/proxy/$request_uri;
        }

        location /proxy {
            default_type text/html;
            return 200 "Hello! I got your URI request - $request_uri\n";
        }
    }
}

Range example

nginx
load_module modules/ngx_http_app_protect_module.so;

http {

    server {
        listen       127.0.0.1:8080;
        server_name  localhost;

        location / {
            app_protect_enable on;
            proxy_pass         http://127.0.0.1:8081$request_uri;
        }
    }

    server {
        listen       127.0.0.1:8081;
        server_name  localhost;

        location / {
            proxy_pass         http://1.2.3.4$request_uri;
            proxy_force_ranges on;
        }
    }
}