<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Dan Carroll | Blog | Django</title>
    <link>https://dancarroll.org/blog/tags/django/</link>
    <description>Recent content in Django on Dan Carroll</description>
    <language>en-us</language>
    <copyright>Dan Carroll</copyright>
    <lastBuildDate>Tue, 15 Feb 2011 23:25:46 -0500</lastBuildDate>
    <atom:link href="https://dancarroll.org/blog/tags/django/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Customizing error code for Cloudflare mTLS cert check</title>
      <link>https://dancarroll.org/blog/2026/01/customizing-error-code-cloudflare-mtls/</link>
      <pubDate>Fri, 30 Jan 2026 00:20:00 -0500</pubDate>
      <author>Dan Carroll</author>
      <guid>https://dancarroll.org/blog/2026/01/customizing-error-code-cloudflare-mtls/</guid>
      <description>&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;&#xA;&lt;p&gt;The &lt;a href=&#34;https://bitwarden.com/&#34;&gt;Bitwarden&lt;/a&gt; mobile app wipes its local cache when&#xA;receiving an HTTP 403 error. By default, a WAF rule in a Cloudflare free account&#xA;can only return a 403. This guide shows how to use Cloudflare Workers to validate&#xA;mTLS certificates and return an HTTP 404 instead.&lt;/p&gt;&#xA;&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;&#xA;&lt;p&gt;For accessing internal services remotely, I have been a big fan of&#xA;&lt;a href=&#34;https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/&#34;&gt;Cloudflare Tunnels&lt;/a&gt;.&#xA;This system provides an easy mechanism to provide&#xA;access without opening up firewall ports, and the ability to take&#xA;advantage of Cloudflare security controls like their&#xA;&lt;a href=&#34;https://developers.cloudflare.com/waf/&#34;&gt;Web Application Firewall (WAF)&lt;/a&gt;&#xA;and &lt;a href=&#34;https://developers.cloudflare.com/cloudflare-one/access-controls/&#34;&gt;Zero Trust access control&lt;/a&gt;.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h2 id="summary">Summary</h2>
