When running Spring Boot applications behind Cloudflare, you may encounter errors like this:

RequestRejectedException: The request was rejected because the header: "Cf-Region " has a value "Île-de-France" that is not allowed.

StrictHttpFirewall.java in validateAllowedHeaderValue at line 843

At first glance, this looks like Spring Boot is rejecting UTF-8 characters in headers. In reality, it is a combination of three factors:


Why This Happens

1. Cloudflare Adds Region Headers

If you have Cloudflare's Add Visitor Location Headers feature enabled, Cloudflare injects headers like:

  • cf-region
  • cf-region-code
  • cf-ipcity

These can contain non-ASCII characters (e.g. Île-de-France). Cloudflare documents that these headers are UTF-8 encoded.

2. Servlet Containers Use ISO-8859-1 for Headers

Most servlet containers (Tomcat, Jetty, Undertow) expose header values as ISO-8859-1 Strings. This means that UTF-8 byte sequences get misinterpreted, producing mojibake such as Île-de-France.

3. Spring Security’s StrictHttpFirewall is Very Conservative

StrictHttpFirewall validates headers and rejects any containing control characters or non-printable characters by default. Misinterpreted UTF-8 sequences often contain bytes outside the allowed range, triggering:

RequestRejectedException

Should Cloudflare Be Injecting These?

Yes — this is expected if you have enabled the managed transform. If you do not need human-readable region names, prefer the ASCII-only headers cf-region-code or cf-ipcountry.


Solutions

Option 1: Use ASCII-Safe Headers (Recommended)

Disable cf-region or ignore it, and use cf-region-code or cf-ipcountry instead.

  • Cloudflare Dashboard: Disable Add Visitor Location Headers entirely, or
  • Transform Rules: Remove or normalize cf-region before it reaches the origin.

This avoids passing non-ASCII data to your application entirely.

Option 2: Normalize Headers at the Edge

You can add a Cloudflare Transform Rule to rewrite cf-region values to ASCII (e.g., Île-de-FranceIle-de-France). This keeps the header but avoids encoding issues.

Option 3: Relax Spring’s Firewall (Use Carefully)

If you really need cf-region, you can override the header validation in StrictHttpFirewall. Example configuration:

@Bean
HttpFirewall httpFirewall() {
    StrictHttpFirewall firewall = new StrictHttpFirewall();
    // Allow all printable characters except CR/LF
    firewall.setAllowedHeaderValues(value -> {
        for (int i = 0; i < value.length(); i++) {
            char c = value.charAt(i);
            if (c == '\r' || c == '\n') return false;
        }
        return true;
    });
    return firewall;
}

@Bean
WebSecurityCustomizer webSecurityCustomizer(HttpFirewall firewall) {
    return web -> web.httpFirewall(firewall);
}

This approach removes the restriction on non-ASCII values, but you are responsible for validating and sanitizing input to avoid response-splitting and header-injection attacks.


Key Takeaways

  • This is not a Spring Boot bug — it’s a combination of Cloudflare adding non-ASCII headers, servlet encoding behavior, and Spring’s strict validation.
  • Best practice: Avoid sending non-ASCII header values to the backend when possible.
  • If you must accept them: Either normalize them at the edge or explicitly relax StrictHttpFirewall to allow them.

By handling these headers at the edge or normalizing them before they hit your application, you keep your backend secure without losing valuable geolocation data.