Create a Post
cancel
Showing results for 
Search instead for 
Did you mean: 
Shay_Levin
Admin
Admin

URI Rewrite - Complete Implementation Reference

What is URI Rewriting and Why Does it Matter?

A URI rewrite is the act of changing the request path — silently, server-side — before it reaches the upstream application. The client sends one URL; the backend receives a different one. No redirect is issued; the client is unaware.

Why it is needed

Real-world deployments rarely have a clean 1:1 mapping between the public URL structure and the backend's internal URL structure. URI rewriting bridges that gap without requiring changes to either the client or the backend.

Common use cases

Use case Example
Expose a sub-path publicly, serve from root Public: /chatbot/* → Backend: /*
Version translation Public: /api/v1/* → Backend: /v1/* (strip the /api prefix a gateway added)
Legacy URL migration Old: /old-endpoint → New: /new-endpoint (keep old clients working)
Path normalization Add a prefix the backend expects: /public/*  /static/public/*
Segment reordering REST to internal: /users/42/profile  /profile/42
Multi-service routing One WAF asset routes /payments/*, /orders/*, /catalog/* to separate upstreams by rewriting paths
WAF in front of a legacy app The app was built with a hard-coded path structure that can't be changed; the WAF adapts incoming paths to match it

URI rewrite vs redirect — key distinction

A rewrite is invisible to the client (internal, server-side). A redirect tells the client to issue a new request to a different URL (HTTP 301/302, client-visible). For reverse proxy scenarios, you almost always want a rewrite — the client should not know or care about the backend's internal path structure.


Table of Contents

  1. Architecture Overview
  2. How CloudGuard WAF Manages NGINX
  3. The Two Injection Points
  4. What URI Rewrite Means in This Context
  5. Critical Limitation: $request_uri vs $uri
  6. The Workaround: Per-Path Location Blocks with Literal proxy_pass
  7. Location Block File Reference
  8. The rewrite Directive In Depth
  9. NGINX Variables Available Inside the Location Block
  10. Setting the Host Header
  11. Rewrite Pattern Catalogue
  12. Generated NGINX Artifacts
  13. Deployment Procedure (Infinity Portal)
  14. Validation and Testing
  15. Logging Implications
  16. Troubleshooting Guide

1. Architecture Overview

CloudGuard WAF operates as an L7 reverse proxy in front of upstream application servers. All client traffic is routed through the WAF; the WAF inspects, optionally transforms, and forwards requests to the upstream.

  Client (browser / curl / API)
          │
          │  HTTP/HTTPS  (public DNS, e.g. jwt.cpwaf.net)
          ▼
  ┌───────────────────────────────────────────────────────┐
  │           CloudGuard WAF — AppSec Gateway             │
  │                                                       │
  │  nginx (WAF-managed)                                  │
  │  ┌─────────────────────────────────────────────────┐  │
  │  │  server block: jwt.cpwaf.net:80/443           │  │
  │  │  location / {                                   │  │
  │  │    [Additional Location Block injected here]    │  │
  │  │    proxy_pass $upstream_endpoint$request_uri;   │  │ ← generated
  │  │  }                                              │  │
  │  └─────────────────────────────────────────────────┘  │
  └───────────────────────────────────────────────────────┘
          │
          │  HTTP  (internal / private network)
          │  Host: <as configured>
          ▼
  ┌─────────────────────────────┐
  │  Upstream Backend           │
  │  e.g. 51.4.115.60:8080         │
  └─────────────────────────────┘

Key facts:

  • The WAF terminates the client connection and opens a new connection to the upstream.
  • URI rewriting happens inside the WAF, before the upstream sees the request.
  • The client never observes the rewrite (no HTTP redirect is issued).
  • The upstream receives the rewritten URI, not the original client URI.

2. How CloudGuard WAF Manages NGINX

CloudGuard WAF runs nginx as its proxy engine. Unlike self-managed nginx deployments, the admin cannot directly edit nginx.conf or any generated server block file. The WAF management plane (Infinity Portal) owns these files and regenerates them on every policy enforce.

What is auto-generated and what is user-controlled

File / Component Who controls it Editable by admin?
/etc/cp/conf/rpmanager/nginx.conf CloudGuard management No
/etc/cp/conf/rpmanager/servers/80_jwt.cpwaf.net.conf CloudGuard management No — overwritten on enforce
/etc/cp/conf/rpmanager/include/waf_N_additional_location_config.conf User (via Portal upload) Yes — this is the injection point
/etc/nginx/modules/*.conf CloudGuard management No
/etc/cp/conf/rpmanager/error-pages/ CloudGuard management No

Important: Even if you manually edit a generated server conf on the gateway host, those changes are overwritten the next time you enforce a policy from the Infinity Portal. The only durable injection point is the Additional Location Block / Additional Server Block upload.

Generated server block structure

For a WAF asset listening on port 80 for jwt.cpwaf.net proxying to 51.4.115.60:8080, CloudGuard generates a file at:

/etc/cp/conf/rpmanager/servers/80_jwt.cpwaf.net.conf

The relevant excerpt looks like this:

server {
    listen 80;
    server_name jwt.cpwaf.net;

    proxy_pass_request_headers on;
    proxy_pass_request_body on;

    # ONLY present because "Host: cpfakedomain.com" was added under
    # Advanced Proxy Settings -> Custom Headers in the Portal.
    # Without that setting this line does NOT appear in the generated config.
    proxy_set_header Host cpfakedomain.com;

    resolver 168.63.129.16 ipv6=off;

    location / {
        include /etc/cp/conf/rpmanager/include/waf_5_additional_location_config.conf;

        set $upstream_endpoint http://51.4.115.60:8080;
        proxy_pass $upstream_endpoint$request_uri;   # ← see Section 5

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
    }
}

The include file (waf_N_additional_location_config.conf) is where your uploaded Additional Location Block content lands. The N is an internal counter assigned by the WAF management plane — it is not predictable in advance and may change across policy enforces.

To find which include file belongs to your asset at any point in time:

sudo grep "include" /etc/cp/conf/rpmanager/servers/80_jwt.cpwaf.net.conf

3. The Two Injection Points

Navigate to: Infinity Portal → Policy → [Your Asset] → Advanced Proxy Settings

3.1 Additional Location Block Instructions

Content is injected inside the location / block, before the proxy_pass directive.

Allowed directives:

  • rewrite
  • set
  • add_header
  • proxy_set_header
  • sub-location blocks (e.g. location ~ ^/path { ... }) — with caveats, see Section 6
  • Most per-request nginx directives

NOT allowed (will cause nginx config test failure):

  • server { } — server-level block; belongs in Server Block
  • http { } — global block; not injectable
  • upstream { } — not injectable; use the Portal's Reverse Proxy settings
  • Bare proxy_pass without a specific location wrapper — partially allowed but ineffective; see Section 5

3.2 Additional Server Block Instructions

Content is injected inside the server { } block, outside of any location.

Use this for:

  • proxy_set_header directives you want to apply to all locations
  • map blocks (though map is technically an http-level directive in standard nginx — behavior may vary)
  • Custom error pages
  • Access log configuration

3.3 Custom Headers (Proxy Settings)

The Portal also provides a Custom Headers table (under Advanced Proxy Settings → Add custom headers). Headers added here are rendered as proxy_set_header directives at the server block level, so they apply to every proxied request for that asset.

This is the recommended way to set the upstream Host header (see Section 10). Using Custom Headers is officially supported; manually writing proxy_set_header inside the Additional Location Block is a workaround.


4. What URI Rewrite Means in This Context

There are four distinct operations that are often conflated. Understanding the difference is essential before writing any rewrite rule.

Operation What happens Client sees redirect? Upstream sees rewritten path? Where to configure
Internal URI rewrite WAF changes $uri before forwarding No Yes Additional Location Block (rewrite ... break)
Client redirect WAF sends HTTP 301/302 to client Yes No (client re-requests) rewrite ... redirect or return 301
Upstream path via proxy_pass URL proxy_pass appends a fixed path prefix No Yes (prefix substitution only) Portal Reverse Proxy URL field
Server block routing Different location blocks handle different paths No Depends on location Additional Location Block (nested location)

For CloudGuard WAF reverse proxy URI rewriting, you almost always want internal URI rewrite — the client sends /old-path, the WAF forwards /new-path, the client is unaware.


5. Critical Limitation: $request_uri vs $uri

This is the most important technical detail when implementing rewrites in CloudGuard WAF, and the source of the most common failure mode.

How nginx handles URI variables

Variable What it contains Modified by rewrite?
$uri Current normalized URI (path only, no query string) Yes — updated each time a rewrite fires
$request_uri Original URI as sent by client (path + query string) No — read-only, never changes
$args Query string (without ?) No — but preserved by break flag
$is_args ? if query string exists, empty otherwise No

The problem with CloudGuard's generated proxy_pass

CloudGuard WAF generates the following inside the location / block:

set $upstream_endpoint http://51.4.115.60:8080;
proxy_pass $upstream_endpoint$request_uri;

Because proxy_pass uses $request_uri, it always forwards the original client URI — regardless of any rewrite directive that fires before it.

Example:

Client request:   GET /chatbot/items HTTP/1.1

rewrite fires:    $uri         = /items   ← updated ✓
                  $request_uri = /chatbot/items  ← unchanged ✗

proxy_pass sends: GET /chatbot/items   ← rewrite had zero effect

This means: bare rewrite directives placed in the Additional Location Block have no effect on what the upstream receives when proxy_pass uses a variable like $upstream_endpoint$request_uri.

This is not a bug in nginx — it is the documented behavior when proxy_pass references $request_uri explicitly. It becomes a limitation in CloudGuard WAF because the admin cannot change the generated proxy_pass line.

Why $uri$is_args$args would fix it

If CloudGuard generated:

proxy_pass $upstream_endpoint$uri$is_args$args;

Then bare rewrite ... break rules in the Additional Location Block would work as expected, because $uri is rewrite-aware. This is the correct approach for a rewrite-capable reverse proxy, but it is not what CloudGuard currently generates.


6. The Workaround: Per-Path Location Blocks with Literal proxy_pass

Because bare rewrite rules are ineffective (see Section 5), the working approach is to define sub-location blocks inside the Additional Location Block file. Each sub-location block:

  1. Matches a specific path pattern
  2. Applies the rewrite
  3. Uses a literal proxy_pass URL (not a variable) pointing directly to the upstream

When proxy_pass is specified as a literal URL (e.g. proxy_pass http://51.4.115.60:8080;) and a rewrite ... break fires in the same location, nginx uses the rewritten $uri — not $request_uri — as the upstream path. This is documented nginx behavior for literal proxy_pass + rewrite break.

location ~ ^/chatbot {
    rewrite ^/chatbot/?(.*)$ /$1 break;
    proxy_pass http://51.4.115.60:8080;    # literal URL → respects $uri after rewrite
}

How it works

Client request:   GET /chatbot/items HTTP/1.1

nginx matches:    location ~ ^/chatbot  ✓
rewrite fires:    $uri = /items
proxy_pass:       literal URL → nginx appends $uri
upstream receives: GET /items HTTP/1.1  ✓

Why this works but proxy_pass $var$request_uri does not

proxy_pass form URI appended to upstream Rewrite-aware?
proxy_pass http://backend; (literal, no path) Rewritten $uri + $is_args$args Yes
proxy_pass http://backend/; (literal, with trailing slash) Replaces location prefix Yes (but path manipulation is prefix-based)
proxy_pass $variable; $request_uri (original, unmodified) No
proxy_pass $variable$request_uri; $request_uri No

Status: This is an inferred workaround based on nginx's documented behavior and confirmed through testing on a live CloudGuard WAF deployment. It is not explicitly documented in Check Point's official CloudGuard WAF documentation as of the writing of this guide.

Constraint: the upstream IP must be hardcoded

The sub-location proxy_pass must reference the upstream directly (e.g. proxy_pass http://51.4.115.60:8080;). You cannot reference the $upstream_endpoint variable that CloudGuard defines, because using a variable in proxy_pass reintroduces the $request_uri problem.

If your upstream IP changes, you must update the Additional Location Block file and re-enforce.


7. Location Block File Reference

File format

The Additional Location Block file is a plain text file containing nginx directives. There is no wrapping location block in the file itself — the content is injected directly inside the generated location /.

However, when using the sub-location workaround, you do include location { } blocks in the file. These become nested locations inside location /.

What to put in the file

Pattern A — Bare rewrite (only effective if you can modify proxy_pass, or for redirect use cases):

# This is INEFFECTIVE for upstream path rewriting in CloudGuard WAF
# because proxy_pass uses $request_uri
rewrite ^/old-path$ /new-path break;

Pattern B — Sub-location with literal proxy_pass (the working workaround):

location ~ ^/old-path {
    rewrite ^/old-path/?(.*)$ /new-path/$1 break;
    proxy_pass http://51.4.115.60:8080;
}

File constraints

  • No server { } blocks — nginx rejects these inside a location context
  • No http { } blocks — same reason
  • No upstream { } blocks — these are http-level; use the Portal's Reverse Proxy settings
  • No include directives — the file is already an include; recursive includes are not supported by the Portal upload mechanism
  • Avoid unicode characters in comments — some CloudGuard nginx builds reject non-ASCII characters in config files, causing nginx config test failure and HTTP 500 on all requests. Use only ASCII in comments.
  • Always use ASCII -> not  in comments for the same reason.

Verifying the file was applied

SSH to the gateway and run:

# Find which include file belongs to your asset
sudo grep "include" /etc/cp/conf/rpmanager/servers/80_jwt.cpwaf.net.conf

# Read the file
sudo cat /etc/cp/conf/rpmanager/include/waf_N_additional_location_config.conf

# Test nginx config validity
sudo nginx -t

If nginx -t fails, the enforce in the Portal will have produced a 500 error on all requests to that asset.


8. The rewrite Directive In Depth

Full syntax

rewrite regex replacement [flag];
Part Description
regex PCRE regular expression matched against $uri (path only, no query string)
replacement The new URI. Can reference capture groups via $1, $2, etc.
flag Controls what happens after the rewrite. See flags below.

Flags

Flag Behavior Use in CloudGuard WAF
break Stop processing rewrite rules. Proceed with the current location using the new $uri. When combined with a literal proxy_pass, forwards the rewritten $uri to upstream. Use this for upstream path rewriting.
last Stop processing rewrite rules. Re-initiate location matching with the new $uri. Can match a different location block. Use with caution — can cause loops. Not recommended for proxy rewrites.
redirect Issue HTTP 302 to the client with the replacement as the Location header. Use when you want the client to follow a new URL.
permanent Issue HTTP 301 to the client. Use for permanent client-visible redirects.
(none) Continue processing subsequent rewrite rules. The last matching rule applies. Useful for chaining, but the final proxy_pass will still use $request_uri.

Regex syntax (PCRE)

Construct Meaning Example
^ Anchor: start of string ^/api matches URIs starting with /api
$ Anchor: end of string /endpoint$ matches URIs ending with /endpoint
(.*) Capture group: zero or more of any character ^/chatbot/(.*)$ captures everything after /chatbot/
([^/]+) Capture group: one or more non-slash characters ^/users/([^/]+)/ captures a path segment
? Makes the preceding character optional /? matches optional trailing slash
$1, $2 Backreference to 1st, 2nd capture group /$1 inserts first captured group
\. Escaped dot (literal .) \.json$ matches URIs ending in .json
(foo|bar) Alternation ^/(foo|bar)/ matches /foo/ or /bar/

Query string behavior

The rewrite directive operates on $uri  the path only, without the query string. The query string ($args) is handled separately:

  • With break or last: the original query string is automatically appended to the rewritten URI when forwarded. GET /chatbot/search?foo=bar rewritten to /search arrives at the upstream as /search?foo=bar.
  • With redirect / permanent: the query string is appended to the redirect Location header.
  • To drop the query string in a rewrite: end the replacement with ?: rewrite ^/old$ /new? break;

Rewrite evaluation order

When multiple rewrite directives exist in the same context, nginx evaluates them in order. The first matching rule with a break or redirect flag stops processing. Without a flag, all rules are tested and the last match wins before proxy_pass.

rewrite ^/api/v1/(.*)$ /v1/$1 break;   # matches /api/v1/* → stops here
rewrite ^/api/(.*)$ /legacy/$1 break;   # never reached for /api/v1/* paths

9. NGINX Variables Available Inside the Location Block

These variables are available inside the Additional Location Block and behave as in standard nginx.

Variable Contains Notes
$uri Current request URI (path only, no ? or query string) Updated by rewrite. Use this, not $request_uri, if you need the post-rewrite path.
$request_uri Original URI as sent by client, including query string Read-only. Never modified. This is what CloudGuard's generated proxy_pass uses.
$args Query string without the leading ? E.g. foo=bar&x=1
$is_args ? if $args is non-empty, otherwise empty string Useful for constructing URIs: $uri$is_args$args
$host Host request header (or server name if absent) Value the client sent
$http_host Raw Host header including port E.g. jwt.cpwaf.net:8080
$remote_addr Client IP address  
$scheme Request scheme: http or https  
$server_name nginx server_name value for this virtual host  
$http_<name> Any request header, lowercased, hyphens replaced with underscores E.g. $http_x_forwarded_for, $http_authorization
$upstream_endpoint Set by CloudGuard to the configured upstream URL E.g. http://51.4.115.60:8080 — available but problematic in proxy_pass (see Section 5)

10. Setting the Host Header

The upstream backend may enforce a specific Host header and reject requests with the wrong value (HTTP 403 or 421). CloudGuard WAF by default forwards the original client Host header to the upstream, which is almost never the correct backend hostname.

Infinity Portal → Policy → Asset → Advanced Proxy Settings → Add custom headers

Name Value
Host cpfakedomain.com

This renders as proxy_set_header Host cpfakedomain.com; at the server block level in the generated nginx config, applying to all requests for this asset. This is the officially supported method.

The Portal UI displays: "You don't need to add the proxy_set_header header, as it will be automatically included in the configuration." This note refers to standard forwarded headers (X-Forwarded-For, etc.) — it is not saying you cannot add Host. Adding Host via Custom Headers is intentional and works correctly.

Alternative: proxy_set_header inside the Additional Location Block

If for any reason you need the Host header set only for specific sub-location blocks:

location ~ ^/api {
    rewrite ^/api/?(.*)$ /$1 break;
    proxy_pass http://51.4.115.60:8080;
    proxy_set_header Host cpfakedomain.com;
}

When using sub-location blocks (the workaround), server-level proxy_set_header directives are inherited by sub-locations that do not override them. So if you set Host via the Custom Headers UI, it applies inside your sub-location blocks as well — you do not need to repeat it.

Why this matters for the demo/test setup

If the upstream enforces Host validation and you forget to set it:

Client → WAF → Upstream
                ← 403 Forbidden (wrong Host: jwt.cpwaf.net, expected: cpfakedomain.com)

Without proxy_set_header Host, the upstream receives whatever the client sent — typically the WAF's public DNS name. Every request through the WAF will return 403, and it will look like a WAF problem when it is actually a Host header problem. Always verify the Host header first when debugging 403 errors on a new asset.


11. Rewrite Pattern Catalogue

All patterns below are for the Additional Location Block file using the sub-location workaround. Replace 51.4.115.60:8080 with your upstream address.

Pattern 1 — Simple prefix strip

Remove a path prefix before forwarding. Classic use case: a WAF asset is exposed at /chatbot but the upstream serves everything from /.

# /chatbot          -> /
# /chatbot/foo/bar  -> /foo/bar
location ~ ^/chatbot {
    rewrite ^/chatbot/?(.*)$ /$1 break;
    proxy_pass http://51.4.115.60:8080;
}

Regex explained:

  • ^/chatbot — URI starts with /chatbot
  • /? — optional trailing slash after chatbot
  • (.*) — capture everything after (may be empty)
  • /$1 — prepend / to the captured group (handles the bare /chatbot  / case)

Pattern 2 — Prefix replacement

Replace one path prefix with another. The client uses /api/v1/..., the upstream expects /v1/....

# /api/v1/items  -> /v1/items
# /api/v1/users  -> /v1/users
location ~ ^/api/v1 {
    rewrite ^/api(/v1/.*)$ $1 break;
    proxy_pass http://51.4.115.60:8080;
}

Regex explained:

  • ^/api — match starts at /api
  • (/v1/.*) — capture /v1/ and everything after it
  • $1 — the replacement IS the capture group (no extra prefix)

Pattern 3 — Path injection (add a prefix)

The client accesses /public/..., the upstream serves static assets from /static/public/....

# /public/logo.png       -> /static/public/logo.png
# /public/css/main.css   -> /static/public/css/main.css
location ~ ^/public {
    rewrite ^(/public/.*)$ /static$1 break;
    proxy_pass http://51.4.115.60:8080;
}

Regex explained:

  • ^(/public/.*) — capture the entire path starting from /public/
  • /static$1 — prepend /static to the full captured path

Pattern 4 — Full path swap

An exact-match rewrite. No wildcards; the entire path is replaced.

# /old-endpoint  -> /new-endpoint  (exact match only)
location = /old-endpoint {
    rewrite ^/old-endpoint$ /new-endpoint break;
    proxy_pass http://51.4.115.60:8080;
}

Using location = /old-endpoint (exact match) is more efficient than regex for single-path swaps.

Pattern 5 — Regex capture with segment reordering

Reorder path segments. The client sends /users/<id>/profile, the upstream expects /profile/<id>.

# /users/42/profile    -> /profile/42
# /users/alice/profile -> /profile/alice
location ~ ^/users/[^/]+/profile$ {
    rewrite ^/users/([^/]+)/profile$ /profile/$1 break;
    proxy_pass http://51.4.115.60:8080;
}

Regex explained:

  • ([^/]+) — capture one or more non-slash characters (the user ID)
  • /profile/$1 — place the captured ID after /profile/

Pattern 6 — Query string preservation

Query strings are preserved automatically with the break flag. No special handling is needed.

# /chatbot/search?foo=bar&x=1  -> /search?foo=bar&x=1
location ~ ^/chatbot {
    rewrite ^/chatbot/?(.*)$ /$1 break;
    proxy_pass http://51.4.115.60:8080;
}

To drop the query string intentionally, append ? to the replacement:

rewrite ^/chatbot/?(.*)$ /$1? break;

Pattern 7 — Multi-pattern combined file

A complete Additional Location Block file covering all the above cases:

# Pattern 1 - Simple prefix strip: /chatbot/* -> /*
location ~ ^/chatbot {
    rewrite ^/chatbot/?(.*)$ /$1 break;
    proxy_pass http://51.4.115.60:8080;
}

# Pattern 2 - Prefix replacement: /api/v1/* -> /v1/*
location ~ ^/api/v1 {
    rewrite ^/api(/v1/.*)$ $1 break;
    proxy_pass http://51.4.115.60:8080;
}

# Pattern 3 - Path injection: /public/* -> /static/public/*
location ~ ^/public {
    rewrite ^(/public/.*)$ /static$1 break;
    proxy_pass http://51.4.115.60:8080;
}

# Pattern 4 - Full path swap: /old-endpoint -> /new-endpoint
location = /old-endpoint {
    rewrite ^/old-endpoint$ /new-endpoint break;
    proxy_pass http://51.4.115.60:8080;
}

# Pattern 5 - Regex reorder: /users/<id>/profile -> /profile/<id>
location ~ ^/users/[^/]+/profile$ {
    rewrite ^/users/([^/]+)/profile$ /profile/$1 break;
    proxy_pass http://51.4.115.60:8080;
}

12. Generated NGINX Artifacts

File locations on the gateway host

/etc/cp/conf/rpmanager/
├── nginx.conf                          # Main nginx config (auto-generated, do not edit)
├── servers/
│   ├── 00_nginx_conf_include.conf      # Global includes (auto-generated)
│   ├── 80_jwt.cpwaf.net.conf         # Per-asset server block (auto-generated)
│   └── 127.0.0.1_5555.conf            # Internal health check (auto-generated)
└── include/
    ├── waf_0_additional_location_config.conf   # Injection point for asset 0
    ├── waf_1_additional_location_config.conf   # Injection point for asset 1
    ...
    └── waf_N_additional_location_config.conf   # Your file lands here

How CloudGuard assigns waf_N files to assets

CloudGuard assigns a sequential index N (0–9, then a–f in hex) to each WAF asset. The mapping between asset and N is managed internally by the management plane and is not guaranteed to be stable across portal re-enforcements or gateway restarts. Always verify the current mapping with:

sudo grep "include" /etc/cp/conf/rpmanager/servers/80_jwt.cpwaf.net.conf

What a full generated server block looks like (annotated)

Verified output from sudo cat /etc/cp/conf/rpmanager/servers/80_jwt.cpwaf.net.conf on a live CloudGuard WAF gateway:

# Auto-generated by CloudGuard WAF management. DO NOT EDIT MANUALLY.
# Changes are overwritten on next policy enforce.

# Used internally for rate-limit logging decisions
map $status $RateLimitLoggable {
    429 1;
    default 0;
}

server
{
    listen  80;
    server_name jwt.cpwaf.net;

    proxy_pass_request_headers on;
    proxy_pass_request_body on;

    # Generated ONLY because "Host: cpfakedomain.com" was added in
    # Portal -> Advanced Proxy Settings -> Custom Headers.
    # This line is absent by default if no custom Host header is configured.
    proxy_set_header Host cpfakedomain.com;

    access_log off;                                      # access logging disabled by default
    error_log /var/log/nginx/error.log warn;             # fixed path, not configurable here

    # SSL Client Certificate JavaScript Processing      # placeholder — unused unless mTLS configured

    resolver 168.63.129.16 ipv6=off;                    # Azure DNS resolver (deployment-specific)

    location /
    {
        # === YOUR ADDITIONAL LOCATION BLOCK CONTENT IS INJECTED HERE ===
        include   /etc/cp/conf/rpmanager/include/waf_5_additional_location_config.conf;

 #unused redirect;                                       # CloudGuard placeholder — not active
 #unused DNS server;

        # Generated upstream variable from Portal Reverse Proxy settings
        set $upstream_endpoint http://51.4.115.60:8080;

        # THIS uses $request_uri — see Section 5 for why rewrites require a workaround
        proxy_pass $upstream_endpoint$request_uri;

        proxy_set_header Host cpfakedomain.com;          # repeated here even if set at server level
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 #unused access_log;
 #unused access syslog;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
 #unused header;                                         # placeholders for optional headers
    }
}

Notable observations from the real output:

  • #unused comment lines are CloudGuard-generated placeholders for features that are configured elsewhere in the Portal (redirects, rate limits, custom headers, access logging). They are inert — nginx treats them as comments.
  • proxy_set_header Host appears twice: once at the server block level (from Custom Headers) and once inside location /. Both are generated by CloudGuard; the location-level one takes precedence for proxied requests.
  • access_log off is hardcoded. To enable nginx-level access logging you must add it via the Additional Server Block.
  • The Azure DNS resolver (168.63.129.16) is deployment-specific. On non-Azure gateways this will differ.

13. Deployment Procedure (Infinity Portal)

Step 1: Prepare the location block file

Create a plain text file (e.g. location-block.conf) with your rewrite sub-locations. Rules:

  • ASCII only in comments
  • No server, http, upstream blocks
  • Use literal proxy_pass http://51.4.115.60:8080; not variables
  • Test regex patterns locally with: echo "/your/path" | grep -P 'your_pattern'

Step 2: Set the upstream in the Portal

Policy → Asset → Reverse Proxy → WAF will reach the application at:

Set this to http://51.4.115.60:8080. This defines $upstream_endpoint in the generated config and is used for health checks.

Step 3: Set the Host header

Policy → Asset → Advanced Proxy Settings → Add custom headers

Name Value
Host <expected-hostname-by-backend>

Step 4: Upload the location block file

Policy → Asset → Advanced Proxy Settings → Additional location block instructions → Upload

Select your .conf file and click OK.

Step 5: Enforce the policy

Click Enforce (or Publish depending on your portal version). CloudGuard will:

  1. Write your file to /etc/cp/conf/rpmanager/include/waf_N_additional_location_config.conf
  2. Regenerate the server block
  3. Run nginx -t
  4. If the test passes: reload nginx (nginx -s reload)
  5. If the test fails: nginx is not reloaded; all requests to the asset return HTTP 500

Step 6: Verify (see Section 14)


14. Validation and Testing

Verify the file was written correctly

# Find your asset's include file
sudo grep "include" /etc/cp/conf/rpmanager/servers/80_jwt.cpwaf.net.conf

# Read it
sudo cat /etc/cp/conf/rpmanager/include/waf_N_additional_location_config.conf

# Confirm nginx is running cleanly
sudo nginx -t

Test each rewrite pattern with curl

The -v flag shows request/response headers. The backend JSON response echoes back path, host, and query_string for easy verification.

# Pattern 1 — prefix strip
curl -v http://jwt.cpwaf.net/chatbot
# Expected: backend receives GET /

curl -v http://jwt.cpwaf.net/chatbot/items
# Expected: backend receives GET /items

# Pattern 2 — prefix replacement
curl -v http://jwt.cpwaf.net/api/v1/items
# Expected: backend receives GET /v1/items

# Pattern 3 — path injection
curl -v http://jwt.cpwaf.net/public/logo.png
# Expected: backend receives GET /static/public/logo.png

# Pattern 4 — full path swap
curl -v http://jwt.cpwaf.net/old-endpoint
# Expected: backend receives GET /new-endpoint

# Pattern 5 — regex reorder
curl -v http://jwt.cpwaf.net/users/42/profile
# Expected: backend receives GET /profile/42

# Pattern 6 — query string preservation
curl -v "http://jwt.cpwaf.net/chatbot/search?foo=bar&x=1"
# Expected: backend receives GET /search?foo=bar&x=1

# Host header verification
curl -v http://jwt.cpwaf.net/
# Expected: backend JSON shows "host": "cpfakedomain.com"

# Host enforcement (direct to backend — should 403)
curl -v -H "Host: wronghost.example.com" http://51.4.115.60:8080/
# Expected: 403 from backend

# Host enforcement (direct to backend — should 200)
curl -v -H "Host: cpfakedomain.com" http://51.4.115.60:8080/
# Expected: 200 from backend

15. Logging Implications

Access log

By default, CloudGuard WAF's nginx has access logging disabled in generated server blocks (access_log off;). WAF-level access events are logged through the Check Point unified logging infrastructure (SmartConsole / Log & Monitor), not through nginx access logs.

To enable raw nginx access logging for debugging, add to the Additional Server Block:

access_log /var/log/nginx/access_debug.log;

This is a temporary debugging measure. Remove it before production use.

Error log

The error log is configured at the server block level by CloudGuard:

error_log /var/log/nginx/error.log warn;

When a location block file contains a syntax error, nginx fails its config test and does not reload. The error is visible in:

sudo nginx -t 2>&1

If nginx rejects the config, all requests to the affected asset return HTTP 500, because nginx is running the previous (last-good) config, which does not include your changes.

Tracking which requests hit your rewrite rules

Since access logging is off by default, the best way to observe what the backend receives is to instrument the backend to log the incoming request URI and Host header. The Flask backend in this project echoes both back as JSON, making it ideal for testing.

Alternatively, enable access logging on the Additional Server Block temporarily and inspect /var/log/nginx/access_debug.log on the gateway host:

sudo tail -f /var/log/nginx/access_debug.log

16. Troubleshooting Guide

HTTP 500 on all requests to the asset after enforce

Cause: nginx config test failed. Your location block file has a syntax error.

Diagnosis:

sudo nginx -t 2>&1

Common causes and fixes:

Error message Cause Fix
host not found in upstream "upstream" Used proxy_pass http://upstream; with a placeholder name Replace with the actual IP: proxy_pass http://51.4.115.60:8080;
unexpected "{" Used server { } or http { } block in the location block file Remove — these are not allowed
invalid number of arguments Syntax error in a directive Check directive spelling and argument count
pcre_compile() failed Invalid regex Test your regex: echo "/path" | grep -P 'your_regex'
Non-ASCII character error Unicode in comments (e.g. , ) Replace with ASCII equivalents (->, -)

Rewrite rules uploaded but paths are not being rewritten

Cause: Bare rewrite directives (without sub-location wrappers) have no effect because the generated proxy_pass $upstream_endpoint$request_uri uses $request_uri, which is never modified by rewrites.

Diagnosis:

# Check what the backend actually receives
curl -s http://<waf-hostname>/your-path | jq .path

If the backend still receives the original path, use sub-location blocks with a literal proxy_pass (see Section 6).

HTTP 403 on every request through the WAF

Cause: The upstream is enforcing a Host header check and the WAF is forwarding the client's Host header instead of the backend's expected hostname.

Diagnosis:

curl -s http://<waf-hostname>/ | jq .received_host

Fix: Add the correct Host header via Portal → Asset → Advanced Proxy Settings → Custom Headers: Host = <backend-expected-hostname>

HTTP 502 Bad Gateway

Cause: The WAF cannot connect to the upstream.

Checklist:

  1. Is the upstream running? curl http://51.4.115.60:8080/
  2. Is port 8080 open on the upstream firewall? sudo ufw status
  3. Is the upstream IP correct in the Portal?
  4. If using sub-location proxy_pass, is the hardcoded IP correct and reachable from the WAF?

Rewrite fires but query string is lost

Cause: Using redirect or permanent flags instead of break. These issue client-side redirects, and the redirect URL construction may drop query strings depending on nginx version.

Fix: Use break for all upstream proxy rewrites.

Trailing slash issues (e.g. /chatbot/ not matching but /chatbot does)

Cause: The regex does not account for an optional trailing slash.

Fix: Use /? in the regex:

rewrite ^/chatbot/?(.*)$ /$1 break;
#               ^^  optional trailing slash

Location block changes not visible after enforce

Cause: The Portal may have assigned a new waf_N index to your asset, and the server block now includes a different file.

Fix:

sudo grep "include" /etc/cp/conf/rpmanager/servers/80_jwt.cpwaf.net.conf
# Then read the file it points to
sudo cat /etc/cp/conf/rpmanager/include/waf_<N>_additional_location_config.conf

Always verify the actual included file rather than assuming the index is stable.

rewrite ... break vs rewrite ... last — choosing the wrong flag

Scenario Correct flag
Rewriting the path for upstream proxy break — stops rewriting, proxies with new $uri
Rewriting to match a different location block last — re-evaluates location matching
Issuing a browser redirect redirect or permanent

Using last in a proxy rewrite can cause infinite loops if the rewritten URI re-matches the same location, resulting in a 500.

(1)
0 Replies

Leaderboard

Epsum factorial non deposit quid pro quo hic escorol.

Upcoming Events

    Thu 07 May 2026 @ 01:30 PM (AEST)

    CheckMates Live Sydney

    Tue 02 Jun 2026 @ 09:00 AM (CEST)

    CheckMates Live Denmark - Aarhus

    Wed 03 Jun 2026 @ 09:00 AM (CEST)

    CheckMates Live Denmark - Copenhagen
    CheckMates Events