- Products
- Learn
- Local User Groups
- Partners
- More
The State of Ransomware Q1 2026
Key Trends and Their Impact
Good, Better, Best:
Prioritizing Defenses Against Credential Abuse
AI Security Masters E7:
How CPR Broke ChatGPT's Isolation and What It Means for You
Blueprint Architecture for Securing
The AI Factory & AI Data Center
Call For Papers
Your Expertise. Our Stage
CheckMates Go:
CheckMates Fest
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.
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.
| 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 |
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.
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:
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.
| 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.
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
Navigate to: Infinity Portal → Policy → [Your Asset] → Advanced Proxy Settings
Content is injected inside the location / block, before the proxy_pass directive.
Allowed directives:
rewritesetadd_headerproxy_set_headersub-location blocks (e.g. location ~ ^/path { ... }) — with caveats, see Section 6NOT allowed (will cause nginx config test failure):
server { } — server-level block; belongs in Server Blockhttp { } — global block; not injectableupstream { } — not injectable; use the Portal's Reverse Proxy settingsproxy_pass without a specific location wrapper — partially allowed but ineffective; see Section 5Content is injected inside the server { } block, outside of any location.
Use this for:
proxy_set_header directives you want to apply to all locationsmap blocks (though map is technically an http-level directive in standard nginx — behavior may vary)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.
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.
$request_uri vs $uriThis is the most important technical detail when implementing rewrites in CloudGuard WAF, and the source of the most common failure mode.
| 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 |
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.
$uri$is_args$args would fix itIf 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.
proxy_passBecause 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:
proxy_pass URL (not a variable) pointing directly to the upstreamWhen 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
}
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 ✓
proxy_pass $var$request_uri does notproxy_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.
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.
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 /.
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;
}
server { } blocks — nginx rejects these inside a location contexthttp { } blocks — same reasonupstream { } blocks — these are http-level; use the Portal's Reverse Proxy settingsinclude directives — the file is already an include; recursive includes are not supported by the Portal upload mechanism-> not → in comments for the same reason.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.
rewrite Directive In Depthrewrite 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. |
| 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. |
| 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/ |
The rewrite directive operates on $uri — the path only, without the query string. The query string ($args) is handled separately:
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.redirect / permanent: the query string is appended to the redirect Location header.?: rewrite ^/old$ /new? break;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
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) |
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.
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.
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.
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.
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)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)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 pathAn 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.
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/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;
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;
}
/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
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
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.168.63.129.16) is deployment-specific. On non-Azure gateways this will differ.Create a plain text file (e.g. location-block.conf) with your rewrite sub-locations. Rules:
server, http, upstream blocksproxy_pass http://51.4.115.60:8080; not variablesecho "/your/path" | grep -P 'your_pattern'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.
Policy → Asset → Advanced Proxy Settings → Add custom headers
| Name | Value |
|---|---|
Host |
<expected-hostname-by-backend> |
Policy → Asset → Advanced Proxy Settings → Additional location block instructions → Upload
Select your .conf file and click OK.
Click Enforce (or Publish depending on your portal version). CloudGuard will:
/etc/cp/conf/rpmanager/include/waf_N_additional_location_config.confnginx -tnginx -s reload)# 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
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
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.
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.
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
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 (->, -) |
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).
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>
Cause: The WAF cannot connect to the upstream.
Checklist:
curl http://51.4.115.60:8080/sudo ufw statusproxy_pass, is the hardcoded IP correct and reachable from the WAF?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.
/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
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.
Leaderboard
Epsum factorial non deposit quid pro quo hic escorol.
| User | Count |
|---|---|
| 3 | |
| 1 | |
| 1 |
Tue 12 May 2026 @ 10:00 AM (CEST)
The Cloud Architects Series: Check Point Cloud Firewall delivered as a serviceWed 13 May 2026 @ 11:00 AM (EDT)
TechTalk: The State of Ransomware Q1 2026: Key Trends and Their ImpactThu 14 May 2026 @ 07:00 PM (EEST)
Under the Hood: Presentando Check Point Cloud Firewall como ServicioTue 12 May 2026 @ 10:00 AM (CEST)
The Cloud Architects Series: Check Point Cloud Firewall delivered as a serviceTue 19 May 2026 @ 06:00 PM (IDT)
AI Security Masters E8 - Claude Mythos: New Era in Cyber SecurityAbout CheckMates
Learn Check Point
Advanced Learning
YOU DESERVE THE BEST SECURITY