Skip to main content

Command Palette

Search for a command to run...

A Practical Approach To Securing API With Cloudflare WAF

Updated
10 min readView as Markdown

Building an API is relatively easy. The harder problem starts when the API becomes publicly accessible.

Once an API is exposed to the Internet, it can receive much more than the requests we expect. Attackers can send malicious requests, repeatedly hit endpoints, use unexpected HTTP methods, send suspicious query parameters, or simply generate enough traffic to consume application resources.

A common approach is to handle all of these concerns inside the application. The application receives the request, validates it, checks authentication, and finally decides whether the request should be processed.

But this raises another question:

What if we could reject unwanted requests before they reach the application?

This is where a Web Application Firewall (WAF) can help. A WAF is a security layer that inspects incoming web traffic and applies rules to determine whether a request should be allowed, challenged, or blocked.

Consider a simple request to an orders API:

GET /api/orders?id=1
Authorization: Bearer <token>

Without an edge security layer, the request travels directly to the application:

With an Web Application Firewall such as Cloudflare WAF, the request can be evaluated before it reaches the application:

If a request violates one of the configured policies, Cloudflare can reject it instead of forwarding it to the application.

This is the idea behind an edge security layer.

The code snippets in this article focus on the important parts of the implementation. For the complete configuration you can find the source code in the repository below.

Github Repository: https://github.com/muhammadyulasfipahrizal/WAF-API.git


What We Are Building

In this project, we will build a small Go API and place Cloudflare WAF in front of it to demonstrate several edge security controls.

The application is intentionally simple. It provides enough endpoints and request patterns to demonstrate different WAF policies without introducing unnecessary application complexity.

The application exposes three endpoints:

/api/login
/api/orders
/admin

API

The /api/login endpoint is used to authenticate a user. For this demo, valid credentials return a JWT that can then be used when accessing /api/orders.

The /api/orders endpoint represents a simple API that returns order data. It supports several query parameters:

id
item
price
status

These parameters give us different request patterns to test against the WAF.

The /admin endpoint represents an administrative page that should not be publicly accessible.

Security Controls

The WAF configuration demonstrates the following protections:

Security Control Purpose
Main page challenge Challenge requests when accessing the application
Authorization header enforcement Reject protected API requests without an Authorization header
HTTP method filtering Allow only expected methods for an endpoint
IP restriction Restrict access to the restricted page
Suspicious request filtering Block requests containing suspicious patterns
Rate limiting Limit excessive request traffic
API schema validation Ensure requests follow the defined API contract

Project Architecture

Cloudflare sits between the Internet and the application. This allows incoming requests to be evaluated against edge level security policies before they reach the origin.

For example, since /api/orders is a read only endpoint, requests using unsupported methods such as POST can be rejected by Cloudflare before they are forwarded to the application:

The responsibility is divided between two layers:

Layer Responsibility
Cloudflare WAF Challenge, rate limiting, method filtering, IP restrictions, suspicious request filtering, schema validation
Go API JWT authentication, application validation, business logic

Passing the WAF does not mean that a request is trusted. It only means that the request passed the configured edge level policies.


Application Structure

  1. cmd directory contains the application entry point, while internal contains the authentication, middleware, and HTTP handlers.

  2. schema directory contains the OpenAPI definition used to describe the API contract and demonstrate schema validation with Cloudflare.

  3. web directory contains the frontend used to interact with the API and test the WAF scenarios.

  4. The application is containerized using the Dockerfile, and

  5. .github/workflows is used to build and publish the Docker image with Github Action.


Cloudflare WAF

Cloudflare WAF provides several mechanisms that can be used to enforce policies at the edge. In this project, each rule is designed to address a specific type of unwanted request.

Challenge the Main Page

The first rule protects the main application page with an interactive challenge.

(http.request.uri.path eq "/")

When accessing /, the client must pass the Cloudflare challenge before the request continues to the application.

This rule prevent automated clients from accessing the application page without first passing a basic verification step. This is useful for reducing unwanted bot traffic before it reaches the origin.


Require Authorization Header for APIs

Protected API endpoints should not receive requests that do not contain an Authorization header.

The login endpoint is excluded because clients need to access it before they have a token.

starts_with(http.request.uri.path, "/api/")
and not any(http.request.headers["authorization"][*] ne "")
and http.request.uri.path ne "/api/login"

The behavior is:

This does not replace authentication.

The WAF checks for the presence of the Authorization header. The Go application validates the JWT itself.

For example, a request containing:

Authorization: Bearer <token>

may pass the WAF rule, but the application still needs to determine whether the token is valid, expired, and properly signed.

