Bonaventure OgetoBy Bonaventure Ogeto|

Paystack Webhook IP Allowlisting: How and When

IP allowlisting restricts your webhook endpoint to only accept requests from Paystack's known IP addresses. Configure it in your reverse proxy (Nginx), cloud security group (AWS, GCP), or application middleware. It adds defense in depth on top of HMAC signature verification, but comes with maintenance cost because Paystack's IPs can change. Signature verification is the primary security mechanism. IP allowlisting is the optional second layer.

What IP Allowlisting Is

IP allowlisting (sometimes called IP whitelisting) means configuring your server to accept webhook requests only from specific IP addresses. Any request from an IP not on the list gets rejected immediately, usually with a 403 Forbidden response.

When Paystack sends a webhook, the request comes from one of their server IP addresses. If you know which IPs Paystack uses, you can configure your server to only accept requests from those IPs on the webhook endpoint. This blocks an attacker who tries to send fake webhooks from their own server, because their IP will not be on the list.

This is a defense-in-depth measure. You already have HMAC signature verification, which is strong authentication. IP allowlisting adds a network-level filter on top of that. An attacker would need both a valid signature (which requires your secret key) and a request originating from one of Paystack's IP addresses (which requires compromising Paystack's infrastructure or spoofing at the network level).

Paystack's Webhook IP Addresses

Paystack publishes the IP addresses their webhook requests originate from. These are documented in the Paystack API documentation and may also be available through their support team.

Important: Do not hardcode IP addresses from a blog post or tutorial (including this one). Paystack can change their IPs at any time. Always check the official Paystack webhook documentation for the current, authoritative list.

What you need to know about the IPs:

  • There are multiple IPs. Paystack uses several servers for webhook delivery for redundancy.
  • Test mode and live mode webhooks may come from different IPs.
  • The IPs can change when Paystack scales their infrastructure, migrates to new providers, or makes security changes.
  • Paystack may not always announce IP changes in advance, so if you implement allowlisting, build in monitoring to detect when legitimate webhooks start getting blocked.

For the rest of this guide, we will use placeholder IPs. Replace them with the actual values from the Paystack documentation.

Configuring Allowlisting in Nginx

If your application sits behind Nginx (which is common for Node.js, Python, and PHP deployments), configure the allowlist at the Nginx level. This blocks unwanted requests before they reach your application process.

# /etc/nginx/sites-available/your-app.conf

