I have an App Runner service running a NodeJS/Express server which is a REST API. App Runner has given me the following end point -
https://example_server_random_id.awsapprunner.com
The frontend is developed using React and deployed in S3 as a static website. I have created a CloudFront distribution for serving the static content from S3 and have a default behavior (*). CloudFront has given me the following URL -
https://cloud_front_ranom_id.cloudfront.net
So I have updated the backend code to support CORS and added the CloudFront URL as allowed origin. From the React app, I am using the App Runner URL as my base URL for all the API calls. This setup works perfectly.
Now, I wanted to get rid of CORS related setup by using the CloudFront distribution as a reverser proxy to my App Runner service. To do that, I have done following -
I have added a custom origin that points to my App Runner service endpoint - https://example_server_random_id.awsapprunner.com
I have created a new behavior (/api*) that has precedence 0, so it should be handling all the requests that have "/api" in its path instead of the default behavior (*). This behavior uses the custom origin that I have created in the previous step.
I have allowed all the HTTP request (GET, POST, OPTION, PUT etc.) for this new behavior.
For the new behavior, I have used "CachingDisabled" as the Cache Policy and "All Viewer" as the Origin Request Policy.
I have replaced the base URL and use /api as the new base URL instead of the App Runner endpoint.
I did not remove the CORS related settings from the backend code (access-control-allow-origin header value is the CloudFront endpoint and access-control-allow-credential header value is true).
This setup is not working as expected. I can still access the static contents but for all the requests to the backend (with /api base URL) give me the following errors -
Cannot POST /api/users/sign-in Status Code: 404
Cannot GET /api/users Status Code: 404
etc.
Can you please let me know if there is any way to debug this and what could be the problem in the setup?
I am writing down how I fixed this in case someone faced the same issue.
The following article helped me to pinpoint the issue - https://advancedweb.hu/how-to-debug-cloudfront-origin-requests/
Basically, the problem was the cache policy I was using. I used the following webhook tester to find out the header values, query parameter and cookies and modified the cache policy to use the legacy cache settings (Appropriate header, cookies and query parameters)-
https://webhook.site/
I have an angular app hosted on S3. When I try to directly go to a route like myapp.com/items I get a 403 forbidden error. But when I access the app from myapp.com everything is okay and from the app I can navigate to myapp.com/items. I don't understand why this is happening.
How can I make it so that routes like myapp.com/items can be accessed directly and not throw an error?
On CloudFront distribution enter error pages tab
Then create a new error response to deal with any othe routes and redirect it to index.html file
I have a Lambda function (a packaged next.js app) which I'm trying to access via CloudFront. The web app works unless I try to hit the homepage.
When I hit /search or /video/{videoId} the page loads just fine.
When I try to hit the homepage, I get the following error page:
502 ERROR
The request could not be satisfied.
The Lambda function returned invalid JSON: The JSON output is not parsable. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
Generated by cloudfront (CloudFront)
Request ID: {id}
Why would just the homepage be invalid JSON? Where can I see this JSON to determine what is wrong? I created a mock Cloudfront request test in the Lambda function and it just returns successfully.
The problem was due to the 1 MB size limit of CloudFront Lambda#Edge responses. I didn't realize that Next.js's serverside rendering was creating a large <script id="__NEXT_DATA__"> tag on my homepage with all the fetched info from my API duplicated multiple times over. This resulted in my app's homepage being >2 MB.
I refactored my app to only send one network request, and made sure that data is only put into the __NEXT_DATA__ tag once. The app now works.
For a while, I was simply storing the contents of my website in a s3 bucket and could access all pages via the full url just fine. I wanted to make my website more secure by adding an SSL so I created a CloudFront Distribution to point to my s3 bucket.
The site will load just fine, but if the user tries to refresh the page or if they try to access a page using the full url (i.e., www.example.com/home), they will receive an AccessDenied page.
I have a policy on my s3 bucket that restricts access to only the Origin Access Identity and index.html is set as my domain root object.
I am not understanding what I am missing.
To demo, feel free to visit my site.
You will notice how it redirects you to kurtking.me/home. To reproduce the error, try refreshing the page or access a page via full URL (i.e., kurtking.me/life)
Any help is much appreciated as I have been trying to wrap my head around this and search for answers for a few days now.
I have figured it out and wanted to post my solution in case anyone else runs into this issue.
The issue was due to Angular being a SPA (Single Page App) and me using an S3 bucket to store it. When you try to go to a specific page via url access, CloudFront will take (for example, /about) and go to your S3 bucket looking for that file. Because Angular is a SPA, that file doesn't technically exist in your S3 bucket. This is why I was getting the error.
What I needed to do to fix it
If you open your distribution in Cloudfront, you will see an 'Error Pages' tab. I had to add two 'Custom Error Responses' that handled 400 and 403. The details are the same for 400 and 403, so I only include a photo for 400. See below:
Basically, what is happening is you are telling Cloudfront that regardless of a 400 or 403 error, to redirect back to index.html, thus giving control to Angular to decide if it can go to the path or not. If you would like to serve the client a 400 or 403 error, you need to define these routes in Angular.
After setting up the two custom error responses, I deployed my cloudfront solutions and wallah, it worked!
I used the following article to help guide me to this solution.
The better way to solve this is to allow list bucket permission and add a 404 error custom rule for cloudfront to point to index.html. 403 errors are also returned by WAF, and will cause additional security headaches, if they are added for custom error handling to index.html. Hence, better to avoid getting 403 errors from S3 in the first place for angular apps.
If you have this problem using CDK you need to add this :
MyAmplifyApp.addCustomRule({
source: '</^[^.]+$|\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|ttf|map|json)$)([^.]+$)/>',
target: '/index.html',
status: amplify.RedirectStatus.REWRITE
});
The accepted answer seems to work but it does not seem like a good practice. Instead check if the cloudfront origin is set to S3 Bucket(in which the static files are) or the actual s3 url endpoint. It should be the s3 url endpoint and not the s3 bucket.
The url after endpoint should be the origin
Add a Cloudfront function to rewrite the requested uri to /index.html if it doesn't match a regex.
For example, if none of your SPA routes contain a "." (dot), you could do something like this:
function handler(event) {
var request = event.request
if (/^(?!.*\..*).*$/.test(request.uri)) {
request.uri = '/index.html'
}
return request
}
This gets around any kind of side effects you would get by redirecting 403 -> index.html. For example, if you use a WAF to restrict access by IP address, if you try to navigate to the website from a "bad IP", a 403 will be thrown, but with the previously suggested 403 -> index.html redirect, you'd still see index.html. With a cloudfront function, you wont.
For those who are trying to achieve this using terraform, you only need to add this to your CloudFront Configuration:
resource "aws_cloudfront_distribution" "cf" {
...
custom_error_response {
error_code = 403
response_code = 200
response_page_path = "/index.html"
}
...
}
Please check from the console which error are you getting. In my case I was getting a 403 forbidden error, and using the settings which are shown in the screenshot worked for me
I have a webstack that is currently serving both dynamic and static content. I want start serving the static content from AWS CloudFront without having to change all of the urls.
So I set up CloudFront to serve the static content from AWS S3 and the dynamic content from a custom url (current webstack). This worked however CloudFront was making the request to our webstack via a rewrite rule.
Is it possible for CloudFront to return a 302 redirect so that the request to my webstack would come from the users browser?
Unfortunately this is critical for our application to work properly.
I don't think it's possible, because CloudFront should follow 302 redirects and cache the result, rather than return the 302 redirect to the client.