<p>The <a href="https://bitwarden.com/">Bitwarden</a> mobile app wipes its local cache when
receiving an HTTP 403 error. By default, a WAF rule in a Cloudflare free account
can only return a 403. This guide shows how to use Cloudflare Workers to validate
mTLS certificates and return an HTTP 404 instead.</p>
<h2 id="background">Background</h2>
<p>For accessing internal services remotely, I have been a big fan of
<a href="https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/">Cloudflare Tunnels</a>.
This system provides an easy mechanism to provide
access without opening up firewall ports, and the ability to take
advantage of Cloudflare security controls like their
<a href="https://developers.cloudflare.com/waf/">Web Application Firewall (WAF)</a>
and <a href="https://developers.cloudflare.com/cloudflare-one/access-controls/">Zero Trust access control</a>.</p>
<h2 id="previous-authorization-model">Previous authorization model</h2>
<p>Up until recently, I have secured my internal applications with a
simple OAuth identity validation through Zero Trust. Depending on the identity
provider used, this can provide a significant amount of protection, but it
can cause issues with non-web based applications like mobile apps.</p>
<p>One simple alternative I&rsquo;ve used is <a href="https://developers.cloudflare.com/cloudflare-one/access-controls/service-credentials/service-tokens/">Service Tokens</a>, which can be provided
via custom HTTP headers &ndash; if the app I&rsquo;m trying to use supports that type of
customization. However, this is quite rare, so I started looking for a more
universal approach.</p>
<h2 id="mtls-authorization">mTLS authorization</h2>
<p>Historically, during client/server communication, the focus has been on
ensuring the server endpoint is trustworthy, and where SSL/TLS is a standard
requirement. There are many use cases where the server may need to assess the
trustworthiness of the device communicating with it. For a very sensitive system like
a password manager, validating the device in addition to the user credentials provides
an extra layer of security.</p>
<p>Mutual TLS (mTLS) is a process that provides the missing validation, by having the
client provide its certificate to the server (after the client has validated the
server&rsquo;s certificate).</p>
<h2 id="cloudflare-mtls-support-on-free-plans">Cloudflare mTLS support on free plans</h2>
<p>I am currently using Cloudflare&rsquo;s free plan, as the pricing for their Pro and
Business plans is quite lofty for a single household use case.</p>
<p>With the free plan, you can create certificates via the Cloudflare dashboard,
which ends up creating a certificate signed by a
<a href="https://developers.cloudflare.com/ssl/client-certificates/#how-it-works">Cloudflare-managed, account-level root CA</a>. Then, the client certificate validation can be referenced in a
<a href="https://developers.cloudflare.com/learning-paths/mtls/mtls-app-security/#3-validate-the-client-certificate-in-the-waf">WAF rule</a>.</p>
<p>However, there are some important limitations here:</p>
<ul>
<li>You cannot bring your own CA, which means the certificates cannot be used in
Zero Trust access controls.</li>
<li>You cannot get the cert for your individual Cloudflare-managed CA, which means you cannot
bundle it into the client certificate, which is a requirement to use it some
situations (like <a href="https://android.stackexchange.com/questions/252812/how-to-get-chrome-or-any-browser-to-present-a-tls-client-certificate/252927#252927">Chrome on Android</a>).</li>
</ul>
<p>Another limitation, not strictly related to mTLS certs, is that the Cloudflare WAF rules
will always return HTTP 403 when they block traffic. Customizing the response, including
the response code, is <a href="https://android.stackexchange.com/questions/252812/how-to-get-chrome-or-any-browser-to-present-a-tls-client-certificate/252927#252927">limited to Pro plans</a>
(and above).</p>
<h2 id="need-for-customizing-the-response-code">Need for customizing the response code</h2>
<p>I have a password manager application (Bitwarden) that caches passwords locally, which is a
helpful feature when I don&rsquo;t have an Internet connection. However, this app will always try
to sync passwords if it can. As a security mechanism, if it is able to reach the server
(Vaultwarden, in my case), and the server returns a 401 or 403 response code, the app will
immediately clear the local cache.</p>
<p>I encountered this scenario when testing my mTLS configuration. I immediately thought of
future situations where this could cause significant distress &ndash; let&rsquo;s say I&rsquo;m outside my
network and need access to a password. Something has happened on my device: maybe the mTLS
cert was accidentally uninstalled, or maybe it expired and I haven&rsquo;t realized it yet. I try
to access the password, everything vanishes, and I&rsquo;m stuck.</p>
<p>I wanted to prevent this from being a possibility, which is why I wanted to alter the
response code from 403 to 404. As mentioned in the previous section, this isn&rsquo;t possible
on the free plan.</p>
<h2 id="enter-cloudflare-workers">Enter Cloudflare Workers</h2>
<p>However, there is another option within reach of the free plan, which is Cloudflare Workers.
Workers allow you to deploy some custom code that can be <a href="https://developers.cloudflare.com/workers/configuration/routing/routes/">mapped to a specific URL path</a>.
The free plan currently allows up to 100,000 Workers requests per day, which is plenty for
household use.</p>
<p>To get this setup, I did the following:</p>
<h3 id="create-and-configure-the-mtls-certificate">Create and configure the mTLS certificate</h3>
<p><a href="https://developers.cloudflare.com/ssl/client-certificates/create-a-client-certificate/">Create an mTLS certificate</a>
and <a href="https://developers.cloudflare.com/ssl/client-certificates/enable-mtls/">enable it for the targeted domain names</a>.</p>
<h3 id="remove-any-waf-rules">Remove any WAF rules</h3>
<p>I first made sure any WAF rules I had did not apply to the hostname in question. In the
request lifecycle, these will be executed early on.</p>
<h3 id="add-a-zero-trust-bypass-rule-optional">Add a Zero Trust bypass rule (optional)</h3>
<p>Zero Trust access rules also run prior to Workers, so you need to make sure this won&rsquo;t
block access to your application.</p>
<p>If you only have Zero Trust applications set up for specific subdomains, you may not need
to do anything here. I happen to have a wildcard application setup, so that anything in my
domain would route to the Zero Trust handling by default. This means I needed to specify an
exception just for the subdomain that I wanted to protect with an mTLS cert.</p>
<p>To do this, I created a &ldquo;bypass all rule&rdquo;:
<img src="bypass_rule.png" alt="Cloudflare Zero Trust bypass all rule"></p>
<h3 id="create-the-worker">Create the worker</h3>
<p>Create a new worker, using the provided &ldquo;Hello World&rdquo; template. Replace the code with
the following:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">async</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">request</span><span class="p">,</span> <span class="nx">env</span><span class="p">,</span> <span class="nx">ctx</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Get the certificate status from the Cloudflare edge
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">const</span> <span class="nx">clientTrust</span> <span class="o">=</span> <span class="nx">request</span><span class="p">.</span><span class="nx">cf</span><span class="o">?</span><span class="p">.</span><span class="nx">tlsClientAuth</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Check that the mTLS has been presented and verified by Cloudflare.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">clientTrust</span> 
</span></span><span class="line"><span class="cl">        <span class="o">||</span> <span class="nx">clientTrust</span><span class="p">.</span><span class="nx">certPresented</span> <span class="o">!==</span> <span class="s2">&#34;1&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="o">||</span> <span class="nx">clientTrust</span><span class="p">.</span><span class="nx">certVerified</span> <span class="o">!==</span> <span class="s2">&#34;SUCCESS&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="k">new</span> <span class="nx">Response</span><span class="p">(</span><span class="s2">&#34;Not Found&#34;</span><span class="p">,</span> <span class="p">{</span> 
</span></span><span class="line"><span class="cl">        <span class="nx">status</span><span class="o">:</span> <span class="mi">404</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span> <span class="s2">&#34;Content-Type&#34;</span><span class="o">:</span> <span class="s2">&#34;text/plain&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// If valid, forward the fetch to the backend server.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="nx">fetch</span><span class="p">(</span><span class="nx">request</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span></span></span></code></pre></div><p><strong>Optional:</strong> If you would normally use a WAF rule to block other types of traffic, you
likely can incorporate that logic into the Worker as well. For example,
you could add a regional restriction like this:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nx">request</span><span class="p">.</span><span class="nx">cf</span><span class="o">?</span><span class="p">.</span><span class="nx">country</span> <span class="o">!==</span> <span class="s2">&#34;US&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="k">new</span> <span class="nx">Response</span><span class="p">(</span><span class="s2">&#34;Not Found&#34;</span><span class="p">,</span> <span class="p">{</span> 
</span></span><span class="line"><span class="cl">    <span class="nx">status</span><span class="o">:</span> <span class="mi">404</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">headers</span><span class="o">:</span> <span class="p">{</span> <span class="s2">&#34;Content-Type&#34;</span><span class="o">:</span> <span class="s2">&#34;text/plain&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="set-up-route-and-test-the-new-worker">Set up route and test the new worker</h3>
<p>After deploying the worker, you can then configure the appropriate route
by following the <a href="https://developers.cloudflare.com/workers/configuration/routing/routes/#set-up-a-route-in-the-dashboard">Cloudflare documentation</a>.</p>
<p>Then, try accessing the application you have protected, on devices with and
without the mTLS certificate installed.</p>
<h2 id="future-wishes">Future wishes</h2>
<p>Ideally, I would really like to be able to target mTLS certificates as part of the
Zero Trust access policies. This would allow different combinations to be created,
like supporting either mTLS auth or an OAuth identity provider. Depending on the
application being protected, this type of configuration may provide adequate security,
while providing greater flexibility &ndash; allowing web browser access with just OAuth,
but falling back to mTLS auth for devices where it is the best option.</p>
<p>There is <a href="https://developers.cloudflare.com/cloudflare-one/access-controls/service-credentials/mutual-tls-authentication/">rich support for mTLS auth in Zero Trust</a>,
but this support is limited to Business accounts only! At hundreds of dollars per
month, that&rsquo;s a very high price to pay given that I don&rsquo;t need most of what that
plan provides.</p>
<p>If anyone from Cloudflare is listening, it&rsquo;d be great to expand the availability of
this feature &ndash; potentially they could offer individual feature plans, like what is
currently done for <a href="https://developers.cloudflare.com/workers/">Workers</a>. I&rsquo;d happily
pay a reasonable amount just to get support to bring my own root CA and use it as part
of my Zero Trust access policies.</p>
<h2 id="final-thoughts">Final thoughts</h2>
<p>Securing a server shouldn&rsquo;t come at the cost of usability. By moving logic from the limited
WAF rules in a Cloudflare free account into a Cloudflare Worker, I believe I&rsquo;ve managed to
keep the high-security of using mTLS while smoothing out the quirks of the password manager
application I&rsquo;m using. It’s an easy-to-configure change that avoids a potential lockout.</p>
<p>Are you running a similar setup? Have suggestions on other ways to leverage Cloudflare&rsquo;s
copious free functionality? Leave a comment below or <a href="https://dancarroll.org/about/">send me an email</a>.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Migrating from Hyde to Hugo</title>
      <link>https://dancarroll.org/blog/2025/01/hugo/</link>
      <pubDate>Sat, 11 Jan 2025 01:15:00 -0500</pubDate>
      <author>Dan Carroll</author>
      <guid>https://dancarroll.org/blog/2025/01/hugo/</guid>
      <description>&lt;p&gt;Almost &lt;a href=&#34;https://dancarroll.org/blog/2013/03/redesign/&#34;&gt;twelve years&lt;/a&gt; after the last big site&#xA;infrastructure change, it&amp;rsquo;s time for another round. This time is purely&#xA;technical, with no intentional visual changes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;motivation&#34;&gt;Motivation&lt;/h2&gt;&#xA;&lt;p&gt;I haven&amp;rsquo;t posted in a long time, and this blog had largely faded into the&#xA;background for me. I was doing some management of my domains, trying to clean&#xA;things up after my domains registered with Google &lt;a href=&#34;https://9to5google.com/2024/04/05/google-domains-squarespace-controls-migration/&#34;&gt;were transferred to&#xA;Squarespace&lt;/a&gt;.&#xA;As part of this, I decided to use Cloudfare as my nameserver, which I&amp;rsquo;ve used&#xA;for other projects.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Almost <a href="https://dancarroll.org/blog/2013/03/redesign/">twelve years</a> after the last big site
