92
votes

I am trying to create a lambda service on AWS and have it accessed from outside via the API gateway with no authentication or restriction required.

To make things easy I set the gateway for now to be a Mock.

On the Get method of the API, the Authorization is set to None and the API Key is not required.

When I try this, I get {"message":"Forbidden"} (same message if I connect it to the actual lambda service).

Any advice on how to make it accessible?

25
Did you add get method before you deploy?Cenxui
I wonder you don't have the correct invoke URL.Ka Hou Ieong
I can not vote down this question, but I would like to do it. "something-like-that" answers doesn't help the community. I have experimented forbidden messages because of bad indentation in my serverless.yml file.Perimosh
Please select the checkmark next to the correct answer, rather than adding [solved] to your question. Thanks!Tim Malone
It would be useful if you explained what was wrong and how you fixed it.Eppilo

25 Answers

100
votes

If you set 'API Key Required' option to true, please check below.

  1. you have to pass 'x-api-key' HTTP Header Parameter to API Gateway.
  2. The API Key had to be created.
  3. In addition, you need to check a Usage Plan for the API Key on API Gateway Console.
65
votes

On the API Gateway dashboard choose Resources, click Actions and choose Deploy API. Before your first deployment the only response you'll get is the {"message":"Forbidden"}.

45
votes

If you use a custom domain name and forget to select destination staging, you'll get the Forbidden message.

Simply go to Custom Domain Names and click Edit under your domain, and then select the stage under Base Path Mappings.

16
votes

You need to deploy your api on stage and use stage url go to Resources, click Actions and choose Deploy API

Now if you are getting error

{"message":"Forbidden"}.

Please check following steps

1 ) If you enable api key copy and pass your key in postman

enter image description here

2) Now you still getting same error means you will need to create usage plan

enter image description here

3) set limit and assign plan to your api

enter image description here

9
votes

I had a similar problem, and I had the following:

  1. A Custom Domain (Edge Optimized)
  2. Multiple Stages (dev, staging, prod)

I also didn't set any Authorization nor restrictions to make things simple.

I was able to fix the problem by adding Base Path Mappings for each of my stages (dev, staging, prod).

8
votes

I might be too late but one of the reasons API Gateway would give "forbidden" message is when you pass data in request Body on a GET operation. To solve the problem either make your resource POST or you do not pass data in request Body.

7
votes

This may be far from obvious, but another reason of seeing "Forbidden" error when using AWS API Gateway may be calling incorrect URL that does not correspond to any deployed API method. It can occur if you're actually hitting wrong URL (e.g. instead of calling https://9999xx9x99.execute-api.us-east-1.amazonaws.com/dev/users (note dev stage before users) you called https://9999xx9x99.execute-api.us-east-1.amazonaws.com/users (no stage). You'd expect to get 404, but you'll get 403.

BTW: after you make a deployement to https://9999xx9x99.execute-api.us-east-1.amazonaws.com/dev/users calling https://9999xx9x99.execute-api.us-east-1.amazonaws.com/user (note singular noun form here) you'll get… 403 as well, but with "Missing Authentication Token" message!

6
votes

If you set 'API' key required to true, you need to pass the api key as header.

API Key is passed as header field 'x-api-key'. Even after adding this field in header, this issue may occur. In that case, please validate below points

  • Do you have a Usage Plan? if not need to create one.
  • Link you API with Usage Plan. For that add a stage, it will link your API.
  • Do you have API Key? if not you need to create an API Key and enable it.
  • Add the Usage Plan which is linked with your API to this API Key. For that add Usage Plan.
3
votes

If Authorization and API KEY Required both are set to true for the method, then make sure you have the following Headers while sending the request:

  1. Content-Type (usually application/x-www-form-urlencoded if GET call)
  2. HOST
  3. X-Amz-Date
  4. Authorization
  5. x-api-key

I use POSTMAN for API testing which is quite reliable and then it's preety straight forward.

Note: Do not add x-api key header if you have set API KEY REQUIRED as FALSE. And if you have set AUTHORIZATION as FALSE then do not add Authorization header.

3
votes

The only other reason that I've experienced which I don't see mentioned here is literally that you tried to reach the API too quickly after being published. I hit publish and see the "your API is reachable at" domain name, and immediately copy and pasted that into Postman to check it.

I get the forbidden message. Change nothing. Check all settings to ensure that I haven't done anything - everything is correct. Kinda tearing my hair out.

Return a few minutes later to try because I'm quite sure I'm doing it all correct - it works.

DNS man. No matter how fast the Internet is - it ain't instant :)

