├── README.md ├── assets ├── Cloudflare_add_route.png ├── Cloudflare_dashboard.png ├── Cloudflare_replace_values.png └── Cloudflare_worker_editor.png └── index.js /README.md: -------------------------------------------------------------------------------- 1 | # Prerender Cloudflare worker 2 | 3 | This worker sample code lets users who host their website code on static hosting services (such as AWS S3, GitHub Pages...etc) and use Cloudflare for content delivery to use Prerender to prerender their sites for search engines and other bots. 4 | 5 | ## Installation 6 | 7 | The install guide assumes that you already have a site hosted through Cloudflare. 8 | 9 | 1. Select your site on the Cloudflare dashboard. 10 | ![Cloudflare dashboard](./assets/Cloudflare_dashboard.png) 11 | 12 | 2. Navigate through `Workers -> Manage workers -> Create a worker`. 13 | 14 | 3. Copy and paste the content of index.js over the code editor on the left hand side, overwriting its current content. Please note the name of the worker for later reference(top-left corner). 15 | ![Cloudflare dashboard](./assets/Cloudflare_worker_editor.png) 16 | 17 | 4. Replace your `API_KEY` value with your actual API key from the Prerender dashboard, and edit the `PRERENDERED_DOMAINS` array to match your site configuration. 18 | ![Cloudflare dashboard](./assets/Cloudflare_replace_values.png) 19 | 20 | > Note: Do not send a test request yet, as the site mapping is not yet done. We'll do that later. 21 | 22 | 5. Click `Save and Deploy` and navigate back to your workers as explained in step 2, but instead of clicking `Manage workers`, click `Add route`. 23 | 24 | 6. Edit your route to match your site config (e.g. `example.com/*` to cover your whole site on the Apex domain, or `*.example.com/*` if you want to cover all subdomains too). 25 | ![Cloudflare dashboard](./assets/Cloudflare_add_route.png) 26 | 27 | 7. Click save and you should be good to go! 28 | 29 | > Note: Optionally you can now go back to test or debug your worker as shown in step 3, but remember to replace the entire workker URL with your actual site URL (or add your worker URL to the `PRERENDERED_DOMAINS` array). 30 | -------------------------------------------------------------------------------- /assets/Cloudflare_add_route.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prerender/prerender-cloudflare-worker/62e2ad1dab374e59a78b539efdee54bfd4f63e6a/assets/Cloudflare_add_route.png -------------------------------------------------------------------------------- /assets/Cloudflare_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prerender/prerender-cloudflare-worker/62e2ad1dab374e59a78b539efdee54bfd4f63e6a/assets/Cloudflare_dashboard.png -------------------------------------------------------------------------------- /assets/Cloudflare_replace_values.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prerender/prerender-cloudflare-worker/62e2ad1dab374e59a78b539efdee54bfd4f63e6a/assets/Cloudflare_replace_values.png -------------------------------------------------------------------------------- /assets/Cloudflare_worker_editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/prerender/prerender-cloudflare-worker/62e2ad1dab374e59a78b539efdee54bfd4f63e6a/assets/Cloudflare_worker_editor.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Basic settings 3 | */ 4 | 5 | // The API key visible on your Prerender dashboard. 6 | const API_KEY = 'xxx'; 7 | 8 | // The domains that you want to be prerendered. 9 | const PRERENDERED_DOMAINS = [ 10 | 'example.com' 11 | ]; 12 | 13 | /** 14 | * Advanced settings 15 | */ 16 | 17 | // These are the user agents that the worker will look for to 18 | // initiate prerendering of the site. 19 | const BOT_AGENTS = [ 20 | 'googlebot', 21 | 'yahoo! slurp', 22 | 'bingbot', 23 | 'yandex', 24 | 'baiduspider', 25 | 'facebookexternalhit', 26 | 'twitterbot', 27 | 'rogerbot', 28 | 'linkedinbot', 29 | 'embedly', 30 | 'quora link preview', 31 | 'showyoubot', 32 | 'outbrain', 33 | 'pinterest/0.', 34 | 'developers.google.com/+/web/snippet', 35 | 'slackbot', 36 | 'vkshare', 37 | 'w3c_validator', 38 | 'redditbot', 39 | 'applebot', 40 | 'whatsapp', 41 | 'flipboard', 42 | 'tumblr', 43 | 'bitlybot', 44 | 'skypeuripreview', 45 | 'nuzzel', 46 | 'discordbot', 47 | 'google page speed', 48 | 'qwantify', 49 | 'pinterestbot', 50 | 'bitrix link preview', 51 | 'xing-contenttabreceiver', 52 | 'chrome-lighthouse', 53 | 'telegrambot', 54 | 'google-inspectiontool' 55 | ]; 56 | 57 | // These are the extensions that the worker will skip prerendering 58 | // even if any other conditions pass. 59 | const IGNORE_EXTENSIONS = [ 60 | '.js', 61 | '.css', 62 | '.xml', 63 | '.less', 64 | '.png', 65 | '.jpg', 66 | '.jpeg', 67 | '.gif', 68 | '.pdf', 69 | '.doc', 70 | '.txt', 71 | '.ico', 72 | '.rss', 73 | '.zip', 74 | '.mp3', 75 | '.rar', 76 | '.exe', 77 | '.wmv', 78 | '.doc', 79 | '.avi', 80 | '.ppt', 81 | '.mpg', 82 | '.mpeg', 83 | '.tif', 84 | '.wav', 85 | '.mov', 86 | '.psd', 87 | '.ai', 88 | '.xls', 89 | '.mp4', 90 | '.m4a', 91 | '.swf', 92 | '.dat', 93 | '.dmg', 94 | '.iso', 95 | '.flv', 96 | '.m4v', 97 | '.torrent', 98 | '.woff', 99 | '.ttf', 100 | '.svg', 101 | '.webmanifest' 102 | ]; 103 | 104 | /** 105 | * This attaches the event listener that gets invoked when CloudFlare receives 106 | * a request. 107 | */ 108 | addEventListener('fetch', event => { 109 | const { request } = event; 110 | const url = new URL(request.url); 111 | const { hostname } = url; 112 | const requestUserAgent = (request.headers.get('User-Agent') || '').toLowerCase(); 113 | const xPrerender = request.headers.get('X-Prerender'); 114 | const pathName = url.pathname.toLowerCase(); 115 | const ext = pathName.substring(pathName.lastIndexOf('.') || pathName.length); 116 | 117 | if ( 118 | !xPrerender 119 | && containsOneOfThem(BOT_AGENTS, requestUserAgent) 120 | && !isOneOfThem(IGNORE_EXTENSIONS, ext) 121 | && isOneOfThem(PRERENDERED_DOMAINS, hostname) 122 | ) { 123 | event.respondWith(prerenderRequest(request)) 124 | } 125 | }) 126 | 127 | /** 128 | * Helper function to check if an array contains an exact match for an element or not. 129 | * 130 | * @param {string[]} array - The array to check. 131 | * @param {string} element - The element to check if the array contains. 132 | * @returns {boolean} 133 | */ 134 | function isOneOfThem(array, element) { 135 | return array.some(e => e === element); 136 | } 137 | 138 | /** 139 | * Helper function to check if an array contains an element or not. 140 | * 141 | * @param {string[]} array - The array to check. 142 | * @param {string} element - The element to check if the array contains. 143 | * @returns {boolean} 144 | */ 145 | function containsOneOfThem(array, element) { 146 | return array.some(e => element.indexOf(e) !== -1); 147 | } 148 | 149 | /** 150 | * Function to request the prerendered version of a request. 151 | * 152 | * @param {Request} request - The request received by CloudFlare 153 | * @returns {Promise} 154 | */ 155 | function prerenderRequest(request) { 156 | const { url, headers } = request; 157 | const prerenderUrl = `https://service.prerender.io/${url}`; 158 | const headersToSend = new Headers(headers); 159 | 160 | headersToSend.set('X-Prerender-Token', API_KEY); 161 | 162 | const prerenderRequest = new Request(prerenderUrl, { 163 | headers: headersToSend, 164 | redirect: 'manual', 165 | }); 166 | 167 | return fetch(prerenderRequest); 168 | } 169 | --------------------------------------------------------------------------------