infrastructure change, it&rsquo;s time for another round. This time is purely
technical, with no intentional visual changes.</p>
<h2 id="motivation">Motivation</h2>
<p>I haven&rsquo;t posted in a long time, and this blog had largely faded into the
background for me. I was doing some management of my domains, trying to clean
things up after my domains registered with Google <a href="https://9to5google.com/2024/04/05/google-domains-squarespace-controls-migration/">were transferred to
Squarespace</a>.
As part of this, I decided to use Cloudfare as my nameserver, which I&rsquo;ve used
for other projects.</p>
<p>I started reading about <a href="https://pages.cloudflare.com/">Cloudfare Pages</a>, which
seemed like a great match for easily deploying a statically-generated site. You
can point Cloudflare to a GitHub repo, and it will automatically kick off a
build after every commit, and deploy the static artifacts. Unfortunately, the
<a href="https://github.com/hyde/hyde">Hyde framework</a> I was using is no longer
maintained, and still does not support Python 3. Cloudflare Pages does not
have support for Python 2.7.</p>
<p>So I decided it would be a fun project to migrate to a new static site
generator.</p>
<h2 id="finding-a-new-tool">Finding a new tool</h2>
<p>Searching around, I quickly settled on <a href="https://gohugo.io/">Hugo</a> due to its
large community, and reputation for blazing fast speed.</p>
<p>I wasn&rsquo;t quite sure how the easy it would be to migrate my old site template,
so I started off following the <a href="https://gohugo.io/getting-started/quick-start/">quick start</a>
guide, chose an existing public theme that looked ok, and started copying the
content from my site&rsquo;s old repository. This was pretty easy since both tools
are based on Markdown &ndash; I just needed to slightly adjust each post&rsquo;s metadata.
With less than 20 total posts, I did that manually.</p>
<h2 id="fundamental-differences">Fundamental differences</h2>
<p>Hyde is a very simple generation tool, compared to Hugo which has a much larger
feature set, but also very strong opinions on how things should be structured.
In Hyde, I had a flat folder of templates, and could directly point individual
pages to an arbitraty template.</p>
<p>Hugo has a <a href="https://gohugo.io/getting-started/directory-structure/">very specific structure</a>
to follow, which it leverages to classify into a <a href="https://gohugo.io/templates/types/">set of page types</a>.
This prevents the organizational structure that I wished to have, but I quickly
adjusted. For example, my previous file hierachy looked like this:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">content/
</span></span><span class="line"><span class="cl">content/blog
</span></span><span class="line"><span class="cl">content/blog/2025
</span></span><span class="line"><span class="cl">content/blog/2025/01
</span></span><span class="line"><span class="cl">content/blog/2025/01/post.md</span></span></code></pre></div><p>Having multiple nested directories is <a href="https://discourse.gohugo.io/t/hugo-way-to-create-new-content-in-year-month-subfolders/36557">not easily supported</a>,
so I needed to switch to the following hierarchy:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">content/
</span></span><span class="line"><span class="cl">content/blog
</span></span><span class="line"><span class="cl">content/blog/2025-01-post.md</span></span></code></pre></div><p>Given the limited number of posts, I don&rsquo;t mind this setup too much. However,
if I were a more prolific blogger, I would probably dig in to find a better
way to organize individual post files.</p>
<h2 id="porting-a-hyde-theme">Porting a Hyde theme</h2>
<p><a href="https://gohugo.io/templates/">Hugo themes</a> are also much more structured than
the ad-hoc Hyde layouts. There is a <a href="https://gohugo.io/templates/lookup-order/">rigid template lookup order</a>,
which required some trial-and-error to determine where all of the template
types needed to live in order to get picked up correctly.</p>
<p>Hugo uses a Go templating library, so I needed to tweak all of my previous
Jinja2 templates. This was a bit of manual work, but overall not too bad,
largely because of the <a href="https://gohugo.io/functions/">rich documentation available</a>.</p>
<p>On the plus side, Hugo does have <a href="https://gohugo.io/templates/embedded/">built-in templates</a>
for Google Analytics and Disqus, both of which are used on this site. Rather
than needing to include the code for those in my templates, I could
just define some simple config parameters and reference the built-in templates:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-html-template" data-lang="go-html-template"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="cp">{{</span><span class="w"> </span><span class="k">template</span><span class="w"> </span><span class="s">&#34;_internal/google_analytics.html&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span></span></span></code></pre></div><p>For posts, I ended up defining one <a href="https://gohugo.io/templates/single/">&ldquo;single&rdquo; template</a>,
which is used for both posts and standalone pages like <a href="https://dancarroll.org/about/">About</a>.
I need conditional logic to prevent showing Disqus on non-posts. I settled on
excluding pages that appear in the site navigation menu:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go-html-template" data-lang="go-html-template"><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="na">.Params.menu</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">template</span><span class="w"> </span><span class="s">&#34;_internal/disqus.html&#34;</span><span class="w"> </span><span class="na">.</span><span class="w"> </span><span class="cp">}}</span>
</span></span><span class="line"><span class="cl"><span class="cp">{{</span><span class="w"> </span><span class="k">end</span><span class="w"> </span><span class="cp">}}</span></span></span></code></pre></div><h2 id="final-result">Final result</h2>
<p>As before, the code for this site remains open source. I left the old code
in its own repository, and created a new repository for the Hugo-base site
implementation, which you can <a href="https://github.com/dancarroll/dancarrollorg_hugo">find on GitHub</a>.</p>
]]></content:encoded>
    </item>
    <item>
      <title>PAX East 2013</title>
      <link>https://dancarroll.org/blog/2013/04/pax-east/</link>
      <pubDate>Thu, 04 Apr 2013 23:30:00 -0500</pubDate>
      <author>Dan Carroll</author>
      <guid>https://dancarroll.org/blog/2013/04/pax-east/</guid>
      <description>&lt;p&gt;I wanted to write a quick post with some general impressions from my time at the 2013 edition of &lt;a href=&#34;http://east.paxsite.com/&#34;&gt;PAX East&lt;/a&gt;. I&amp;rsquo;ve been attending PAX since 2008, but this was just my second year at the East coast version.  I also only went for a single day, which greatly limits the amount of stuff you can see on the convention floor.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-venue&#34;&gt;The venue&lt;/h2&gt;&#xA;&lt;p&gt;PAX East is held at the &lt;a href=&#34;https://www.signatureboston.com/bcec&#34;&gt;Boston Convention &amp;amp; Exhibition Center&lt;/a&gt;, and is the perfect location for this type of event.  The building has a ton of space, and can easily handle the number of people that attend.  It doesn&amp;rsquo;t have the wacky charm that the Washington State Convention Center has (the site of PAX Prime), but I&amp;rsquo;ll take the elbow room over charm any day.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I wanted to write a quick post with some general impressions from my time at the 2013 edition of <a href="http://east.paxsite.com/">PAX East</a>. I&rsquo;ve been attending PAX since 2008, but this was just my second year at the East coast version.  I also only went for a single day, which greatly limits the amount of stuff you can see on the convention floor.</p>