2
votes

I got this error from an nginx fargate service trying to access a private API in API Gateway. I needed to add a policy under resource policies in my api like this

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:us-east-1:<AccountID>:<RestApiID>/*",
            "Condition": {
                "StringEquals": {
                    "aws:sourceVpce": "<VPC Endpoint ID for execute-api>"
                }
            }
        }
    ]
}
2
votes

There are a few things to do when we receive the {message: forbidden} in the API Gateway:

CORS enabled?

  1. Check if CORS is Enabled within the API ( to start with, allow the origin '*', to make sure we can test safely )
  2. Deploy the API to make sure all settings are as expected

API Key enabled?

  1. Check if we have the API Key enabled in the API Gateway
  2. Check if there is an API Key configured.
  3. Check if your API Key is assigned to the correct usageplan and add an API Stage, without the API Stage you will always receive an {message: forbidden}

If you are still facing issues, let me know so me or one of our cloud gurus @levarne can help.

2
votes

I might have come across a solution to this problem. I had the same issue right now on MacOS. I tried to flush my DNS and then it worked!

Try this in the terminal:

Mac OS X Yosemite and later

sudo killall -HUP mDNSResponder

Mac OS X Yosemite v10.10 through v10.10.3

sudo discoveryutil mdnsflushcache

Mac OS X Mavericks, Mountain Lion and Lion

sudo killall -HUP mDNSResponder

Mac OS X Snow Leopard

sudo dscacheutil -flushcache
1
votes

Local Firewall / antivirus or NGIPS (Cisco Bluecoat). The latter was my case, where I wouldn't even get logs in CloudWatch from my API. It was allowing my top level domain hosted website, but was blocking with 403 the api subdomain, with no body in the browser's network dev-tools tab.

1
votes

I got {"message":"Forbidden"} on an API with EndpointConfiguration set to PRIVATE, and a VpcEndpoint created for it in the Vpc's private subnets (this is an inter-service API)

The reason I got {"message":"Forbidden"} was that I was under the impression I should use one of the VpcEndpoint's urls. The URL to use is still the one associated with the stage (in ApiGateway console). It is:

https://${RestApiId}.execute-api.${Region}.amazonaws.com/${StageName}

1
votes

Just a note on the similar case I ran into with Swagger Editor:

  • I exported the OpenAPI 3.0 YAML from API Gateway → Stages → select "Prod" → select "Export" tab → switch radiobutton to "OpenAPI 3" → "Export as OpenAPI 3 + API Gateway Extensions"
  • Paste the received YAML to https://editor.swagger.io/
  • Execute a trivial GET method.
  • It returns 403 Forbidden with {"message":"Forbidden"} body.

curl command from Swagger Editor looked like this:

curl -X GET "https://xxx52xxxx9.execute-api.eu-central-1.amazonaws.com//Prod/users" -H "accept: application/json"

(note the double // before Prod).

And the same curl command without // worked via the command line!

The trick that worked is to replace this server structure returned in the API Gateway-generated:

servers:
  - url: "https://xxx52xxxx9.execute-api.eu-central-1.amazonaws.com/{basePath}"
    variables:
      basePath:
        default: "/Prod"

With the full url without variables:

servers:
  - url: "https://xxx52xxxx9.execute-api.eu-central-1.amazonaws.com/Prod"

Notably, removing the leading slash from default: "/Prod" didn't help.

1
votes

I had a similar problem. Turned out that my certificate in Certificate Manager was not created in North Virginia region (us-east-1), therefore I could not mark Custom Domain as Edge-optimized. I had to choose Regional instead.

When I re-imported the certificate using N. Virginia region and created a Custom Domain again, but this time with Edge-optimized endpoint configuration, it worked flawlessly.

1
votes

In my case, it was because I used the

Managed-AllViewer

origin request policy. Switching to

Managed-UserAgentRefererHeaders

solved the problem.

My client sent an Accept-Encoding header, which CloudFront did not like. You can verify this by creating a custom origin request policy of type "Whitelist", and although Accept-Encoding is selectable from the list, you receive an error when creating the policy: "The parameter Headers contains Accept-Encoding that is not allowed."

I did not find the relevant documentation unfortunately. Also no clue why such an inconspicuous header is disallowed.

1
votes

I received this error today because the aws_host in the signing header was incorrect (using Boto3 and AWSRequestAuth).

While refactoring I started to loop through multiple requests, but this introduced request was on a different API that required a different aws_host.

auth = AWSRequestsAuth(aws_access_key=credentials.access_key,
                       aws_secret_access_key=credentials.secret_key,
                       aws_token=credentials.token,
                       aws_host=f'api.{env}.XXX.dk',
                       aws_region=region,
                       aws_service='execute-api')
0
votes

We had faced this issue in our production when we used Kong as our api gateway. Our requests passed thro when initiated from Postman but failed with 403 when initiated via Code. The Bot plugin in Kong was enabled which only allowed requests initiated from Browser or Mobile App based on the user agent header value.Our requests initiated via Http Client failed. Once we disabled the bot plugin then the error didnt occur. It now allows request if the user-agent is Apache-HttpClient/4.5.2 (Java/1.8.0_91).

0
votes

Putting my experience over here as well. I tried all those things above and it turned out that putting the domain with a wildcard solved my {"message":"Forbidden"} issue: *.mydomain.com

Custom domain

0
votes

In my case the api key was not enable. Make sure the API is set as Enabled. enter image description here

0
votes

As @gary69 and @Adriaan Pelzer mentions

https://stackoverflow.com/a/52727654/809043

https://stackoverflow.com/a/55136675/809043

You can get the message {"message":"Forbidden"} when requesting a Private API.

So if you have a setup where all traffic should go thorough a API Endpoint which than directs the traffic to the API Gateway then the following parameters may be used.

APIGatewayVPCEndpoint:
  Type: 'AWS::EC2::VPCEndpoint'
  Properties:
    PolicyDocument: '{
        "Version":"2012-10-17",
        "Statement":[{
          "Effect":"Allow",
          "Principal": "*",
          "Action":["execute-api:Invoke"],
          "Resource":["arn:aws:execute-api:eu-north-1:000000000000:*/*"]
        }]
      }'
  ...
  VpcEndpointType: Interface
  PrivateDnsEnabled: true

