Testing Next-Gen WAF Rate Limiting Rule with GitHub Actions
Learn how to test a Next-Gen WAF rate limiting rule using Fastly and GitHub Actions. Prevent DDoS attacks and enforce Terms of Service with this post.

In this blog post, we'll show you how to configure and test a rate limiting rule. Rate limiting is a technique used to control the number of requests that can be made to a given endpoint within a specified time period. This can be useful for preventing denial-of-service attacks (DDoS) and enforcing Terms of Service. In order to get started, you’ll need the following:
Prerequisites
A Fastly account, if you don’t already have an account, grab a free account here
A GitHub account
Create a rate limiting rule
Create a rule like the following screenshot. This rule will rate-limit on the request header “api-key”. When there are 5 or more requests with the same api-key within 1 minute window, then that api-key will be blocked.
Test the Rule with a Github Action
Now that the rule has been deployed, you can test it by making a series of requests to the protected endpoint. In this example, you will use a Github action to spin up a Next-Gen WAF Agent. With the same GitHub Action, you will send requests with curl to the Agent. If you make more requests than the specified limit, you should receive a 429 Too Many Requests error.
For the following GitHub Action to work you will need to set up your Next-Gen WAF Agent Access Key ID and Secret Access Key as Github Secrets.
name: Rate Limiting Demo
on:
workflow_dispatch:
jobs:
ngwaf-rate-limiting-demo:
runs-on: ubuntu-latest
environment: production
timeout-minutes: 2
services:
sigsci:
image: signalsciences/sigsci-agent:latest
env:
SIGSCI_ACCESSKEYID: ${{ secrets.NGWAF_TERRAFORM_NGWAF_SITE_ACCESSKEYID }}
SIGSCI_SECRETACCESSKEY: ${{ secrets.NGWAF_TERRAFORM_NGWAF_SITE_SECRETACCESSKEY }}
SIGSCI_REVPROXY_LISTENER: "app1:{listener=http://0.0.0.0:8888,upstreams=https://http-me.edgecompute.app:443/,pass-host-header=false};"
SIGSCI_UPLOAD_INTERVAL: "5s"
ports:
- 8888:8888
steps:
- name: Wait for NGWAF Agent
run: |
# Wait for agent to be ready
sleep 5
- name: Send requests through NGWAF
run: |
echo "#### Sending requests for rate limiting test domains"
for i in {1..10}; do
echo "Request Number: $i"
response=$(curl -si -H 'host:api.bubbs.coffee' -X POST 'http://0.0.0.0:8888/anything/start-ai-chat' -H api-key:gh-action-rl-demo)
echo "$response"
# Extract the HTTP status code from the first line (e.g., "HTTP/1.1 406 Not Acceptable")
status=$(echo "$response" | head -n 1 | awk '{print $2}')
if [ $i -gt 4 ] && [ "$status" != "406" ]; then
echo "Error: Unexpected status code: $status on request $i"
exit 1
fi
echo
done
- name: Wait for logs to upload
run: |
# Wait for agent to upload logs
sleep 10Example of failed GitHub Action output
Without the rule in place, you should see the job fail at the 5th request.
#### Sending requests for rate limiting test domains
Request Number: 1
HTTP/1.1 200 OK
Content-Length: 564
Content-Type: application/json
Date: Thu, 06 Feb 2025 22:16:03 GMT
X-Served-By: cache-iad-kiad7000049-IAD
{"args":"","body":"","headers":{"accept":"*/*","accept-encoding":"gzip","api-key":"gh-action-rl-demo","content-length":"0","host":"http-me.edgecompute.app","user-agent":"curl/8.5.0","x-forwarded-for":"172.18.0.1","x-forwarded-host":"api.bubbs.coffee","x-forwarded-port":"80","x-forwarded-server":"4b89e8de722e","x-real-ip":"172.18.0.1","x-sigsci-agentresponse":"200","x-sigsci-requestid":"67a534a42c015ef72d509c9f","x-sigsci-tags":"NO-CONTENT-TYPE,SUSPECTED-BOT"},"ip":"172.214.46.82","method":"POST","url":"https://http-me.edgecompute.app/anything/start-ai-chat"}
Request Number: 2
HTTP/1.1 200 OK
Content-Length: 564
Content-Type: application/json
Date: Thu, 06 Feb 2025 22:16:03 GMT
X-Served-By: cache-iad-kiad7000057-IAD
{"args":"","body":"","headers":{"accept":"*/*","accept-encoding":"gzip","api-key":"gh-action-rl-demo","content-length":"0","host":"http-me.edgecompute.app","user-agent":"curl/8.5.0","x-forwarded-for":"172.18.0.1","x-forwarded-host":"api.bubbs.coffee","x-forwarded-port":"80","x-forwarded-server":"4b89e8de722e","x-real-ip":"172.18.0.1","x-sigsci-agentresponse":"200","x-sigsci-requestid":"67a534a42c015ef72d509ca0","x-sigsci-tags":"NO-CONTENT-TYPE,SUSPECTED-BOT"},"ip":"172.214.46.82","method":"POST","url":"https://http-me.edgecompute.app/anything/start-ai-chat"}
... truncated for brevity
Request Number: 5
HTTP/1.1 200 OK
Content-Length: 564
Content-Type: application/json
Date: Thu, 06 Feb 2025 22:16:03 GMT
X-Served-By: cache-iad-kiad7000153-IAD
{"args":"","body":"","headers":{"accept":"*/*","accept-encoding":"gzip","api-key":"gh-action-rl-demo","content-length":"0","host":"http-me.edgecompute.app","user-agent":"curl/8.5.0","x-forwarded-for":"172.18.0.1","x-forwarded-host":"api.bubbs.coffee","x-forwarded-port":"80","x-forwarded-server":"4b89e8de722e","x-real-ip":"172.18.0.1","x-sigsci-agentresponse":"200","x-sigsci-requestid":"67a534a42c015ef72d509ca3","x-sigsci-tags":"NO-CONTENT-TYPE,SUSPECTED-BOT"},"ip":"172.214.46.82","method":"POST","url":"https://http-me.edgecompute.app/anything/start-ai-chat"}
Error: Unexpected status code: 200 on request 5Example of successful GitHub Action output
Below is an example that shows a successful GitHub Action run. The block action 406 is returned on requests 5 through 10.
#### Sending requests for rate limiting test domains
Request Number: 1
HTTP/1.1 200 OK
Content-Length: 582
Content-Type: application/json
Date: Tue, 25 Feb 2025 20:59:19 GMT
X-Served-By: cache-chi-kigq8000027-CHI
{"args":"","body":"","headers":{"accept":"*/*","accept-encoding":"gzip","api-key":"gh-action-rl-demo","content-length":"0","host":"http-me.edgecompute.app","user-agent":"curl/8.5.0","x-forwarded-for":"172.18.0.1","x-forwarded-host":"api.bubbs.coffee","x-forwarded-port":"80","x-forwarded-server":"a1f860dbb73e","x-real-ip":"172.18.0.1","x-sigsci-agentresponse":"200","x-sigsci-requestid":"67be2f28d07cd4dde0e138fd","x-sigsci-tags":"NO-CONTENT-TYPE,SUSPECTED-BOT,site.api-key-rl"},"ip":"172.183.229.144","method":"POST","url":"https://http-me.edgecompute.app/anything/start-ai-chat"}
Request Number: 2
HTTP/1.1 200 OK
Content-Length: 582
Content-Type: application/json
Date: Tue, 25 Feb 2025 20:59:20 GMT
X-Served-By: cache-chi-kigq8000027-CHI
{"args":"","body":"","headers":{"accept":"*/*","accept-encoding":"gzip","api-key":"gh-action-rl-demo","content-length":"0","host":"http-me.edgecompute.app","user-agent":"curl/8.5.0","x-forwarded-for":"172.18.0.1","x-forwarded-host":"api.bubbs.coffee","x-forwarded-port":"80","x-forwarded-server":"a1f860dbb73e","x-real-ip":"172.18.0.1","x-sigsci-agentresponse":"200","x-sigsci-requestid":"67be2f28d07cd4dde0e138fe","x-sigsci-tags":"NO-CONTENT-TYPE,SUSPECTED-BOT,site.api-key-rl"},"ip":"172.183.229.144","method":"POST","url":"https://http-me.edgecompute.app/anything/start-ai-chat"}
Request Number: 3
HTTP/1.1 200 OK
Content-Length: 582
Content-Type: application/json
Date: Tue, 25 Feb 2025 20:59:20 GMT
X-Served-By: cache-chi-kigq8000027-CHI
{"args":"","body":"","headers":{"accept":"*/*","accept-encoding":"gzip","api-key":"gh-action-rl-demo","content-length":"0","host":"http-me.edgecompute.app","user-agent":"curl/8.5.0","x-forwarded-for":"172.18.0.1","x-forwarded-host":"api.bubbs.coffee","x-forwarded-port":"80","x-forwarded-server":"a1f860dbb73e","x-real-ip":"172.18.0.1","x-sigsci-agentresponse":"200","x-sigsci-requestid":"67be2f28d07cd4dde0e138ff","x-sigsci-tags":"NO-CONTENT-TYPE,SUSPECTED-BOT,site.api-key-rl"},"ip":"172.183.229.144","method":"POST","url":"https://http-me.edgecompute.app/anything/start-ai-chat"}
Request Number: 4
HTTP/1.1 200 OK
Content-Length: 582
Content-Type: application/json
Date: Tue, 25 Feb 2025 20:59:20 GMT
X-Served-By: cache-chi-kigq8000027-CHI
{"args":"","body":"","headers":{"accept":"*/*","accept-encoding":"gzip","api-key":"gh-action-rl-demo","content-length":"0","host":"http-me.edgecompute.app","user-agent":"curl/8.5.0","x-forwarded-for":"172.18.0.1","x-forwarded-host":"api.bubbs.coffee","x-forwarded-port":"80","x-forwarded-server":"a1f860dbb73e","x-real-ip":"172.18.0.1","x-sigsci-agentresponse":"200","x-sigsci-requestid":"67be2f28d07cd4dde0e13900","x-sigsci-tags":"NO-CONTENT-TYPE,SUSPECTED-BOT,site.api-key-rl"},"ip":"172.183.229.144","method":"POST","url":"https://http-me.edgecompute.app/anything/start-ai-chat"}
Request Number: 5
HTTP/1.1 406 Not Acceptable
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Tue, 25 Feb 2025 20:59:20 GMT
Content-Length: 20
406 Not Acceptable
... truncated for brevity
Request Number: 10
HTTP/1.1 406 Not Acceptable
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Tue, 25 Feb 2025 20:59:20 GMT
Content-Length: 20
406 Not AcceptableEnsure security settings are validated
We've outlined a practical approach to testing rate-limiting rules in Fastly's Next-Gen WAF using GitHub Actions. This method allows for automated validation of these rules, crucial for defending against cyber attacks and enforcing usage policies. Watch the demo for a hands-on example: Github Action and NGWAF Rate Limiting Demo.