This provides a simple defense-in-depth mechanism: the edge performs an initial check, while the application performs the actual authentication.


Enforce HTTP Methods

The /api/orders endpoint is designed as a read-only API, so only GET requests are expected.

(http.request.uri.path eq "/api/orders" 
and http.request.method ne "GET")

The behavior is:

The reasoning is straightforward:

The /api/orders endpoint is designed as a read-only API, so unsupported write methods provide no value.


Protect Admin Page By IP

The /admin page represents an administrative interface and should not be publicly accessible.

For this project, access is restricted to a specific trusted IP address.

(http.request.uri.path eq "/admin" 
and ip.src ne ADMIN_IP)

The flow is:

This provides a simple way to restrict an administrative endpoint at the edge.


Block Suspicious Requests

This rule filters API requests containing patterns commonly associated with SQL injection attempts.

starts_with(http.request.uri.path, "/api/")
and (
  lower(http.request.uri.query) contains " or "
  or lower(http.request.uri.query) contains "' or "
  or lower(http.request.uri.query) contains "union select"
  or lower(http.request.uri.query) contains "select "
  or lower(http.request.uri.query) contains "drop table"
  or lower(http.request.uri.query) contains "insert into"
  or lower(http.request.uri.query) contains "delete from"
  or lower(http.request.uri.query) contains "--"
  or lower(http.request.uri.query) contains "/*"
  or lower(http.request.uri.query) contains "*/"
)

For example:

GET /api/orders?id=1' OR '1'='1

can be detected as suspicious and therefore blocked by Cloduflare WAF.

Application-level protection is still required, including input validation, parameterized queries, and secure database practices.


Rate Limiting

An API can also be abused through repeated requests.

Even when individual requests are valid, a large number of requests can consume CPU, memory, bandwidth, connection pools, and other application resources.

Cloudflare can apply rate limiting before this traffic reaches the origin.

Excessive requests can be stopped before they consume resources at the origin.

This is particularly useful when the origin has limited resources or when an endpoint is frequently targeted by bots.


API Schema Validation

An API schema describes what a valid API request should look like.

For example, the /api/orders endpoint define the id query parameter as an integer:

/api/orders:
  get:
    parameters:
      - name: id
        in: query
        schema:
          type: integer

A request such as:

GET /api/orders?id=10
matches the expected type.

The OpenAPI definition can be uploaded to Cloudflare WAF and used for API schema validation.

The flow is:

This is useful because the API contract can become part of the security boundary.

Instead of being used only as documentation, the OpenAPI definition can also help Cloudflare determine whether incoming requests conform to the expected API structure.


Results

After deploying the Go API behind Cloudflare WAF, the configured policies were tested against different request scenarios.

The following results show the behavior of each control.

Challenge the Main Page

Accessing / triggers the configured Cloudflare interactive challenge.

After successfully passing the challenge, the request continues to the application.


Require Authorization Header for APIs

A request to a protected API without an Authorization header is rejected:

GET /api/orders?id=1

A request containing the header can continue to the application, where the JWT is validated.

Enforce HTTP Methods

The /api/orders endpoint accepts GET, while unsupported methods are rejected by Cloudflare.

For example:

POST /api/orders
→ Blocked

Protect the Admin Page by IP

Requests to /admin from the configured administrator IP are allowed, while requests from other IP addresses are blocked.


Block Suspicious Requests

A request containing a suspicious SQL-like pattern is rejected by the WAF.

For example:

GET /api/orders?id=1' OR '1'='1

is detected and blocked by the configured rule.


Rate Limiting

Repeated requests eventually trigger the configured rate-limiting policy.

Normal traffic continues to the origin, while traffic exceeding the configured policy is stopped by Cloudflare.


API Schema Validation

Requests that conform to the OpenAPI contract can reach the origin, while requests that do not match the defined schema can be rejected.

For example:

GET /api/orders?id=10

matches the expected integer type for id.


Conclusion

Securing an API is not only about adding authentication to the application.

For an Internet-facing service, there is value in having an additional security layer between the client and the origin. Cloudflare WAF can inspect and control incoming traffic at the edge, helping protect the application from a wide range of unwanted or potentially malicious requests before they reach the origin.

In this project, we demonstrate several of these capabilities, including challenges, rate limiting, HTTP method restrictions, IP restrictions, suspicious request filtering, and API schema validation.

The important lesson from this project is that security responsibilities do not have to live entirely inside the application.

The edge can handle policies that are suitable for inspection before the request reaches the origin, while the application remains responsible for authentication, business logic, and application level validation.

The code snippets in this article focus on the important parts of the implementation. For the complete source code, you can find it through repository below.

Github Repository