Amazon API Gateway is a great way to wrap Lambda functions as microservices exposed over HTTP/S, among many uses. However, any API Gateway endpoint is publically accessible. There are ways to restrict access using IAM and Authorizers, but for simple task of IP whitelisting was always somewhat challenging, if not downright hack-y.

Recently AWS announced Resource Policies for API Gateway, which make IP whitelisting a breeze. This is extremely helpful for a company such as mine, as we deal with a lot of integrations that rely on IP whitelisting as one of the many layers of security. In this post I will walk through setting up IP whitelisting on an API hosted on API Gateway. We will use API Gateway’s built in Mock API feature to create a simple API, and secure it via IP Whitelisting.

Create an API

We will start by creating a mock API in API Gateway. Log on to the Amazon API Gateway section on the console, and click to create a new API.


Next, create a new resource called hello by clicking Actions/Create Resource.


We will now add a method to this resource by clicking Actions/Create Method with the hello resource selected. We will pick GET.


For this simple test, we’ll use Mock integration for the method GET /hello.


Here we can see the GET method created successfully.


Now, we can deploy this API. Amazon API Gateway deploys APIs as a part of a stage, so we get to create a stage as we deploy the API. Select Actions/Deoply API.


You can pick any stage name, I used dev.


Once deployed, you’ll see a stage editor, with the URL endpoint.


This URL can be accessed as a public endpoint. The Mock API returns HTTP 200 when invoked.

$ curl -i
HTTP/2 200
date: Sun, 15 Apr 2018 00:49:26 GMT
content-type: application/json
content-length: 0
x-amzn-requestid: d8ea6b1b-4046-11e8-97a5-4d3c440fb2a7
x-amz-apigw-id: FW4vhFdvoAMFRbQ=

Whitelist the API

We will set up a whitelist where it is only accessible from our IP address. You can use a service like whatsmyip to get your IP address. If you’re like me and prefer command line, here it is -

$ dig +short

Now we will whitelist this IP address. Click Resource Policy from the left menu.


We will now need the ARN of the API method to build a policy. To get the ARN, go back to the stage page, and copy the ARN of the method.


Once copied, replace the ARN and IP placeholders below with the method ARN and your IP address respectively.

    "Version": "2012-10-17",
    "Statement": [
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "REPLACE_ME_WITH_METHOD_ARN",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [

We now need to redeploy the API. Unlike IAM where policies take affect immediately, I could not see the Resource Policy behaving the same way.

Redeploy the API using the dev stage. This will not change the URL.


Thats it! The IP address is whitelisted. We can verify this via curl.

I used my wireless provider to check and ensure any other IP address is blocked from accessing the API. Be sure to turn off the phone’s wifi if using this method.


Alternatively, you can switch to a different network, or IM the URL to a friend who is not sharing the same network to verify.

Gotchas and Tips

  1. Any changes to the Resource Policy will not reflect unless the API is redeployed as shown above.
  2. I could not find a way to attach the Resource Policy to the API in Serverless Framework. Edit: This feature is now available in the latest Serverless Framework Version, with this merged PR.
  3. The API Gateway Endpoints cannot be restricted to whitelist the VPC using this technique. The endpoints are still public, and they will only see the NAT Gateway EIP as the inbound request.
  4. If an EC2 instance is accessing the API, the API Gateway will use it’s Public IP to evaluate the resource policy.
  5. Wildcards can be used for the resource ARN in the policy, like "Resource": "arn:aws:execute-api:us-east-1:12345678900:*:*" would apply the policy for all the API resources under the account ID 12345678900.
  6. It does take a 30-60 seconds for the change to be reflected after deploying the API. Make sure you’re using incognito mode or curl to avoid any browser caching.
  7. The same example above can be used to blacklist IP addresses, by replacing the Allow with Deny.