{"id":13576485,"url":"https://github.com/sanfrancesco/prerendercloud-lambda-edge","last_synced_at":"2025-04-05T05:31:58.013Z","repository":{"id":23937643,"uuid":"99282579","full_name":"sanfrancesco/prerendercloud-lambda-edge","owner":"sanfrancesco","description":"Pre-render CloudFront with this Lambda@Edge function. Powered by https://headless-render-api.com (formerly named prerender.cloud from 2016 - 2022) ","archived":false,"fork":false,"pushed_at":"2024-07-10T17:47:32.000Z","size":347,"stargazers_count":206,"open_issues_count":4,"forks_count":35,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-11-05T12:33:44.104Z","etag":null,"topics":["cloudfront","lambda","lambda-edge","prerender","prerendering","server-side-rendering"],"latest_commit_sha":null,"homepage":"https://headless-render-api.com","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sanfrancesco.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-08-03T23:16:12.000Z","updated_at":"2024-08-23T08:58:09.000Z","dependencies_parsed_at":"2024-07-20T02:20:52.182Z","dependency_job_id":"5fcbf8b7-5dcc-42c8-807b-3ad7a2de706a","html_url":"https://github.com/sanfrancesco/prerendercloud-lambda-edge","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sanfrancesco%2Fprerendercloud-lambda-edge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sanfrancesco%2Fprerendercloud-lambda-edge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sanfrancesco%2Fprerendercloud-lambda-edge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sanfrancesco%2Fprerendercloud-lambda-edge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sanfrancesco","download_url":"https://codeload.github.com/sanfrancesco/prerendercloud-lambda-edge/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247294448,"owners_count":20915335,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudfront","lambda","lambda-edge","prerender","prerendering","server-side-rendering"],"created_at":"2024-08-01T15:01:10.651Z","updated_at":"2025-04-05T05:31:52.994Z","avatar_url":"https://github.com/sanfrancesco.png","language":"JavaScript","readme":"**Note: please first test your app** with our quick and simple all-in-one solution here: https://github.com/sanfrancesco/prerendercloud-server before going through this Lambda@Edge+CloudFront setup process. Once that's debugged and working the way you want it, come back here to configure Lambda@Edge+CloudFront. This will save you hours of headache because debugging/iterating with Cloudfront/Lambda/S3 is slow.\n\n# Pre-render CloudFront (via AWS Lambda@Edge)\n\n\u003cimg align=\"right\" src=\"https://cloud.githubusercontent.com/assets/22159102/21554484/9d542f5a-cdc4-11e6-8c4c-7730a9e9e2d1.png\"\u003e\n\n![Github Actions CI](https://github.com/sanfrancesco/prerendercloud-lambda-edge/actions/workflows/node.js.yml/badge.svg)\n\n4-minute YouTube video walk-through (2024-07-10 update: AWS UI/UX has slightly changed since video but the video is still accurate): [https://youtu.be/SsMNQ3EaNZ0](https://youtu.be/SsMNQ3EaNZ0)\n\nTL;DR:\n\n- step 1: put www files in s3 bucket\n- step 2: create cloudfront distribution pointing at s3 bucket\n- step 3: clone this repo, `npm install` and run: `CLOUDFRONT_DISTRIBUTION_ID=yourDistributionId make deploy` (set yourDistributionId to what was created in step 2)\n- step 4: wait ~5 minutes for aws systems to propagate (cloudfront url will show some error in the meantime)\n\nfull guidance below or in video:\n\n\u003ca href=\"http://www.youtube.com/watch?feature=player_embedded\u0026v=SsMNQ3EaNZ0\n\" target=\"_blank\"\u003e\u003cimg src=\"https://img.youtube.com/vi/SsMNQ3EaNZ0/sddefault.jpg\"\nalt=\"4min how-to set up lambda@edge for pre-rendering\" width=\"240\" height=\"180\" border=\"10\" /\u003e\n\u003c/a\u003e\u003cbr /\u003e\n\nServer-side rendering (pre-rendering) via Lambda@Edge for single-page apps hosted on CloudFront with an s3 origin. It forwards requests to [Headless-Render-API.com](https://headless-render-api.com) to be pre-rendered using a headless Chrome browser.\n\nThis is a [serverless](https://github.com/serverless/serverless) project with a `make deploy` command that:\n\n1. [serverless.yml](serverless.yml) deploys 3 functions to Lambda (`viewerRequest`, `originRequest`, `originResponse`)\n2. [deploy.js](deploy.js) associates them with your CloudFront distribution\n3. [create-invalidation.js](create-invalidation.js) clears/invalidates your CloudFront cache\n\nRead more:\n\n- [Headless-Render-API.com](https://headless-render-api.com) (formerly named prerender.cloud from 2016 - 2022)\n- [Dec, 2016 Lambda@Edge intro](https://aws.amazon.com/blogs/aws/coming-soon-lambda-at-the-edge/)\n- [Lambda@Edge docs](http://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html)\n- [CloudFront docs for Lambda@Edge](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-at-the-edge.html)\n\n#### 1. Prerequisites\n\n1. S3 bucket with index.html and JavaScript files\n2. CloudFront distribution pointing to that S3 bucket (that also has \\* read access to that bucket)\n\nStart with a new test bucket and CloudFront distribution before modifying your production account:\n\n(it'll be quick because you'll be using the defaults with just 1 exception)\n\n- S3 bucket in us-east-1 with default config (doesn't need to be public and doesn't need static web hosting)\n  - yes, us-east-1 makes things easier (using any other region will require a URL change for your CloudFront origin)\n- CloudFront distribution with S3 origin with default config except:\n  - (give CloudFront access to that bucket)\n    - \"Restrict Bucket Access\" = \"Yes\"\n    - \"Origin Access Identity\" = \"Create a New Identity\"\n    - \"Grant Read Permissions on Bucket\" = \"Yes, Update Bucket Policy\"\n    - (alternatively your S3 bucket [can be public - meaning an access policy that allows getObject on `*` for `*`](http://docs.aws.amazon.com/AmazonS3/latest/dev/example-bucket-policies.html#example-bucket-policies-use-case-2))\n  - recommend enabling \"automatic compression\"\n\nThat's all you need. Now just wait a few minutes for the CloudFront DNS to propogate.\n\nNote, you **will not be creating** a CloudFront \"custom error response\" that redirects 404s to index.html, and if you already have one, then remove it - because this project uploads a Lambda@Edge function that replaces that functionality (if you don't remove it, this project won't work).\n\n#### 2. Clone this repo\n\n`$ git clone https://github.com/sanfrancesco/prerendercloud-lambda-edge.git`\n\n#### 3. Install Dependencies\n\nNode v20 (it works with node as low as v12 but aws lambda requires latest version of nodejs), and npm\n\n`$ npm install`\n\n#### 4. Hardcode your headless-render-api.com auth token\n\nEdit [handler.js](handler.js) and set your headless-render-api.com API token (cmd+f for `prerenderToken`)\n\nnote: Headless-Render-API.com was previously known as Prerender.cloud (rebranded 2022-05-01)\n\n#### 5. Edit any other configs (optional)\n\ne.g. `botsOnly`, `removeTrailingSlash` in [handler.js](handler.js)\n\n#### 6. Remove CloudFront custom error response for 404-\u003eindex.html\n\n**(this step is only necessary if you are using an existing CloudFront distribution)**\n\nIf you're using an existing CloudFront distribution, you need to remove this feature.\n\nIt has to be removed because it prevents the execution of the viewer-request function. This project replicates that functionality (see caveats)\n\n1. go here: https://console.aws.amazon.com/cloudfront/home\n2. click on your CloudFront distribution\n3. click the \"error pages\" tab\n4. make note of the TTL settings (in case you need to re-create it)\n5. and delete the custom error response (because having the custom error response prevents the `viewer-request` function from executing).\n\n#### 7. Add `s3:ListBucket` permission to CloudFront user\n\n**(this step is only necessary if you want 404s to work)**\n\nSince we can't use the \"custom error response\", and we're implementing it ourselves, this permission is neccessary for CloudFront+Lambda@Edge to return a 404 for a requested file that doesn't exist (only non HTML files will return 404, see caveats below). If you don't add this, you'll get 403 forbidden instead.\n\n1. go to [s3 console](https://console.aws.amazon.com/s3/home?region=us-east-1)\n2. click on the bucket you created in step 1 for this project\n3. click \"permissions\"\n4. click \"bucket policy\"\n5. modify the Action and Resource to each be an array, they should look like (change the bucket name in resource as appropriate):\n\n```\n\"Action\": [\n    \"s3:GetObject\",\n    \"s3:ListBucket\"\n],\n\"Resource\": [\n    \"arn:aws:s3:::CHANGE_THIS_TO_YOUR_BUCKET_NAME_FROM_STEP_1/*\",\n    \"arn:aws:s3:::CHANGE_THIS_TO_YOUR_BUCKET_NAME_FROM_STEP_1\"\n]\n```\n\nIf you're not editing an IAM policy specifically, the UI/UX checkbox for this in the S3 interface is, for the bucket, under the \"Permissions\" tab, \"List Objects\"\n\nYou can modify the content of the 404 page in [handler.js](handler.js)\n\n#### 8. Lambda@Edge function Deployment (only needs to be done once, or whenever you `git pull` from this repo)\n\n1. Make sure there's a \"default\" section in your ~/.aws/credentials file with aws*access_key_id/aws_secret_access_key that have any of the following permissions: (full root, or see [serverless discussion](https://github.com/serverless/serverless/issues/1439) or you can use the following policies, which are \\_almost* root: [AWSLambdaFullAccess, AwsElasticBeanstalkFullAccess])\n2. now run: `$ CLOUDFRONT_DISTRIBUTION_ID=whateverYourDistributionIdIs make deploy`\n3. See the created Lambda function in Lambda: https://console.aws.amazon.com/lambda/home?region=us-east-1#/functions\n4. See the created Lambda function in CloudFront: (refresh it, click your distribution, then the behaviors tab, then the checkbox + edit button for the first item in the list, then scroll to bottom of that page to see \"Lambda Function Associations\")\n\n#### 9. Deployment (of your single-page application)\n\n1. sync/push the files to s3\n2. invalidate CloudFront\n3. you're done (no need to deploy the Lambda@Edge function after this initial setup)\n\ncaveat: note that headless-render-api.com has a 5-minute server cache that you can disable, see `disableServerCache` in [handler.js](/handler.js)\n\n#### 10. You're done!\n\nVisit a URL associated with your CloudFront distribution. **It will take a few seconds** for the first request (because it is pre-rendered on the first request). If for some reason the pre-render request fails or times out, the non-pre-rendered request will be cached.\n\n#### Viewing AWS Logs in CloudWatch\n\nSee logs in CloudWatch in region closest to where you made the request from (although the function is deployed to us-east-1, it is replicated in all regions).\n\nTo view logs from command line:\n\n1. use an AWS account with `CloudWatchLogsReadOnlyAccess`\n2. `$ pip install awslogs` ( https://github.com/jorgebastida/awslogs )\n   - `AWS_REGION=us-west-2 awslogs get -s '1h' /aws/lambda/us-east-1.Lambda-Edge-Prerendercloud-dev-viewerRequest`\n   - `AWS_REGION=us-west-2 awslogs get -s '1h' /aws/lambda/us-east-1.Lambda-Edge-Prerendercloud-dev-originRequest`\n   - (change `AWS_REGION` to whatever region is closest to where you physically are since that's where the logs will be)\n   - (FYI, for some reason, San Francisco based requests are ending up in us-west-2)\n\n#### Viewing Headless-Render-API.com logs\n\nSign in to headless-render-api.com web app and you'll see the last few requests made for your API key.\n\n#### Cleanup\n\n`$ make destroy` will attempt to remove the Lambda@Edge functions - but as of Nov 2017, AWS still doesn't allow deleting \"replicated functions\" - in which case, just unnassociate them from your CloudFront distribution until delete functionality works.\n\nThis also means if you attempt to delete and recreate the functions, it will fail - so you'll need to change the name in [serverless.yml](serverless.yml) and [deploy.js](deploy.js) (just append a v2)\n\nYou can also sign into AWS and go to CloudFormation and manually remove things.\n\n## Caveats\n\n1. If you can't tolerate a slow first request (where subsequent requests are served from cache in CloudFront):\n   - crawl before invalidating the CloudFront distrubtion - just hit all of the URLs with [service.headless-render-api.com](https://headless-render-api.com/docs/api) and configure a `prerender-cache-duration` of something longer than the default of 5 minutes (300) - like 1 week (604800).\n2. This solution will serve index.html in place of something like `/some-special-file.html` even if `/some-special-file.html` exists on your origin\n   - We're waiting for the Lambda@Edge to add a feature to address this\n   - in the meantime use the `blacklistPaths` option (see [handler.js](https://github.com/sanfrancesco/prerendercloud-lambda-edge/blob/ccd87b5484a4334d823dbb8f0df16e843b2dc910/handler.js#L81))\n3. Redirects (301/302 status codes)\n   - if you use `\u003cmeta name=\"prerender-status-code\" content=\"301\"\u003e` to initiate a redirect, your CloudFront TTL must be zero, otherwise CloudFront will cache the body/response and return status code 200 with the body from the redirected path\n\n## Updating Node.js runtime\n\nSimply update [serverless.yaml](./serverless.yml) to the [latest or whatever you prefer](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-requirements-limits.html#lambda-requirements-lambda-function-configuration) and re-deploy: `make deploy`\n\n## Troubleshooting\n\n- Read through the console output from the `make deploy` command and look for errors\n- Check your user-agent if using botsOnly\n- Sometimes (rarely) you'll see an error message on the webpage itself.\n- Check the AWS logs (see section \"Viewing AWS Logs in CloudWatch\")\n- Check headless-render-api.com logs (see section \"Viewing headless-render-api.com logs\")\n- Sometimes (rarely) there's an actual problem with AWS Lambda and you [may just need to re-deploy](https://www.reddit.com/r/aws/comments/7gumv7/question_aws_lambda_nodejs610_environment_issue/)\n","funding_links":[],"categories":["JavaScript","AWS Lambda Functions"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsanfrancesco%2Fprerendercloud-lambda-edge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsanfrancesco%2Fprerendercloud-lambda-edge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsanfrancesco%2Fprerendercloud-lambda-edge/lists"}