<h2 id="the-venue">The venue</h2>
<p>PAX East is held at the <a href="https://www.signatureboston.com/bcec">Boston Convention &amp; Exhibition Center</a>, and is the perfect location for this type of event.  The building has a ton of space, and can easily handle the number of people that attend.  It doesn&rsquo;t have the wacky charm that the Washington State Convention Center has (the site of PAX Prime), but I&rsquo;ll take the elbow room over charm any day.</p>
<h2 id="the-games">The games</h2>
<p><strong>Battleblock Theater</strong>: I first saw this game back at PAX Prime in 2009, before it had a name.  I&rsquo;ve watched it closely since then, following its progression over the years. The game finally released today, and it seems to have benefited from the extra polish.  I hope it can captivate me the same way Castle Crashers did, and I look forward to seeing what&rsquo;s next for <a href="http://www.thebehemoth.com/">The Behemoth</a>.</p>
<p><strong>Nutjitsu</strong>: This was a brand new game from <a href="http://www.ninjabee.com/">Ninjabee</a>.  In its current state, it is a simple path building and stealth game built for tablets, with a fun art style and main character.  I&rsquo;m a big fan of their Kefling series of games, so I&rsquo;m looking forward to playing this when it gets released.</p>
<p><strong>Perspective</strong>: This game was one of the games playable at the Digipen booth, as it was a <a href="https://www.digipen.edu/showcase/student-games/perspective">student project</a>.  It&rsquo;s an excellent platformer that makes very clever use out of the interaction between 2D and 3D space.  I highly recommend checking it out, as well as some of the other Digipen games from recent years.</p>
<h2 id="the-talks">The talks</h2>
<p>Beyond playing new games, I really enjoy PAX for the variety of industry talks and panels that are held.</p>
<p><strong>Mass Effect Retrospective</strong>: This panel had a number of designers, writers, and even a voice actor from the Mass Effect series of games.  I had envisioned fans with pitchforks, still angry about the ending of <em>Mass Effect 3</em>, but the panel ended up being mostly fluff and softball questions.  It was still nice to hear some background information over various bits of the development process.</p>
<p><strong>Classic Videogame Panel</strong>: This panel was hosted by <a href="https://www.classicarcademuseum.org/">ACAM</a>, who also have a classic arcade game room at PAX East.  They hosted a similar panel last year, with a number of the same panelists, but that didn&rsquo;t dampen my enthusiasm.  I have no problem listening to those guys share stories and talk about what it was like to develop videogames back in the 80s.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Configuring No-WWW for IIS on Azure</title>
      <link>https://dancarroll.org/blog/2013/03/iis-no-www/</link>
      <pubDate>Fri, 22 Mar 2013 00:45:00 -0500</pubDate>
      <author>Dan Carroll</author>
      <guid>https://dancarroll.org/blog/2013/03/iis-no-www/</guid>
      <description>&lt;p&gt;As part of the rollout of my site redesign, I&amp;rsquo;ve also switched hosts from &lt;a href=&#34;http://www.webfaction.com/&#34;&gt;WebFaction&lt;/a&gt; to &lt;a href=&#34;http://www.windowsazure.com/&#34;&gt;Windows Azure&lt;/a&gt;.  WebFaction was a great value for a shared host, and I&amp;rsquo;ll probably continue to use it in the future for small Python/Django projects, but I&amp;rsquo;ve been experimenting with Azure on some other projects and took the opportunity to make the switch.&lt;/p&gt;&#xA;&lt;h2 id=&#34;whered-my-htaccess-go&#34;&gt;Where&amp;rsquo;d my .htaccess go?&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a href=&#34;http://www.windowsazure.com/en-us/home/features/web-sites/&#34;&gt;Azure Web Sites&lt;/a&gt; use IIS as a web server, not the more common Apache (or Nginx) server widely used across the Unix world.  While Azure handles all of the server setup and maintenance for many cases, you&amp;rsquo;ll still need to get your hands dirty if you need custom handling.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>As part of the rollout of my site redesign, I&rsquo;ve also switched hosts from <a href="http://www.webfaction.com/">WebFaction</a> to <a href="http://www.windowsazure.com/">Windows Azure</a>.  WebFaction was a great value for a shared host, and I&rsquo;ll probably continue to use it in the future for small Python/Django projects, but I&rsquo;ve been experimenting with Azure on some other projects and took the opportunity to make the switch.</p>