server {
    listen 443 ssl;
    server_name yourdomain.com;

    # ... SSL config, other routes ...

    # Paystack webhook endpoint with IP allowlisting
    location /webhooks/paystack {
        # Only allow requests from Paystack's IPs
        # Replace these with the actual IPs from Paystack's documentation
        allow 52.31.139.75;
        allow 52.49.173.169;
        allow 52.214.14.220;
        deny all;

        # Proxy to your application
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # IMPORTANT: Do not modify the request body
        proxy_request_buffering on;
    }

    # All other routes (no IP restriction)
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

After changing the config, test and reload Nginx:

sudo nginx -t && sudo systemctl reload nginx

Key points:

  • The allow and deny directives are processed in order. List all allowed IPs, then deny all.
  • If Paystack uses CIDR ranges (like 52.31.139.0/24), you can use those in the allow directive.
  • The location block applies only to the webhook path. Other routes are not affected.
  • Blocked requests get a 403 response from Nginx and never reach your application.

Configuring Allowlisting in AWS Security Groups

If your webhook endpoint runs on AWS (EC2, ECS, or behind an ALB), you can use security groups to restrict inbound traffic at the network level.

For a dedicated webhook instance or service:

  1. Open the AWS Console and navigate to EC2 > Security Groups.
  2. Create a new security group (or edit the existing one for your webhook service).
  3. Add inbound rules allowing HTTPS (port 443) from each of Paystack's IP addresses.
  4. Remove any rule that allows HTTPS from 0.0.0.0/0 (anywhere).

For an Application Load Balancer (ALB):

If your webhook shares an ALB with other services, security groups on the ALB apply to all traffic, not just the webhook path. In this case, use ALB listener rules or a WAF (Web Application Firewall) rule instead.

# Create a WAF IP set with Paystack's IPs (AWS CLI)
aws wafv2 create-ip-set   --name paystack-webhook-ips   --scope REGIONAL   --ip-address-version IPV4   --addresses "52.31.139.75/32" "52.49.173.169/32" "52.214.14.220/32"

# Then create a WAF rule that allows requests from this IP set
# on the /webhooks/paystack path and blocks everything else on that path

The WAF approach is more flexible because you can match on both the path and the source IP. Security groups can only match on IP and port.

For GCP or Azure: The equivalent is VPC firewall rules (GCP) or Network Security Groups (Azure). The concept is the same: create a rule that allows traffic from Paystack's IPs to your webhook endpoint and blocks everything else.

Configuring Allowlisting in Application Middleware

If you do not have access to Nginx or cloud security groups (for example, on a shared hosting environment or a PaaS like Heroku or Railway), you can implement IP allowlisting in your application code.

const express = require('express');
const app = express();

// Paystack webhook IPs - check Paystack docs for current list
const PAYSTACK_IPS = new Set([
  '52.31.139.75',
  '52.49.173.169',
  '52.214.14.220',
]);

function paystackIpCheck(req, res, next) {
  // Get the real client IP
  // If behind a proxy, use X-Forwarded-For or X-Real-IP
  const clientIp = req.headers['x-forwarded-for']
    ? req.headers['x-forwarded-for'].split(',')[0].trim()
    : req.ip;

  if (!PAYSTACK_IPS.has(clientIp)) {
    console.warn(
      'Webhook request from non-Paystack IP: ' + clientIp
    );
    return res.status(403).send('Forbidden');
  }

  next();
}

app.post(
  '/webhooks/paystack',
  paystackIpCheck,
  express.raw({ type: 'application/json' }),
  (req, res) => {
    // IP is already verified, now verify the signature
    // ... signature verification code ...
    res.status(200).send('OK');
  }
);

Warnings about application-level IP checking:

  • X-Forwarded-For can be spoofed. If your application is not behind a trusted proxy, an attacker can set any value in the X-Forwarded-For header. Only trust this header if you know your proxy sets it correctly and strips any client-supplied value.
  • Multiple proxies add multiple IPs. If you are behind multiple proxies (CDN + load balancer + Nginx), X-Forwarded-For contains a comma-separated list. The correct IP is usually the first one, but this depends on your proxy chain.
  • req.ip in Express depends on the trust proxy setting. If trust proxy is not configured, req.ip returns the proxy's IP, not Paystack's IP.

Set trust proxy in Express if you are behind a reverse proxy:

// Trust the first proxy
app.set('trust proxy', 1);

// Or trust specific proxy IPs
app.set('trust proxy', '127.0.0.1');

When IP Allowlisting Helps

IP allowlisting is worth the operational cost in these scenarios:

High-value transactions. If your webhook handles payments in the millions of Naira or large institutional transfers, the extra security layer is justified. The cost of a successful attack is high enough to warrant additional defense.

Regulated environments. Financial services, insurance, and healthcare applications often have compliance requirements that mandate multiple layers of authentication. IP allowlisting satisfies the "network-level access control" requirement that auditors look for.

Endpoints with no other authentication. If for some reason you cannot implement HMAC signature verification (please do), IP allowlisting at least limits who can send requests to your webhook endpoint. This is a weak substitute, not a replacement.

Reducing noise. If your webhook endpoint is being hit by scanners, bots, or random traffic, IP allowlisting silences that noise at the network level. Your application never sees the requests, which reduces log clutter and processing load.

When Signature Verification Is Enough

For most applications, HMAC SHA512 signature verification is sufficient. Here is why:

The signature is cryptographically strong. SHA512 produces a 512-bit hash. Brute-forcing a valid signature for a given payload is computationally infeasible. The only way to produce a valid signature is to know your secret key.

IP allowlisting has maintenance cost. Paystack can change their IPs. If they do and you do not update your allowlist, your webhook stops working. Depending on how you detect this (alerts vs. customer complaints), you might lose hours or days of webhook events. For a small team without 24/7 ops coverage, this risk is real.

IP spoofing at the TCP level is impractical. An attacker cannot easily spoof the source IP of a TCP connection (which HTTP requires) because TCP uses a three-way handshake. The attacker would need to intercept the handshake packets, which requires being on the network path between Paystack and your server.

IP allowlisting does not protect against compromised keys. If an attacker obtains your Paystack secret key (from a leaked .env file, a compromised CI pipeline, or a breached developer machine), they can forge valid signatures. IP allowlisting blocks them from sending those forged requests from their own server, but they could also use the key to do damage through the Paystack API directly.

If you are a startup, a small team, or building an MVP, implement signature verification correctly and move on. Add IP allowlisting later when you have the operational maturity to maintain it.

Monitoring for IP Changes

If you implement IP allowlisting, you need a way to detect when Paystack's IPs change. Otherwise, you will learn about the change when your webhooks stop arriving and customers start complaining.

Monitor your blocked requests. Log every request that your IP allowlist rejects. If you suddenly see a spike of blocked requests on the webhook path with valid-looking Paystack payloads, the IPs probably changed.

function paystackIpCheck(req, res, next) {
  const clientIp = req.headers['x-forwarded-for']
    ? req.headers['x-forwarded-for'].split(',')[0].trim()
    : req.ip;

  if (!PAYSTACK_IPS.has(clientIp)) {
    // Log blocked requests for monitoring
    console.warn(JSON.stringify({
      type: 'webhook_ip_blocked',
      ip: clientIp,
      path: req.path,
      timestamp: new Date().toISOString(),
      hasSignatureHeader: !!req.headers['x-paystack-signature'],
    }));
    return res.status(403).send('Forbidden');
  }

  next();
}

Set up alerts. Trigger an alert (Slack, PagerDuty, email) if the webhook endpoint receives zero successful requests for longer than your expected interval. If you normally get webhooks every few minutes, an hour of silence is suspicious.

Build a health check. Periodically send a test transaction in Paystack's test mode and verify that the webhook arrives. If it does not, something is wrong with your webhook pipeline, and IP changes are one possible cause.

Check for Paystack announcements. Follow Paystack's developer changelog, status page, and any developer-facing communication channels. They may announce IP changes, though this is not guaranteed.

For broader monitoring strategies, see monitoring and alerting on webhook failures.

Combining IP Allowlisting and Signature Verification

If you decide to use both, the order matters: check the IP first, then verify the signature.

app.post(
  '/webhooks/paystack',
  paystackIpCheck,        // Layer 1: Network-level filter
  express.raw({ type: 'application/json' }),
  (req, res) => {
    // Layer 2: Cryptographic verification
    const secret = process.env.PAYSTACK_SECRET_KEY;
    const signature = req.headers['x-paystack-signature'];

    const hash = crypto
      .createHmac('sha512', secret)
      .update(req.body)
      .digest('hex');

    if (hash !== signature) {
      return res.status(400).send('Invalid signature');
    }

    const event = JSON.parse(req.body.toString());
    res.status(200).send('OK');

    // Queue for processing
  }
);

IP checking is faster than HMAC computation, so doing it first rejects non-Paystack requests without burning CPU cycles on the hash. This matters if you are receiving a lot of junk traffic on the webhook path.

At the infrastructure level (Nginx + application), the flow is: Nginx checks the IP and blocks non-Paystack requests with 403. If the IP passes, the request reaches your application. Your application verifies the signature. Both layers must pass for the event to be processed.

This article is part of the Paystack webhooks engineering guide. For the cryptographic verification details, see verifying the x-paystack-signature header correctly.

Key Takeaways

  • IP allowlisting restricts webhook requests to Paystack's known IP addresses, blocking spoofed requests before they reach your application.
  • Paystack publishes their webhook IP addresses. Check the official Paystack documentation for the current list, as IPs can change.
  • Signature verification (HMAC SHA512) is the primary security mechanism. IP allowlisting is an additional layer, not a replacement.
  • Configure allowlisting at the infrastructure level (Nginx, cloud security groups) for best performance. Requests are blocked before reaching your application.
  • The maintenance cost is real: if Paystack changes their IPs and you do not update your allowlist, legitimate webhooks get blocked silently.
  • For most applications, signature verification alone is sufficient. Add IP allowlisting if you handle high-value transactions or operate in a regulated environment.

Frequently Asked Questions

Does Paystack publish their webhook IP addresses?
Paystack does publish IP addresses that webhook requests originate from in their documentation. Always check the official Paystack webhook documentation for the current list, as these IPs can change. Do not rely on IP addresses from third-party blog posts or tutorials, as they may be outdated.
Is IP allowlisting a replacement for signature verification?
No. IP allowlisting and signature verification serve different purposes. Signature verification proves the request body was created by someone who has your secret key and was not tampered with. IP allowlisting restricts which network addresses can reach your endpoint. Use both for maximum security, but if you can only implement one, signature verification is the essential one.
What happens if Paystack changes their IPs and I have an allowlist?
Your webhook endpoint will reject legitimate Paystack webhooks with a 403 error. Paystack will retry, but each retry will also be rejected. You will miss all webhook events until you update the allowlist. This is the biggest operational risk of IP allowlisting. Mitigate it with monitoring, alerts, and periodic health checks.
Should I allowlist IPs at the Nginx level or in my application code?
At the Nginx (or load balancer) level whenever possible. This blocks unwanted requests before they reach your application process, which saves CPU cycles and prevents your application from even seeing the traffic. Application-level allowlisting works but is less efficient and more prone to IP parsing errors, especially behind proxies.
Can an attacker spoof the source IP to bypass my allowlist?
TCP IP spoofing (forging the source IP of a TCP connection) is very difficult because TCP requires a three-way handshake. The attacker would need to intercept packets on the network path. At the HTTP level, an attacker can spoof the X-Forwarded-For header, which is why you must only trust that header from proxies you control. If your IP check relies on X-Forwarded-For without a trusted proxy, it can be bypassed.

Ready to build real-world apps?

Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.

Apply to the McTaba Marathon