
<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>Tue, 14 Apr 2026 15:06:19 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Introducing Cf-Terraforming]]></title>
            <link>https://blog.cloudflare.com/introducing-cf-terraform/</link>
            <pubDate>Fri, 15 Feb 2019 20:02:04 GMT</pubDate>
            <description><![CDATA[ Ever since we implemented support for configuring Cloudflare via Terraform, we’ve been steadily expanding the set of features and services you can manage via this popular open-source tool.  ]]></description>
            <content:encoded><![CDATA[ <p></p><p>Ever since <a href="/getting-started-with-terraform-and-cloudflare-part-1/">we implemented support for configuring Cloudflare via Terraform</a>, we’ve been steadily expanding the set of features and services you can manage via this popular open-source tool.</p><p>If you're unfamiliar with how Terraform works with Cloudflare, check out <a href="https://developers.cloudflare.com/terraform/">our developer docs</a>.</p><p>We are Terraform users ourselves, and we believe in the stability and reproducibility that can be achieved by defining your infrastructure as code.</p>
    <div>
      <h2>What is Terraform?</h2>
      <a href="#what-is-terraform">
        
      </a>
    </div>
    <p><a href="https://www.terraform.io/">Terraform</a> is an open-source tool that allows you to describe your infrastructure and cloud services (think virtual machines, servers, databases, network configurations, Cloudflare API resources, and more) as human-readable configurations.</p><p>Once you’ve done this, you can run the Terraform command-line tool and it will figure out the difference between your desired state and your current state, and make the API calls in the background necessary to reconcile the two.</p><p>Unlike other solutions, Terraform does not require you to run software on your hosts, and instead of spending time manually configuring machines, creating DNS records, and specifying Page Rules, you can simply run:</p>
            <pre><code>$ terraform apply</code></pre>
            <p>and the state described in your configuration files will be built for you.</p>
    <div>
      <h2>Enter Cloudflare Terraforming</h2>
      <a href="#enter-cloudflare-terraforming">
        
      </a>
    </div>
    <p>Terraform is a tremendous time-saver once you have your configuration files in place, but what do you do if you’re already a Cloudflare user and you need to convert your particular setup, records, resources and rules into Terraform config files in the first place?</p><p>Today, we’re excited to share a <a href="https://github.com/cloudflare/cf-terraforming">new open-source utility</a> to make the migration of even complex Cloudflare configurations into Terraform simple and fast.</p><p>It’s called <a href="https://github.com/cloudflare/cf-terraforming">cf-terraforming</a> and it downloads your Cloudflare setup, meaning everything you’ve defined via the Cloudflare dashboard and API, into Terraform-compliant configuration files in a few commands.</p>
    <div>
      <h2>Getting up and running quickly</h2>
      <a href="#getting-up-and-running-quickly">
        
      </a>
    </div>
    <p>Cf-terraforming is open-source and <a href="https://github.com/cloudflare/cf-terraforming">available on Github now</a>. You need a working <a href="https://golang.org/doc/install">Golang installation</a> and a <a href="https://dash.cloudflare.com/sign-up">Cloudflare account</a> with some resources defined. That’s it!</p><p>Let’s first install cf-terraforming, while also pulling down all dependencies and updating them as necessary:</p>
            <pre><code>$ go get -u github.com/cloudflare/cf-terraforming/...</code></pre>
            <p>Cf-terraforming is a command line tool that you invoke with your Cloudflare credentials, some zone information and the resource type that you want to export. The output is a valid Terraform configuration file describing your resources.</p><p>To use cf-terraforming, first <a href="https://support.cloudflare.com/hc/en-us/articles/200167836-Where-do-I-find-my-Cloudflare-API-key-">get your API key</a> and Account ID from the Cloudflare dashboard. You can find your account id at the bottom right of the overview page for any zone in your account. It also has a quick link to get your API key as well. You can store your key and account ID in environment variables to make it easier to work with the tool:</p>
            <pre><code>export CLOUDFLARE_TOKEN=”&lt;your-key&gt;” 
export CLOUDFLARE_EMAIL=”&lt;your-email&gt;”
export CLOUDFLARE_ACCT_ID=”&lt;your-id&gt;” </code></pre>
            <p>Cf-terraforming can create configuration files for any of the resources currently available in <a href="https://www.terraform.io/docs/providers/cloudflare/index.html">the official Cloudflare Terraform provider</a>, but sometimes it’s also handy to export individual resources as needed.</p><p>Let’s say you’re migrating your Cloudflare configuration to Terraform and you want to describe your Spectrum applications. You simply call cf-terraforming with your credentials, zone, and the spectrum_application command, like so:</p>
            <pre><code>go run cmd/cf-terraforming/main.go --email $CLOUDFLARE_EMAIL --key $CLOUDFLARE_TOKEN --account $CLOUDFLARE_ACCT_ID spectrum_application</code></pre>
            <p>Cf-terraforming will contact the Cloudflare API on your behalf and define your resources in a format that Terraform understands:</p>
            <pre><code>resource"cloudflare_spectrum_application""1150bed3f45247b99f7db9696fffa17cbx9" {    
    protocol = "tcp/8000"    
    dns = {        
        type = "CNAME"        
        name = "example.com"    
    }    
    ip_firewall = "true"    
    tls = "off"    
    origin_direct = [ "tcp://37.241.37.138:8000", ]
}</code></pre>
            <p>You can redirect the output to a file and then start working with Terraform. First, ensure you are in the cf-terraforming directory, then run:</p>
            <pre><code>go run cmd/cf-terraforming/main.go --email $CLOUDFLARE_EMAIL --key $CLOUDFLARE_TOKEN --account $CLOUDFLARE_ACCT_ID spectrum_application &gt; my_spectrum_applications.tf </code></pre>
            <p>The same goes for Zones, DNS records, Workers scripts and routes, security policies and more.</p>
    <div>
      <h2>Download all Cloudflare resources</h2>
      <a href="#download-all-cloudflare-resources">
        
      </a>
    </div>
    <p>Use the <b>all</b> command to download everything and convert it into Terraform config.</p>
            <pre><code>go run cmd/cf-terraforming/main.go --email $CLOUDFLARE_EMAIL --key $CLOUDFLARE_TOKEN --account $CLOUDFLARE_ACCT_ID all</code></pre>
            
    <div>
      <h2>Which resources are supported?</h2>
      <a href="#which-resources-are-supported">
        
      </a>
    </div>
    <p>Currently, <a href="https://github.com/cloudflare/cf-terraforming">cf-terraforming</a> supports every resource type that you can manage via the official <a href="https://www.terraform.io/docs/providers/cloudflare/index.html">Cloudflare Terraform provider</a>:</p><ul><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/access_application.html">access_application</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/access_rule.html">access_rule</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/access_policy.html">access_policy</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/account_member.html">account_member</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/custom_pages.html">custom_pages</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/filter.html">filter</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/firewall_rule.html">firewall_rule</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/load_balancer.html">load_balancer</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/load_balancer_pool.html">load_balancer_pool</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/load_balancer_monitor.html">load_balancer_monitor</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/page_rule.html">page_rule</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/rate_limit.html">rate_limit</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/record.html">record</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/spectrum_application.html">spectrum_application</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/waf_rule.html">waf_rule</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/worker_route.html">worker_route</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/worker_script.html">worker_script</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/zone.html">zone</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/zone_lockdown.html">zone_lockdown</a></p></li><li><p><a href="https://www.terraform.io/docs/providers/cloudflare/r/zone_settings_override.html">zone_settings_override</a></p></li></ul>
    <div>
      <h2>Get involved</h2>
      <a href="#get-involved">
        
      </a>
    </div>
    <p>We’re looking for feedback and any issues you might encounter while getting up and running with cf-terraforming. Please open any issues against <a href="https://github.com/cloudflare/cf-terraforming">the GitHub repo</a>.</p><p>Cf-terraforming is open-source, so if you want to get involved feel free to pick up an open issue or make a pull request.</p>
    <div>
      <h2>Looking forward</h2>
      <a href="#looking-forward">
        
      </a>
    </div>
    <p>We’ll continue to expand <a href="https://www.terraform.io/docs/providers/cloudflare/index.html">the set of Cloudflare resources that you can manage via Terraform</a>, and that you can export via cf-terraforming. Be sure to keep and eye on the <a href="https://github.com/cloudflare/cf-terraforming">cf-terraforming repo</a> for updates.</p> ]]></content:encoded>
            <category><![CDATA[Terraform]]></category>
            <category><![CDATA[Programming]]></category>
            <category><![CDATA[API]]></category>
            <category><![CDATA[Developers]]></category>
            <guid isPermaLink="false">2H5PM4gKSBPg3AesoilW5y</guid>
            <dc:creator>Zack Proser</dc:creator>
        </item>
        <item>
            <title><![CDATA[Dogfooding Cloudflare Workers]]></title>
            <link>https://blog.cloudflare.com/dogfooding-edge-workers/</link>
            <pubDate>Fri, 04 May 2018 16:20:00 GMT</pubDate>
            <description><![CDATA[ We deployed a Cloudflare worker in front of www.cloudflare.com and api.cloudflare.com, that has since served over <X> million requests. Here's what we learned about debugging and "failing open" when writing workers. ]]></description>
            <content:encoded><![CDATA[ <p>On the WWW team, we’re responsible for Cloudflare’s REST APIs, account management services and the dashboard experience. We take security and <a href="https://www.cloudflare.com/learning/privacy/what-is-pci-dss-compliance/">PCI compliance</a> seriously, which means we move quickly to stay up to date with regulations and relevant laws.</p><p>A <a href="/deprecating-old-tls-versions-on-cloudflare-dashboard-and-api/">recent compliance project</a> had a requirement of detecting certain end user request data at the edge, and reacting to it both in API responses as well as visually in the dashboard. We realized that this was an excellent opportunity to dogfood Cloudflare Workers.</p>
    <div>
      <h3>Deploying workers to <a href="http://www.cloudflare.com">www.cloudflare.com</a> and api.cloudflare.com</h3>
      <a href="#deploying-workers-to-and-api-cloudflare-com">
        
      </a>
    </div>
    <p>In this blog post, we’ll break down the problem we solved using a single worker that we shipped to multiple hosts, share the annotated source code of our worker, and share some best practices and tips and tricks we discovered along the way.</p><p>Since being deployed, <b>our worker has served over 400 million requests</b> for both calls to api.cloudflare.com and the <a href="http://www.cloudflare.com">www.cloudflare.com</a> dashboard.</p>
    <div>
      <h3>The task</h3>
      <a href="#the-task">
        
      </a>
    </div>
    <p>First, we needed to detect when a client was connecting to our services using an outdated TLS protocol. Next, we wanted to pass this information deeper into our application stack so that we could act upon it and conditionally decorate our responses with notices providing a heads up about the imminent changes.</p><p>Our Edge team was quick to create a patch to capture TLS connection information for every incoming request, but how would we propagate it to our application layer where it could be acted upon?</p>
    <div>
      <h3>The solution</h3>
      <a href="#the-solution">
        
      </a>
    </div>
    <p>The Workers team made a modification to the core platform to ingest the TLS protocol version data sent from the edge, making it available in the workers environment as a property of the cf object available to the worker Javascript context (You can now use this property in your own workers).</p><p>With our workers able to inspect the TLS protocol versions of requests, we needed only to append a custom HTTP header containing this information before forwarding them into our application layer.</p><p>Our APIs use this data to add deprecation warnings to responses, and our UI uses it to display banners explaining the upcoming changes.</p>
            <figure>
            
            <img src="https://cf-assets.www.cloudflare.com/zkvhlag99gkb/1TlooYc7slpilLJyFkXxD5/313021631d31baa4c712bf48b7647fd6/tls-deprecation-banner.png" />
            
            </figure><p>Let's now take a look at the source code for our worker.</p>
    <div>
      <h3>Anatomy of a fail open worker</h3>
      <a href="#anatomy-of-a-fail-open-worker">
        
      </a>
    </div>
    
            <pre><code>/**
 * Cloudflare workers implement the service worker spec
 * See: https://developers.cloudflare.com/workers/about/ for an intro
 *
 * Binding an event handler to the fetch event allows your worker to intercept a request for your zone
 */
addEventListener('fetch', event =&gt; {
    /**
     * In the event of an uncaught exception, fail open as if the  worker did not exist
     * If you're not sure what you're doing, it's recommended you include this call
     * as the very first thing your worker does in its fetch event listener
     *
     * If you do not include this call, but your worker encounters an uncaught exception
     * while processing your request, your user will see an edge-level error page
     * instead of a response from your site, app or API
     *
     * Read on below for more info on deciding whether to
     * fail open or fail closed in your workers
     */
    event.passThroughOnException()
    //This allows you to return your own Response object from your worker
    event.respondWith(requestWithTLSHeader(event))
})
/**
 * Calls out to our Sentry account to create an exception event
 *
 * Note that for this to work properly with the event.waitUntil() call in the
 * exception block within requestWithTLSHeader, this function must return a promise
 *
 * @returns {Promise}
 */
function promisifiedSentryLog(ex) {
    //Change these constants to your own Sentry values if you want to use this script
    const sentryProjectId = '&lt;Replace-Me-With-Your-Sentry-Project-Id&gt;'
    const sentryAPIKey = '&lt;Replace-Me-With-Your-Sentry-API-Key&gt;'
    const sentrySecretKey = '&lt;Replace-Me-With-Your-Sentry-Secret-Key&gt;'
    //Manually configure our call to Sentry
    let b = {
        project: sentryProjectId,
        logger: "javascript",
        platform: "javascript",
        exception: {
            values: [
                { type: "Error", value: ((ex) &amp;&amp; (ex.message)) ? ex.message : 'Unknown' }
            ]
        }
    }
    let sentryUrl = `https://sentry.io/api/${sentryProjectId}/store/?sentry_version=7&amp;sentry_client=raven-js%2F3.24.2&amp;sentry_key=${sentryAPIKey}&amp;sentry_secret=${sentrySecretKey}`
    /**
     * Fire off a POST request to Sentry's API, which includes our project
     * and credentials information, plus arbitrary logging data
     *
     * In this case, we're passing along the exception message,
     * but you could use this pattern to log anything you want
     *
     * Keep in mind that fetch returns a promise,
     * which is what makes this function compatible with event.waitUntil
     */
    return fetch(sentryUrl, { body: JSON.stringify(b), method: 'POST' })
}
/**
 * Creates a new request for the backend that mirrors the incoming request,
 * with the addition of a new header that specifies which TLS version was used
 * in the connection to the edge
 *
 * This is the main function that contains the core logic for this worker
 *
 * It works by checking for the presence of a property 'tlsVersion' that is being forwarded
 * from the edge into the workers platform so that worker scripts can access it
 *
 * The worker starts with a default TLS header. If the tlsVersion property,
 * which represents the version of the TLS protocol the client connected with,
 * is present, the worker sets its local tlsVersion variable to the value of this property
 *
 * It then wraps the incoming request headers in a new headers object,
 * which enables us to append our own custom X-Client-SSL-Protocol header
 *
 * The worker then forwards the original request
 * (overriding the headers with our new headers object) to the origin
 *
 * Now, our application layer can act upon this information
 * to show modals and include deprecation warnings as necessary
 *
 * @returns {Promise}
 */
async function requestWithTLSHeader(event) {
    //It's strongly recommended that you wrap your core worker logic in a try / catch block
    try {
        let tlsVersion = "NONE"
        //Create a new Headers object that includes the original request's headers
        let reqHeaders = new Headers(request.headers)
        if (event &amp;&amp; event.request &amp;&amp; event.request.cf &amp;&amp; event.request.cf.tlsVersion &amp;&amp; typeof event.request.cf.tlsVersion === "string" &amp;&amp; event.request.cf.tlsVersion !== "") {
            tlsVersion = event.request.cf.tlsVersion
        }
        //Add our new header
        reqHeaders.append('X-Client-SSL-Protocol', tlsVersion)
        //Extend the original request's headers with our own, but otherwise fetch the original request
        return await fetch(event.request, { headers: reqHeaders })
    } catch (ex) {
        /**
         * Signal the runtime that it should wait until the promise resolves
         *
         * This avoids race conditions where the runtime stops execution before
         * our async Sentry task completes
         *
         * If you do not do this, the passthrough subrequest will race
         * your pending asychronous request to Sentry, and you will
         * miss many events / fail to capture them correctly
         */
        event.waitUntil(promisifiedSentryLog(ex))
        /**
         * Intentionally throw the exception in order to trigger the pass-through
         * behavior defined by event.passThroughOnException()
         *
         * This means that our worker will fail open - and not block requests
         * to our backend services due to unexpected exceptions
         */
        throw ex
    }
}</code></pre>
            <p>The above Workers script was updated on 5/17/18 to correct how the Sentry's API works.</p>
    <div>
      <h3>Asynchrony, logging and alerts via Sentry</h3>
      <a href="#asynchrony-logging-and-alerts-via-sentry">
        
      </a>
    </div>
    <p>We decided to use Sentry to capture events we sent from our worker, but you could follow this same pattern with any similar service.</p><p>The critical piece to making this work is understanding that you must signal the Cloudflare worker runtime that it needs to wait upon your asynchronous logging subrequest (and not cancel it).</p><p>You do this by:</p><ol><li><p>Ensuring that your logging function returns a promise (what your promise resolves to does not matter)</p></li><li><p>Wrapping your call to your logging function in event.waitUntil as we have done above</p></li></ol><p>This pattern fixes a common race condition: if you don't leverage event.waitUntil, the runtime will race the passthrough subrequest and your logging subrequest.</p><p>If the passthrough subrequest completes significantly faster than your logging subrequest, the logging request could be cancelled. In practice, you'll notice this issue manifesting as dropped logging messages - whether or not a given exception will be logged properly becomes a roll of the dice on every request.</p><p>For additional insight, check out our <a href="https://developers.cloudflare.com/workers/writing-workers/debugging-tips/">official guide to debugging Cloudflare workers</a>.</p>
    <div>
      <h3>Failing open to ensure service continuity</h3>
      <a href="#failing-open-to-ensure-service-continuity">
        
      </a>
    </div>
    <p>A key consideration when designing your worker is failure behavior. Depending on what your particular worker is accomplishing, you either want it to fail open or failed closed. Failing open means that if something goes horribly wrong, the original request will be passed through as if your worker did not exist, while failing closed means that a request that raises an exception in your worker will not be processed further.</p><p>If you are editing metadata, collecting metrics, or adding new non-critical HTTP headers, to name a few examples, you probably don't want an unhandled exception in your worker to prevent the request from being serviced.</p><p>In this case, you can leverage <b>event.passThroughOnException</b> as we have above, and it's recommended that you call this method in the first line of your fetch event handler. This sets a flag that the Cloudflare worker request handler can inspect in case of an exception to determine the desired passthrough behavior.</p><p>On the other hand, if you're employing your worker in a security-centric role, tasking it with blocking malicious requests from nefarious bots, or blocking access to sensitive endpoints when valid security credentials are not supplied, you may want your worker to fail closed. This is the default behavior, and it will prevent requests that raise exceptions in your worker from being processed further.</p><p>Our services are in the critical path for our customers, yet our use case was to conditionally add a new HTTP header, so we knew we wanted to fail open.</p>
    <div>
      <h3>Having your worker generate PagerDuty and Hipchat alerts</h3>
      <a href="#having-your-worker-generate-pagerduty-and-hipchat-alerts">
        
      </a>
    </div>
    <p>Now that you've done the work to get data from your worker logged to Sentry, you can leverage Sentry's PagerDuty integration to configure alerts for exceptions that occur in your workers.</p><p>This will increase your team's confidence in your worker-based solutions, and alert you immediately to any new issues that occur in production.</p>
    <div>
      <h3>Share your worker recipes</h3>
      <a href="#share-your-worker-recipes">
        
      </a>
    </div>
    <p>You can find additional worker recipes and examples in our <a href="https://developers.cloudflare.com/workers/about/">official documentation</a>.</p><p>Have you written a worker that you'd like to share? <a href="#">Send it to us</a> and you might get featured on our blog or added to our <a href="https://developers.cloudflare.com/workers/recipes/a-b-testing/">Cloudflare worker recipe collection</a> with a credit.</p> ]]></content:encoded>
            <category><![CDATA[JavaScript]]></category>
            <category><![CDATA[API]]></category>
            <category><![CDATA[Serverless]]></category>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[Developers]]></category>
            <category><![CDATA[Developer Platform]]></category>
            <guid isPermaLink="false">6V3yF64ARlzJBFHFso2V4L</guid>
            <dc:creator>Zack Proser</dc:creator>
        </item>
    </channel>
</rss>