<h2 id="whered-my-htaccess-go">Where&rsquo;d my .htaccess go?</h2>
<p><a href="http://www.windowsazure.com/en-us/home/features/web-sites/">Azure Web Sites</a> use IIS as a web server, not the more common Apache (or Nginx) server widely used across the Unix world.  While Azure handles all of the server setup and maintenance for many cases, you&rsquo;ll still need to get your hands dirty if you need custom handling.</p>
<p>One of the big behaviors I needed to maintain in the migration was to continue adherence to the <a href="http://no-www.org/">No-WWW philosophy</a>.  This means making dancarroll.org the canonical name of my site, with <a href="https://www.dancarroll.org">www.dancarroll.org</a> redirecting to the no-www version.</p>
<p>I had this set up on the previous iteration of the site using an Apache .htaccess file.  It was very easy to find examples of how to do this across the web.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">RewriteEngine On
</span></span><span class="line"><span class="cl">RewriteBase /
</span></span><span class="line"><span class="cl">RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
</span></span><span class="line"><span class="cl">RewriteRule ^(.*)$ http://%1/$1 [R=permanent,L]</span></span></code></pre></div><p>IIS, however, does not use .htaccess files.  Instead, I needed to create a <a href="http://en.wikipedia.org/wiki/Web.config">Web.config</a> file with the appropriate settings.</p>
<h2 id="iis-url-rewrite">IIS URL Rewrite</h2>
<p>Luckily, IIS has its own <a href="http://www.iis.net/downloads/microsoft/url-rewrite">URL rewriting module</a>.  And, even luckier, Azure Web Sites get this module configured automatically.</p>
<p>To get this set up, you need to create a file named <code>Web.config</code> in the wwwroot directory of your Azure deployment.  I was only able to find a few examples of doing no-www redirects for IIS, but all of them hardcoded the domain name in the file.  This wasn&rsquo;t acceptable, as I like to reduce maintenance and use a similar configuration across many different web sites.</p>
<p>Here&rsquo;s what I came up with:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34;?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;configuration&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;system.webServer&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;rewrite&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;rules&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;rule</span> <span class="na">name=</span><span class="s">&#34;Canonical Hostname&#34;</span> <span class="na">stopProcessing=</span><span class="s">&#34;false&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;match</span> <span class="na">url=</span><span class="s">&#34;(.*)&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;conditions</span> <span class="na">logicalGrouping=</span><span class="s">&#34;MatchAll&#34;</span> <span class="na">trackAllCaptures=</span><span class="s">&#34;false&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;add</span> <span class="na">input=</span><span class="s">&#34;{HTTP_HOST}&#34;</span> <span class="na">pattern=</span><span class="s">&#34;^(www\.)(.*)$&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&lt;/conditions&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;action</span> <span class="na">type=</span><span class="s">&#34;Redirect&#34;</span> <span class="na">url=</span><span class="s">&#34;http://{C:2}{REQUEST_URI}&#34;</span> <span class="na">redirectType=</span><span class="s">&#34;Permanent&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/rule&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&lt;/rules&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/rewrite&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/system.webServer&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/configuration&gt;</span></span></span></code></pre></div><p>And that&rsquo;s it!  I found that Azure would recognize the file modification and start using it, but you may need to manually restart the site using the Azure management portal.</p>
<h2 id="breaking-it-down">Breaking it down</h2>
<p>The <a href="http://www.iis.net/learn/extensions/url-rewrite-module/url-rewrite-module-configuration-reference">documentation for IIS URL Rewrite</a> is decent, but I wanted to provide a few quick notes about what is going on here.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl">  <span class="nt">&lt;match</span> <span class="na">url=</span><span class="s">&#34;(.*)&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;conditions</span> <span class="na">logicalGrouping=</span><span class="s">&#34;MatchAll&#34;</span> <span class="na">trackAllCaptures=</span><span class="s">&#34;false&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;add</span> <span class="na">input=</span><span class="s">&#34;{HTTP_HOST}&#34;</span> <span class="na">pattern=</span><span class="s">&#34;^(www\.)(.*)$&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/conditions&gt;</span></span></span></code></pre></div><p>The <code>match</code> element defines the pattern that will be used to find URLs to be processed.  In this case, the regular expression used captures all URLs.  A condition is then created to provide additional refinement to which URLs will have a rewrite action taken.  Here, the condition only looks at the server variable <code>HTTP_HOST</code>, which requires matching the <em>host domain</em> (not the full URL).  The regular expression used only matches hosts starting with <code>www.</code>.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl">  <span class="nt">&lt;action</span> <span class="na">type=</span><span class="s">&#34;Redirect&#34;</span> <span class="na">url=</span><span class="s">&#34;http://{C:2}{REQUEST_URI}&#34;</span> <span class="na">redirectType=</span><span class="s">&#34;Permanent&#34;</span> <span class="nt">/&gt;</span></span></span></code></pre></div><p>If there was a successful match, then an action is taken for that particular URL.  In this case, the action is a redirect, with the &ldquo;Permanent&rdquo; <code>redirectType</code> mapping to an HTTP 301 status code.  The <code>url</code> attribute defines the redirect URL, and I&rsquo;m using two features here to generate it.</p>
<p><code>{C:2}</code> is a back-reference, which as its name implies, refers back to data captured previously in the rule.  The <code>C</code> states that we are referring to something captured by a condition (as opposed to the rule match), and the <code>2</code> is the second group match (0 is the full pattern, 1 is the <code>www.</code> part, and 2 is the remainder of the host).</p>
<p>Finally, <code>{REQUEST_URI}</code> is a another server variable that IIS lets us reference, and it contains the full path to the requested document.  If the URL is <code>http://www.dancarroll.org/blog/</code>, then <code>REQUEST_URI</code> is <code>/blog/</code>.</p>
<h2 id="bonus-rules">Bonus Rules</h2>
<p>A couple of extra URL rewrite rules I am using that are worth sharing.</p>
<h3 id="forcing-lowercase-urls">Forcing lowercase URLs</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl">  <span class="nt">&lt;rule</span> <span class="na">name=</span><span class="s">&#34;Convert to lower case&#34;</span> <span class="na">stopProcessing=</span><span class="s">&#34;true&#34;</span><span class="nt">&gt;</span>  
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;match</span> <span class="na">url=</span><span class="s">&#34;.*[A-Z].*&#34;</span> <span class="na">ignoreCase=</span><span class="s">&#34;false&#34;</span> <span class="nt">/&gt;</span>  
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;action</span> <span class="na">type=</span><span class="s">&#34;Redirect&#34;</span> <span class="na">url=</span><span class="s">&#34;{ToLower:{R:0}}&#34;</span> <span class="na">redirectType=</span><span class="s">&#34;Permanent&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&lt;/rule&gt;</span></span></span></code></pre></div><h3 id="redirecting-default-documents">Redirecting default documents</h3>
<p>For default documents (i.e. index.html), it is nice to redirect a request to <code>domain.com\dir\index.html</code> to its parent directory, <code>domain.com\dir\</code>.  This also help to ensure there is only a single canonical name for any document.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl">	<span class="nt">&lt;rule</span> <span class="na">name=</span><span class="s">&#34;Default document - html&#34;</span> <span class="na">stopProcessing=</span><span class="s">&#34;true&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">	  <span class="nt">&lt;match</span> <span class="na">url=</span><span class="s">&#34;(.*)index.html&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">	  <span class="nt">&lt;action</span> <span class="na">type=</span><span class="s">&#34;Redirect&#34;</span> <span class="na">url=</span><span class="s">&#34;{R:1}&#34;</span> <span class="na">redirectType=</span><span class="s">&#34;Permanent&#34;</span> <span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&lt;/rule&gt;</span></span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>Redesign</title>
      <link>https://dancarroll.org/blog/2013/03/redesign/</link>
      <pubDate>Sat, 16 Mar 2013 22:38:05 -0500</pubDate>
      <author>Dan Carroll</author>
      <guid>https://dancarroll.org/blog/2013/03/redesign/</guid>
      <description>&lt;p&gt;This site has been quiet for a long time.  My last post was over a year ago in February 2012.  That post before that was yet another year prior.  With a full-time job and other personal commitments, blog posting and other side projects easily fall off the radar.&lt;/p&gt;&#xA;&lt;p&gt;Take my site&amp;rsquo;s new design, rolling out today.  I initially started working on it in February 2011, going by the file creation timestamps on my machine.  I worked on it for a week or so, got some feedback, then dropped it.  A year later, I took the big step to actually create a source repository on Bitbucket to make sure the code was backed up somewhere safe (I don&amp;rsquo;t recall making any other progress at the time).&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>This site has been quiet for a long time.  My last post was over a year ago in February 2012.  That post before that was yet another year prior.  With a full-time job and other personal commitments, blog posting and other side projects easily fall off the radar.</p>