If PrivateDnsEnabled is enabled, than the endpoint in the API Gateway needs to be of Type Private, and a policy needs to added.

  ApiGatewayRest:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Description: A mocked API
      Name: Mocked API
      EndpointConfiguration:
        Types:
          - PRIVATE
      Policy: '{
        "Version": "2012-10-17",
        "Statement": [{
          "Effect": "Allow",
          "Principal": "*",
          "Action": "execute-api:Invoke",
          "Resource": "arn:aws:execute-api:eu-north-1:000000000000:*/*/*/*"
        }]
      }'

This forum thread helped clear out some of the details for me

https://forums.aws.amazon.com/thread.jspa?threadID=286760

0
votes

This usually comes when we try to access the Private API endpoint with incorrect policy & without passing the 'HOST' header in the invoke request. Let's say I have an API that is deployed as a private endpoint with the below resource policy.

{
"Version": "2012-10-17",
"Statement": [
    {
        "Effect": "Allow",
        "Principal": "*",
        "Action": "execute-api:Invoke",
        "Resource": "arn:aws:execute-api:us-west-2:12345678:2ucqasdfasdfryc/*"
    },
    {
        "Effect": "Deny",
        "Principal": "*",
        "Action": "execute-api:Invoke",
        "Resource": "arn:aws:execute-api:us-west-2:12345678:2dgaucqt6dfgdyc/*",
        "Condition": {
            "StringNotEquals": {
                "aws:SourceVpce": "vpce-87878kjlkj8787k"
            }
        }
    }
]
}

Accessing Private API endpoint when private-DNS-hostnames disabled.

curl -v -H 'Host: 01234567ab.execute-api.us-west-2.amazonaws.com' https://vpce-01234567abcdef012-01234567.execute-api.us-east-1.vpce.amazonaws.com/test/pets

                          

[OR] use the API ID instead of the Host header.

curl -v -H 'x-apigw-api-id: 01234567' https://vpce-01234567abcdef012-01234567.execute-api.us-east-1.vpce.amazonaws.com/test/pets
0
votes

I was also facing the same issue a week back and spent sometime to identify the issue. Our api gateway endpoint has been configured to work only through public network and we have a WAF & resource policy to filter the incoming requests to the endpoint. I was able to access the endpoint from a different vpc and not from a particular vpc. I was getting Forbidden error.

At last, found that the vpc that i was trying from, have the VPC Endpoint for the execute api service with private DNS enabled. API Gateway endpoint was resolving to a private IP inside the VPC.

There are two ways to resolve it. One, we can disable the private DNS, which is what I did. It started working very fine after the change. But we have to make sure it doesn't affect others who are using the vpc endpoint.

Two, we can use a custom domain name & we can use that to call from the vpc.