<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Heroku on Noetic Nought</title>
    <link>https://punchagan.muse-amuse.in/tags/heroku/</link>
    <description>Recent content in Heroku on Noetic Nought</description>
    <generator>Hugo</generator>
    <language>en-US</language>
    <copyright>© 2006-2026 CC-BY-SA-4.0</copyright>
    <lastBuildDate>Sun, 06 Nov 2022 12:19:00 +0530</lastBuildDate>
    <atom:link href="https://punchagan.muse-amuse.in/tags/heroku/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Moving from Heroku to Fly.io</title>
      <link>https://punchagan.muse-amuse.in/blog/moving-from-heroku-to-fly-dot-io/</link>
      <pubDate>Sun, 06 Nov 2022 12:19:00 +0530</pubDate>
      <guid>https://punchagan.muse-amuse.in/blog/moving-from-heroku-to-fly-dot-io/</guid>
      <description>&lt;p&gt;A couple of months ago Heroku &lt;a href=&#34;https://blog.heroku.com/next-chapter&#34;&gt;announced&lt;/a&gt; their plan to &amp;ldquo;Focus on Mission&#xA;Critical&amp;rdquo; and shut down the free product plan. The ideas is to free up their&#xA;developer time to focus on the important stuff instead of fighting abuse of the&#xA;free plans. I&amp;rsquo;m surprised they invested in the free plans for so many years!&lt;/p&gt;&#xA;&lt;h2 id=&#34;my-heroku-apps&#34;&gt;My Heroku Apps&lt;/h2&gt;&#xA;&lt;p&gt;I have a bunch of apps on the free (Hobby dev) plan, and only app on the Hobby&#xA;Basic plan because it needed a bigger DB. All of these apps are small enough&#xA;that I could easily host all of them on my shared &lt;a href=&#34;https://www.hetzner.com/&#34;&gt;Hetzner server&lt;/a&gt;, where this&#xA;blog is hosted. But, I like using Heroku for services that I run for other&#xA;people. It makes access control and deployment much easier, and lets me invite&#xA;other people to help maintain apps easily, despite constraints like &lt;a href=&#34;https://devcenter.heroku.com/articles/heroku-postgres-plans#hobby-tier&#34;&gt;10k rows&lt;/a&gt; in&#xA;the Postgres DB and &lt;a href=&#34;https://devcenter.heroku.com/articles/free-dyno-hours&#34;&gt;Dyno sleeping&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Over the years of using Heroku, I ended up accumulating a bunch of small apps&#xA;on the service. Most of these apps aren&amp;rsquo;t actively being used any more and&#xA;didn&amp;rsquo;t feel worth the effort of moving.&lt;/p&gt;&#xA;&lt;p&gt;I decided to let most of the apps die, since they aren&amp;rsquo;t actively being&#xA;used. Only a couple of apps are used regularly enough to be worth keeping alive&#xA;&amp;ndash; &lt;a href=&#34;https://sotg.indiaultimate.org/&#34;&gt;SOTG Calculator&lt;/a&gt; and &lt;a href=&#34;https://rsvp.tiks-ultimate.in/features&#34;&gt;RSVP app&lt;/a&gt;. I turned on &lt;a href=&#34;https://devcenter.heroku.com/articles/maintenance-mode&#34;&gt;maintenance mode&lt;/a&gt; on the RSVP app&#xA;for a couple of days, as an experiment, and I had to bring back the services&#xA;due to &amp;ldquo;popular&amp;rdquo; demand.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-not-pay-heroku&#34;&gt;Why not pay Heroku?&lt;/h2&gt;&#xA;&lt;p&gt;I did consider just moving up to the Hobby basic plan and paying for running&#xA;the app. I wasn&amp;rsquo;t keen on investing numerous hours to get these apps to run&#xA;again. &lt;a href=&#34;https://instagram.com/akilesh.m&#34;&gt;Akilesh&lt;/a&gt; convinced me that the amount to pay Heroku could be collected&#xA;from the team and wouldn&amp;rsquo;t amount to much per person. I almost took this route.&lt;/p&gt;&#xA;&lt;p&gt;But, it bugged me that the amount we&amp;rsquo;d be paying Heroku could fund a couple of&#xA;turf practice sessions for the team. I could host the apps on my Hetzner server&#xA;&amp;ndash; we already pay enough for it. But, giving other people maintainer access to&#xA;apps is important and this was not straightforward with Hetzner.&lt;/p&gt;&#xA;&lt;p&gt;Also, the SOTG app couldn&amp;rsquo;t be funded this way.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-fly-dot-io&#34;&gt;Why Fly.io?&lt;/h2&gt;&#xA;&lt;p&gt;I ranted about this to my friends on Zulip and &lt;a href=&#34;https://github.com/rajaboja/&#34;&gt;Rajesh&lt;/a&gt; offered to help me as a&#xA;way to learn some devops stuff. I was more than happy to have someone to&#xA;discuss this stuff with and get some help.&lt;/p&gt;&#xA;&lt;p&gt;We decided to give &lt;a href=&#34;https://fly.io/&#34;&gt;Fly.io&lt;/a&gt; a try despite being skeptical about using yet another&#xA;free service. Their free Hobby plan includes 3GB of persistent storage space&#xA;(which neither of the apps currently use, but potentially could). They also&#xA;built &lt;a href=&#34;https://fly.io/blog/new-turboku/&#34;&gt;Turboku&lt;/a&gt; &amp;ndash; a tool to move apps from Heroku. It seemed worth giving a try,&#xA;and we could always fall back on Hetzner if it didn&amp;rsquo;t work out.&lt;/p&gt;&#xA;&lt;p&gt;We had a couple of months before the Heroku shutdown date, when I ranted about&#xA;this. Rajesh and I exchanged a few links and messages on how to potentially go&#xA;about doing this, when we got down to it. But, he got busy with other things&#xA;and now the deadline was only a month away. I decided to use the notes and do&#xA;the migration myself. I was glad I had the notes to refer to. The initial&#xA;&amp;ldquo;research&amp;rdquo; made the task seem less daunting.&lt;/p&gt;&#xA;&lt;h2 id=&#34;migrating-a-simple-app&#34;&gt;Migrating a simple app&lt;/h2&gt;&#xA;&lt;p&gt;I started with the simpler app first &amp;ndash; the &lt;a href=&#34;https://sotg.indiaultimate.org/&#34;&gt;SOTG app&lt;/a&gt;. I used the &lt;a href=&#34;https://fly.io/launch/heroku&#34;&gt;Turboku web&#xA;page&lt;/a&gt; and it worked pretty seemlessly! It copied over the environment variables&#xA;from Heroku and deployed the app.&lt;/p&gt;&#xA;&lt;p&gt;But, using the Turboku web page meant that I didn&amp;rsquo;t have a &lt;code&gt;fly.toml&lt;/code&gt; file to&#xA;do any future deployments. I used the &lt;code&gt;fly launch&lt;/code&gt; command to create a new app&#xA;and edited the &lt;code&gt;fly.toml&lt;/code&gt; file to point to the migrated app.&lt;/p&gt;&#xA;&lt;p&gt;Setting up the custom domain was pretty simple too, using the CLI. I just had&#xA;to run &lt;code&gt;fly certs add sotg.indiaultimate.org&lt;/code&gt; after changing the DNS entries to&#xA;point to &lt;code&gt;sotg-calculator.fly.dev&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The whole thing felt quite simple and I was done in under an hour &amp;ndash; reading&#xA;the docs, installing the tool, trying out different things, getting it all&#xA;working. Everything. Quite impressive!&lt;/p&gt;&#xA;&lt;h2 id=&#34;migrating-a-bigger-app&#34;&gt;Migrating a bigger app&lt;/h2&gt;&#xA;&lt;p&gt;Encouraged by this, I started with the RSVP app. I didn&amp;rsquo;t think it would be&#xA;much harder than this, but I guess when there are a few more moving parts, it&amp;rsquo;s&#xA;hard to tell what could pop-up.&lt;/p&gt;&#xA;&lt;p&gt;I used the same approach for the migration &amp;ndash; Turboku web page to migrate from&#xA;Heroku and then ran the launch command to generate a &lt;code&gt;fly.toml&lt;/code&gt; file that I&#xA;edited to point to the deployed app.&lt;/p&gt;&#xA;&lt;p&gt;The deployment seemed to go fine, but the app wouldn&amp;rsquo;t come up. I looked at the&#xA;logs to find out that the app was getting &lt;a href=&#34;https://neo4j.com/developer/kb/linux-out-of-memory-killer/&#34;&gt;OOM killed&lt;/a&gt;. Fly.io provides half the&#xA;memory (256MB) that Heroku provided. But, I wasn&amp;rsquo;t expecting this tiny app to&#xA;need much more.&lt;/p&gt;&#xA;&lt;p&gt;I removed some default imports of large packages, but it didn&amp;rsquo;t really help&#xA;that much. I did consider reducing the Gunicorn&amp;rsquo;s &lt;code&gt;WEB_CONCURRENCY&lt;/code&gt; setting to&#xA;1, but didn&amp;rsquo;t want to take that route unless absolutely required. I looked&#xA;around the forums a little, and &lt;a href=&#34;https://community.fly.io/t/question-about-weird-deployment-size-on-fly/5677/2&#34;&gt;found&lt;/a&gt; that using a Dockerfile instead of the&#xA;buildpacks might improve the memory situation. And it did!&lt;/p&gt;&#xA;&lt;p&gt;Next, I moved to setting up the custom domain and it worked from the Fly.io&#xA;side seemlessly. But, the &lt;code&gt;flask_dance&lt;/code&gt; library was generating &lt;code&gt;http&lt;/code&gt; URLs in&#xA;it&amp;rsquo;s OAuth redirect URLs. The app already uses FlaskSSLify, but that didn&amp;rsquo;t&#xA;seem to be sufficient. After reading some &lt;a href=&#34;https://flask-dance.readthedocs.io/en/latest/proxies.html&#34;&gt;Flask dance&lt;/a&gt; and &lt;a href=&#34;https://flask.palletsprojects.com/en/latest/deploying/proxy_fix/&#34;&gt;Flask&lt;/a&gt; docs, I found&#xA;that Werkzeug&amp;rsquo;s &lt;code&gt;ProxyFix&lt;/code&gt; exists exactly for this!&lt;/p&gt;&#xA;&lt;p&gt;After dealing with these two unexpected problems, I got to the problem that I&#xA;was aware I had to tackle &amp;ndash; Fly.io doesn&amp;rsquo;t have support for cron jobs. The&#xA;RSVP app uses some scheduled jobs to periodically sync some metadata from&#xA;Google Drive, Calendar, etc.&lt;/p&gt;&#xA;&lt;p&gt;I took a hackish route to work around this. Using the &lt;a href=&#34;https://schedule.readthedocs.io/en/stable/&#34;&gt;schedule&lt;/a&gt; library, I wrote&#xA;a &lt;a href=&#34;https://github.com/thatte-idli-kaal-soup/rsvpapp/blob/master/scripts/cron.py&#34;&gt;&amp;ldquo;cron&amp;rdquo; script&lt;/a&gt; that would run the jobs on the desired schedule. I created a&#xA;duplicate app to run the cron service, even though it could potentially be run&#xA;as a different process on the same app. I didn&amp;rsquo;t want to risk any more OOM&#xA;kills on the web app. But, it can be a little bit more work to keep the&#xA;environment variables and other such things in sync between the two apps.&lt;/p&gt;&#xA;&lt;p&gt;I was quite excited about Fly.io having a datacenter in Madras and was swayed&#xA;by Fly.io&amp;rsquo;s marketing about running the app close to the users. I tried this&#xA;but the performance was terrible since the Mongo DB was in North Virginia. I&#xA;quickly switched back to using an East Coast region for Fly.io app too.&lt;/p&gt;&#xA;&lt;p&gt;But, while writing this blog post, I realized that I could move the DB to&#xA;India. I moved the DB to the AWS Mumbai datacenter, and the Fly.io app to the&#xA;Madras datacenter.&lt;/p&gt;&#xA;&lt;h2 id=&#34;all-s-well-that-ends-well&#34;&gt;All&amp;rsquo;s well that ends well&lt;/h2&gt;&#xA;&lt;p&gt;The app has been chugging along for a few days without any new hiccups. I&amp;rsquo;m&#xA;hoping things stay this way for the weeks and months to come, if not years.&lt;/p&gt;&#xA;&lt;p&gt;On the whole, I found the whole experience more beta-ish than deploying&#xA;something to Heroku. But, I&amp;rsquo;m quite happy with how the whole thing turned&#xA;out. I think things will get smoother with time &amp;ndash; with my growing familarity&#xA;with Fly.io and more people using their tools suggesting improvements.&lt;/p&gt;&#xA;&lt;p&gt;I am wary of depending on yet another Hobby plan, but neither of these apps&#xA;make any revenue and I&amp;rsquo;m not (yet) comfortable charging users for the small set&#xA;of features they provide.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;d like to hear stories of other people moving away from Heroku! Feel free to&#xA;reach out!&lt;/p&gt;&#xA;</description>
    </item>
    <item>
      <title>Heroku apps with custom-domain and Cloudflare SSL</title>
      <link>https://punchagan.muse-amuse.in/blog/heroku-apps-custom-domain-cloudflare-ssl/</link>
      <pubDate>Thu, 14 Mar 2019 13:03:00 +0530</pubDate>
      <guid>https://punchagan.muse-amuse.in/blog/heroku-apps-custom-domain-cloudflare-ssl/</guid>
      <description>&lt;h2 id=&#34;motivation&#34;&gt;Motivation&lt;/h2&gt;&#xA;&lt;p&gt;I have a bunch of small (toy?) apps hosted on Heroku. They are probably used by&#xA;a few dozen people, at most. For these apps, I&amp;rsquo;d like the following setup:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Use a custom domain for the apps&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Heroku provides domains of the form &lt;code&gt;appname.herokuapp.com&lt;/code&gt; for all apps.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;But, it also has the option to add one or more custom domains&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I usually already have a domain under which I want to add a sub-domain,&#xA;where these apps would run.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Use HTTPS for all connections&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The domains or the sub-domains usually don&amp;rsquo;t already already have an SSL&#xA;certificate. With lets-encrypt, I guess this can change in future.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Heroku provides SSL certificates for the default domain&#xA;(&lt;code&gt;appname.herokuapp.com&lt;/code&gt;) for free.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;They also have an option to buy SSL certificates for custom domains, but&#xA;they are expensive!&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I&amp;rsquo;m going to use &lt;a href=&#34;https://www.cloudflare.com/ssl/&#34;&gt;Cloudflare&amp;rsquo;s free SSL service&lt;/a&gt; instead. I&amp;rsquo;d like to have&#xA;SSL for the full domain, and not just the app&amp;rsquo;s subdomain.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Redirect all the requests to the Heroku domain to the custom domain&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Nobody should really be using the &lt;code&gt;appname.herokuapp.com&lt;/code&gt; domain, in case&#xA;I&amp;rsquo;d like to move away from it.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;A few lines of app (Flask) code can do this for us.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;&#xA;&lt;h3 id=&#34;setup-dns-to-use-cloudflare&#34;&gt;Setup DNS to use Cloudflare&lt;/h3&gt;&#xA;&lt;p&gt;Since we plan to use Cloudflare for the SSL certificates, we need to change DNS&#xA;settings at our domain registrar to use Cloudflare. We first need to create a&#xA;Cloudflare account, and &lt;a href=&#34;https://support.cloudflare.com/hc/en-us/articles/201720164-Step-2-Create-a-Cloudflare-account-and-add-a-website&#34;&gt;let Cloudflare do a DNS scan&lt;/a&gt; to add all the existing&#xA;domain settings automatically. Next we point to the &lt;a href=&#34;https://support.cloudflare.com/hc/en-us/articles/205195708&#34;&gt;Cloudflare DNS servers&lt;/a&gt; on&#xA;our domain registrar.&lt;/p&gt;&#xA;&lt;h3 id=&#34;enable-https-always-in-cloudflare&#34;&gt;Enable HTTPS always in Cloudflare&lt;/h3&gt;&#xA;&lt;p&gt;Using &lt;a href=&#34;https://support.cloudflare.com/hc/en-us/articles/218411427#always-use-https&#34;&gt;page rules in Cloudflare&lt;/a&gt;, add a rule for the whole domain to &amp;ldquo;Always use&#xA;HTTPS&amp;rdquo;. For example, use the url &lt;code&gt;http://*example.com/*&lt;/code&gt; and select the &lt;code&gt;Always use HTTPS&lt;/code&gt; option.&lt;/p&gt;&#xA;&lt;h3 id=&#34;select-the-ssl-option-to-use&#34;&gt;Select the SSL option to use&lt;/h3&gt;&#xA;&lt;p&gt;Cloudflare provides &lt;a href=&#34;https://support.cloudflare.com/hc/en-us/articles/200170416-What-do-the-SSL-options-mean-&#34;&gt;different options for the SSL setting&lt;/a&gt; which changes whether&#xA;or not traffic is encrypted between Cloudflare and the Heroku app.&lt;/p&gt;&#xA;&lt;p&gt;Choose &lt;code&gt;Full (strict)&lt;/code&gt; to enable encryption between Cloudflare and Heroku.&lt;/p&gt;&#xA;&lt;p&gt;If you are not so concerned about encryption between Cloudflare and Heroku, you&#xA;could select the &lt;code&gt;Flexible&lt;/code&gt; option too.&lt;/p&gt;&#xA;&lt;h3 id=&#34;add-a-custom-domain-in-heroku&#34;&gt;Add a custom domain in Heroku&lt;/h3&gt;&#xA;&lt;p&gt;Add a &lt;a href=&#34;https://devcenter.heroku.com/articles/custom-domains&#34;&gt;custom domain&lt;/a&gt; &amp;ndash; &lt;code&gt;app.example.com&lt;/code&gt; &amp;ndash; in Heroku&amp;rsquo;s settings.&lt;/p&gt;&#xA;&lt;p&gt;Heroku provides a DNS Target that needs to be used as the destination for a&#xA;CNAME setting in the DNS provider (Cloudflare). The DNS Target looks something&#xA;like &lt;code&gt;foo-bar-123abcdef.herokudns.com&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;If you chose &lt;code&gt;Full (strict)&lt;/code&gt; SSL option, this DNS target cannot be used and can&#xA;be ignored.&lt;/p&gt;&#xA;&lt;h3 id=&#34;add-a-cname-for-the-subdomain&#34;&gt;Add a CNAME for the subdomain&lt;/h3&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://support.cloudflare.com/hc/en-us/articles/360019093151-#h%5F60566325041543261564371&#34;&gt;Add a new CNAME setting&lt;/a&gt; in Cloudflare for &lt;code&gt;app.example.com&lt;/code&gt; and use the&#xA;app&amp;rsquo;s Heroku domain name (&lt;code&gt;appname.herokuapp.com&lt;/code&gt;) as the destination value/IP&#xA;address.&lt;/p&gt;&#xA;&lt;p&gt;It is important to use the &lt;code&gt;appname.herokuapp.com&lt;/code&gt; value if the SSL settings you&#xA;chose above was &lt;code&gt;Full (strict)&lt;/code&gt;. Using the DNS Target provided by Heroku instead&#xA;(&lt;code&gt;foo-bar-123abcdef.herokudns.com&lt;/code&gt;) would give an &lt;a href=&#34;https://support.cloudflare.com/hc/en-us/articles/200278659&#34;&gt;SSL Handshake error&lt;/a&gt; since the&#xA;SSL certificate provided by Heroku only works for the &lt;code&gt;appname.herokuapp.com&lt;/code&gt;&#xA;domain, and not for &lt;code&gt;foo-bar-123abcdef.herokudns.com&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;If you chose &lt;code&gt;Flexible&lt;/code&gt;, though, you can use the DNS Target provided by Heroku&#xA;&amp;ndash; &lt;code&gt;foo-bar-123abcdef.herokudns.com&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Also, ensure that the Cloudflare Proxy Toggle is toggled on &amp;ndash; &lt;em&gt;the cloud icon&#xA;is orange, not grey&lt;/em&gt;!&lt;/p&gt;&#xA;&lt;h3 id=&#34;redirect-all-requests&#34;&gt;Redirect all requests&lt;/h3&gt;&#xA;&lt;p&gt;We redirect all the requests coming to the old domain to the new one.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@app.before_request&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;redirect_heroku&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&amp;#34;Redirect herokuapp requests.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;urlparts&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;urlparse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;request&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;urlparts&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;netloc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;appname.herokuapp.com&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;urlparts_list&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;urlparts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;urlparts_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;app.example.com&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;redirect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;urlunparse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;urlparts_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;code&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;301&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;enforce-https-on-heroku&#34;&gt;Enforce HTTPS on Heroku&lt;/h2&gt;&#xA;&lt;p&gt;If you don&amp;rsquo;t care about a custom domain and just wish to enforce SSL for the&#xA;Heroku domain (&lt;code&gt;appname.herokuapp.com&lt;/code&gt;), &lt;code&gt;Flask-SSLify&lt;/code&gt; is the way to go. The&#xA;app can be hosted on Heroku and we can use the certs provided for free.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;flask_sslify&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SSLify&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Flask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;vm&#34;&gt;__name__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;DYNO&amp;#34;&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;environ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;# Always use SSL if the app is running on Heroku (not locally)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;sslify&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SSLify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve done this setup, or some parts of it quite a few times, but each time I&#xA;seem to need to look up the documentation. Every time, it seems to take longer&#xA;than it needs to! Hopefully, this post will make it quick and reproducible.&lt;/p&gt;&#xA;</description>
    </item>
  </channel>
</rss>