<p>Take my site&rsquo;s new design, rolling out today.  I initially started working on it in February 2011, going by the file creation timestamps on my machine.  I worked on it for a week or so, got some feedback, then dropped it.  A year later, I took the big step to actually create a source repository on Bitbucket to make sure the code was backed up somewhere safe (I don&rsquo;t recall making any other progress at the time).</p>
<p>It took me over a year from that point to actually pick up the project and push it through to completion.</p>
<h2 id="the-design">The design</h2>
<p>Like the previous design of my site, I have taken a lot of inspiration from people in the <a href="http://www.djangoproject.com/">Django</a> community.  A lot of the design inspiration comes from <a href="http://stevelosh.com/">Steve Losh</a>, who has consistently done an excellent job at clean, readable web design.</p>
<p>I&rsquo;m not sure what I was thinking with the previous iteration, which featured orange and beige on a darker, almost navy blue background.  That&rsquo;s been fixed now!</p>
<h2 id="the-code">The code</h2>
<p>Like Steve, I also made a transition in how the site is implemented.  Previously, my site was developed using Django.  Django is a Python-powered web framework, and it gave me tremendous flexibility to add features like a <a href="http://en.wikipedia.org/wiki/Lifestreaming">lifestream</a>.  However, it came with a lot of overhead for a simple blog, and could be unreliable on inexpensive, shared web servers.</p>
<p>This new iteration of the site is built using <a href="https://github.com/hyde/hyde">Hyde</a>, a static web site generator built in Python.  Hyde allows me to work with Python code and templating languages, much like the one used with Django, but the end result is a static HTML and CSS website (rather than a dynamic site backed by executing Python code and databases).  The end product can now be hosted anywhere with relatively good performance.</p>
<p>And like the previous iteration, the new version is still open source.  For ease of browsing both implementations, the new design lives in a <a href="https://github.com/dancarroll/dancarrollorg_hyde">separate code repository on GitHub</a>.</p>
<h2 id="the-problems">The problems</h2>
<p>Another big improvement made during this transition is in ease of use and maintainability for me.  Previously, I tried a few different ways of creating blog posts.  First, I tried writing HTML each time I needed to create a new post, and uploaded it using Django&rsquo;s admin UI.  This was a pain and prone to rendering issues.</p>
<p>Next, I spent a lot of time implementing support for the <a href="http://en.wikipedia.org/wiki/MetaWeblog">MetaWeblog API</a>, which allowed me to create blog posts using applications like <a href="http://en.wikipedia.org/wiki/Windows_Live_Writer">Windows Live Writer</a>. That made post creation much easier for me, but those blogging applications would create ugly HTML with their own special markup, and often required tweaking the HTML by hand to get things to show up correctly.</p>
<p>Finally, regardless of how I created posts, they ended up being saved to a database on my web host. That&rsquo;s not a very safe location, so I needed to make sure to create regular backups.</p>
<h2 id="the-solutions">The solutions</h2>
<p>With Hyde, it was easy to convert all of my blog posts to be written in <a href="http://en.wikipedia.org/wiki/Markdown">Markdown</a>.  When generating the site, the posts get passed through a Markdown filter to convert them to HTML.  As long as I have good CSS defined on how to render what HTML gets produced, I can create a blog post using a simple text editor and know that it will come out looking good on the other side.</p>
<p><strong>Aside:</strong> Django has built-in support for Markdown, so I could have used it with the site&rsquo;s previous implementation.  However, things would have been more complicated.  There would be performance issues if posts were passed through the Markdown filter for each web request, deployment complexity if I needed to leverage server-side caching, or implementation complexity if both versions were stored (Markdown version for editing, HTML version for serving up web requests).</p>
<p>For the issue of storing and backing up blog posts, content is now part of the codebase itself.  It gets pushed up to Bitbucket, and lives on a few different machines where I have the code checked out.</p>
<h2 id="the-future">The future</h2>
<p>I feel very accomplished for finally completing this major redesign project, so I hope to actually use this thing more often.  I&rsquo;ll never be prolific, but my goal is to publish at least several posts a year.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
