<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SSL Archives | Clever Cloud</title>
	<atom:link href="https://www.clever.cloud/blog/tag/ssl/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.clever.cloud/blog/tag/ssl/</link>
	<description>From Code to Product</description>
	<lastBuildDate>Tue, 15 Jan 2019 17:36:00 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://cdn.clever-cloud.com/uploads/2023/03/cropped-cropped-favicon-32x32.png</url>
	<title>SSL Archives | Clever Cloud</title>
	<link>https://www.clever.cloud/blog/tag/ssl/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Let&#8217;s Encrypt Certificates For Everyone</title>
		<link>https://www.clever.cloud/blog/features/2019/01/15/automatic-lets-encrypt-certificates/</link>
		
		<dc:creator><![CDATA[Alexandre Berthaud]]></dc:creator>
		<pubDate>Tue, 15 Jan 2019 17:36:00 +0000</pubDate>
				<category><![CDATA[Features]]></category>
		<category><![CDATA[feature]]></category>
		<category><![CDATA[HTTPS]]></category>
		<category><![CDATA[SSL]]></category>
		<guid isPermaLink="false">https://www2.cleverapps.io/wp/blog/technology/2019/01/15/automatic-lets-encrypt-certificates/</guid>

					<description><![CDATA[<p><img width="1400" height="540" src="https://cdn.clever-cloud.com/uploads/2021/08/letsencrypt-1.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="letsencrypt 1" decoding="async" fetchpriority="high" srcset="https://cdn.clever-cloud.com/uploads/2021/08/letsencrypt-1.png 1400w, https://cdn.clever-cloud.com/uploads/2021/08/letsencrypt-1-300x116.png 300w, https://cdn.clever-cloud.com/uploads/2021/08/letsencrypt-1-1024x395.png 1024w, https://cdn.clever-cloud.com/uploads/2021/08/letsencrypt-1-768x296.png 768w, https://cdn.clever-cloud.com/uploads/2021/08/letsencrypt-1-1368x528.png 1368w" sizes="(max-width: 1400px) 100vw, 1400px" /></p><p>We have been issuing and automatically installing Let&#39;s Encrypt® certificates for a while now. The only manual thing was the trigger of this process. But today, we are glad to <strong>announce fully automated Let&#39;s Encrypt certificates for everyone!</strong></p>
<span id="more-2950"></span>

<p>When you add a domain — which targets Clever Cloud — to an application; it will have its own certificate a few minutes later (up to 12 minutes later).</p>
<p>This has been live since 2018-11-16. Hundreds of certificates have been issued since then. This has been possible thanks to <a href="https://letsencrypt.org">Let&#39;s Encrypt</a>, who also extended their rate limiting on their API.</p>
<h2 id="how-do-we-do-this">How do we do this?</h2>
<p>As explained in the <a href="/blog/features/2018/05/30/about-lets-encrypt/">previous blog post</a>, queries to the path used by Let&#39;s Encrypt to check the ownership of the domain are routed to our Let&#39;s Encrypt integration service. This allows us to process the Let&#39;s Encrypt queries, get the certificate and give it to our certificates manager.</p>
<p>Here is what&#39;s new. Once a user adds a new domain, we periodically check that we can reach our Let&#39;s Encrypt integration service. When we do, we start the usual process <em>et voilà</em>.</p>
<h2 id="what-if-i-want-another-kind-of-certificate">What if I want another kind of certificate?</h2>
<p>That&#39;s not a problem:</p>
<p>If you already have a certificate, we will not create a Let&#39;s Encrypt certificate.</p>
<h2 id="how-about-existing-domains">How about existing domains?</h2>
<p>Existing domains which do not yet have a certificate will all get a Let&#39;s Encrypt certificate.</p>
<p>This will be done over the next weeks to come. We can&#39;t do this in a single batch for two reasons:</p>
<ul>
<li>Let&#39;s Encrypt rate limiting (we have extended limits but we still cannot send such a big batch all at once)</li>
<li>We need to spread this out so that we don&#39;t have a big batch of renewals every 3 months</li>
</ul>
<p>If you don&#39;t want to wait, you can simply ask us to enable it.</p>
<h2 id="next-steps">Next steps</h2>
<p>There are a few things yet to come:</p>
<ul>
<li>Interface in the console to track the status of the certificates</li>
<li>Support of wildcard certificates (which will not be quite as automatic because it requires DNS validation; this will require an action from you at first)</li>
</ul>
<h2 id="one-last-thing">One last thing</h2>
<p>We are now proud sponsors of Let&#39;s Encrypt!</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1400" height="540" src="https://cdn.clever-cloud.com/uploads/2021/08/letsencrypt-1.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="letsencrypt 1" decoding="async" srcset="https://cdn.clever-cloud.com/uploads/2021/08/letsencrypt-1.png 1400w, https://cdn.clever-cloud.com/uploads/2021/08/letsencrypt-1-300x116.png 300w, https://cdn.clever-cloud.com/uploads/2021/08/letsencrypt-1-1024x395.png 1024w, https://cdn.clever-cloud.com/uploads/2021/08/letsencrypt-1-768x296.png 768w, https://cdn.clever-cloud.com/uploads/2021/08/letsencrypt-1-1368x528.png 1368w" sizes="(max-width: 1400px) 100vw, 1400px" /></p><p>We have been issuing and automatically installing Let&#39;s Encrypt® certificates for a while now. The only manual thing was the trigger of this process. But today, we are glad to <strong>announce fully automated Let&#39;s Encrypt certificates for everyone!</strong></p>
<span id="more-2950"></span>

<p>When you add a domain — which targets Clever Cloud — to an application; it will have its own certificate a few minutes later (up to 12 minutes later).</p>
<p>This has been live since 2018-11-16. Hundreds of certificates have been issued since then. This has been possible thanks to <a href="https://letsencrypt.org">Let&#39;s Encrypt</a>, who also extended their rate limiting on their API.</p>
<h2 id="how-do-we-do-this">How do we do this?</h2>
<p>As explained in the <a href="/blog/features/2018/05/30/about-lets-encrypt/">previous blog post</a>, queries to the path used by Let&#39;s Encrypt to check the ownership of the domain are routed to our Let&#39;s Encrypt integration service. This allows us to process the Let&#39;s Encrypt queries, get the certificate and give it to our certificates manager.</p>
<p>Here is what&#39;s new. Once a user adds a new domain, we periodically check that we can reach our Let&#39;s Encrypt integration service. When we do, we start the usual process <em>et voilà</em>.</p>
<h2 id="what-if-i-want-another-kind-of-certificate">What if I want another kind of certificate?</h2>
<p>That&#39;s not a problem:</p>
<p>If you already have a certificate, we will not create a Let&#39;s Encrypt certificate.</p>
<h2 id="how-about-existing-domains">How about existing domains?</h2>
<p>Existing domains which do not yet have a certificate will all get a Let&#39;s Encrypt certificate.</p>
<p>This will be done over the next weeks to come. We can&#39;t do this in a single batch for two reasons:</p>
<ul>
<li>Let&#39;s Encrypt rate limiting (we have extended limits but we still cannot send such a big batch all at once)</li>
<li>We need to spread this out so that we don&#39;t have a big batch of renewals every 3 months</li>
</ul>
<p>If you don&#39;t want to wait, you can simply ask us to enable it.</p>
<h2 id="next-steps">Next steps</h2>
<p>There are a few things yet to come:</p>
<ul>
<li>Interface in the console to track the status of the certificates</li>
<li>Support of wildcard certificates (which will not be quite as automatic because it requires DNS validation; this will require an action from you at first)</li>
</ul>
<h2 id="one-last-thing">One last thing</h2>
<p>We are now proud sponsors of Let&#39;s Encrypt!</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>About Let&#8217;s Encrypt</title>
		<link>https://www.clever.cloud/blog/features/2018/05/30/about-lets-encrypt/</link>
		
		<dc:creator><![CDATA[Alexandre Berthaud]]></dc:creator>
		<pubDate>Wed, 30 May 2018 16:15:00 +0000</pubDate>
				<category><![CDATA[Features]]></category>
		<category><![CDATA[feature]]></category>
		<category><![CDATA[HTTPS]]></category>
		<category><![CDATA[SSL]]></category>
		<guid isPermaLink="false">https://www2.cleverapps.io/wp/blog/technology/2018/05/30/about-lets-encrypt/</guid>

					<description><![CDATA[<p><img width="1400" height="540" src="https://cdn.clever-cloud.com/uploads/2021/08/aboutletsencrypt-1.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="aboutletsencrypt 1" decoding="async" srcset="https://cdn.clever-cloud.com/uploads/2021/08/aboutletsencrypt-1.png 1400w, https://cdn.clever-cloud.com/uploads/2021/08/aboutletsencrypt-1-300x116.png 300w, https://cdn.clever-cloud.com/uploads/2021/08/aboutletsencrypt-1-1024x395.png 1024w, https://cdn.clever-cloud.com/uploads/2021/08/aboutletsencrypt-1-768x296.png 768w, https://cdn.clever-cloud.com/uploads/2021/08/aboutletsencrypt-1-1368x528.png 1368w" sizes="(max-width: 1400px) 100vw, 1400px" /></p><p>We have been working on Let&#39;s Encrypt support for quite some time now.</p>
<p>We have a working prototype used for hundreds of domains and it has been running since August 2017 and has handled thousands of renewals without a single hiccup.</p>
<p>To go further, we need to make a small change to the way we route incoming requests.</p>
<span id="more-2936"></span>

<h2 id="how-does-lets-encrypt-work">How does Let&#39;s Encrypt work?</h2>
<p>Let&#39;s Encrypt needs to validate the ownership of a domain before delivering a certificate. As does any public certificate authority.</p>
<p>For our Let&#39;s Encrypt integration, we are using the HTTP challenge to validate that we own the web server that a domain points to.</p>
<p>Here is how the HTTP challenge works, basically:</p>
<ul>
<li>The client asks for a certificate for a domain (with a <a href="https://en.wikipedia.org/wiki/Certificate_signing_request">CSR</a>)</li>
<li>Let&#39;s Encrypt responds with a challenge id and a token</li>
<li>The client sets up this token to be sent when receving a request on <code>http://domain.tld/.well-known/acme-challenge/&lt;the challenge id&gt;</code></li>
<li>The client tells Let&#39;s Encrypt that it&#39;s ready</li>
<li>Let&#39;s Encrypt makes the request to <code>http://domain.tld/.well-known/acme-challenge/&lt;the challenge id&gt;</code></li>
<li>If the challenge is validated, it will then reply to the client with the certificate</li>
</ul>
<h2 id="how-does-clever-cloud-implement-this">How does Clever Cloud implement this?</h2>
<p>What we have been doing since last August is routing the <code>/.well-known/acme-challenge</code> to our Let&#39;s Encrypt integration service when a customer asks us to.</p>
<p>Sadly, this means that we have a bunch of rules in our reverse proxies to handle this. As the list of domains grow, it&#39;s becoming quite clear that this is simply not technically feasible. This huge list of rules is adding a lot of work to HAProxy&#39;s configuration parsing.</p>
<p>As we have hundreds of configuration changes per minute (batched together, but still), this has too much of an impact on the performance of our reverse proxies and the feature is not even released yet!</p>
<h2 id="what-changes">What changes</h2>
<p>Starting today, <em>all</em> requests starting with the path <code>/.well-known/acme-challenge</code> will be sent to our Let&#39;s Encrypt integration.</p>
<p>This can be disabled on dedicated reverse proxies for our <a href="https://www.clever.cloud/clever-cloud-premium">Premium</a> customers only.</p>
<h2 id="when-will-this-feature-be-available-in-the-console">When will this feature be available in the console?</h2>
<p>Right now, we only enable this on demand, domain per domain.</p>
<p>The goal, obviously, is to have an interface for this in the console and in clever-tools.</p>
<p>We still have ways to go, but the current target is by the end of this year.</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1400" height="540" src="https://cdn.clever-cloud.com/uploads/2021/08/aboutletsencrypt-1.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="aboutletsencrypt 1" decoding="async" loading="lazy" srcset="https://cdn.clever-cloud.com/uploads/2021/08/aboutletsencrypt-1.png 1400w, https://cdn.clever-cloud.com/uploads/2021/08/aboutletsencrypt-1-300x116.png 300w, https://cdn.clever-cloud.com/uploads/2021/08/aboutletsencrypt-1-1024x395.png 1024w, https://cdn.clever-cloud.com/uploads/2021/08/aboutletsencrypt-1-768x296.png 768w, https://cdn.clever-cloud.com/uploads/2021/08/aboutletsencrypt-1-1368x528.png 1368w" sizes="auto, (max-width: 1400px) 100vw, 1400px" /></p><p>We have been working on Let&#39;s Encrypt support for quite some time now.</p>
<p>We have a working prototype used for hundreds of domains and it has been running since August 2017 and has handled thousands of renewals without a single hiccup.</p>
<p>To go further, we need to make a small change to the way we route incoming requests.</p>
<span id="more-2936"></span>

<h2 id="how-does-lets-encrypt-work">How does Let&#39;s Encrypt work?</h2>
<p>Let&#39;s Encrypt needs to validate the ownership of a domain before delivering a certificate. As does any public certificate authority.</p>
<p>For our Let&#39;s Encrypt integration, we are using the HTTP challenge to validate that we own the web server that a domain points to.</p>
<p>Here is how the HTTP challenge works, basically:</p>
<ul>
<li>The client asks for a certificate for a domain (with a <a href="https://en.wikipedia.org/wiki/Certificate_signing_request">CSR</a>)</li>
<li>Let&#39;s Encrypt responds with a challenge id and a token</li>
<li>The client sets up this token to be sent when receving a request on <code>http://domain.tld/.well-known/acme-challenge/&lt;the challenge id&gt;</code></li>
<li>The client tells Let&#39;s Encrypt that it&#39;s ready</li>
<li>Let&#39;s Encrypt makes the request to <code>http://domain.tld/.well-known/acme-challenge/&lt;the challenge id&gt;</code></li>
<li>If the challenge is validated, it will then reply to the client with the certificate</li>
</ul>
<h2 id="how-does-clever-cloud-implement-this">How does Clever Cloud implement this?</h2>
<p>What we have been doing since last August is routing the <code>/.well-known/acme-challenge</code> to our Let&#39;s Encrypt integration service when a customer asks us to.</p>
<p>Sadly, this means that we have a bunch of rules in our reverse proxies to handle this. As the list of domains grow, it&#39;s becoming quite clear that this is simply not technically feasible. This huge list of rules is adding a lot of work to HAProxy&#39;s configuration parsing.</p>
<p>As we have hundreds of configuration changes per minute (batched together, but still), this has too much of an impact on the performance of our reverse proxies and the feature is not even released yet!</p>
<h2 id="what-changes">What changes</h2>
<p>Starting today, <em>all</em> requests starting with the path <code>/.well-known/acme-challenge</code> will be sent to our Let&#39;s Encrypt integration.</p>
<p>This can be disabled on dedicated reverse proxies for our <a href="https://www.clever.cloud/clever-cloud-premium">Premium</a> customers only.</p>
<h2 id="when-will-this-feature-be-available-in-the-console">When will this feature be available in the console?</h2>
<p>Right now, we only enable this on demand, domain per domain.</p>
<p>The goal, obviously, is to have an interface for this in the console and in clever-tools.</p>
<p>We still have ways to go, but the current target is by the end of this year.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Redirect to HTTPS With Play 2.4</title>
		<link>https://www.clever.cloud/blog/engineering/2015/12/01/redirect-to-https-in-play/</link>
		
		<dc:creator><![CDATA[Julien Durillon]]></dc:creator>
		<pubDate>Tue, 01 Dec 2015 17:55:00 +0000</pubDate>
				<category><![CDATA[Engineering]]></category>
		<category><![CDATA[Play!Framework]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SSL]]></category>
		<guid isPermaLink="false">https://www2.cleverapps.io/wp/blog/technology/2015/12/01/redirect-to-https-in-play/</guid>

					<description><![CDATA[<p><img width="1400" height="540" src="https://cdn.clever-cloud.com/uploads/2021/08/ssl-post-1.jpg" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="ssl post 1" decoding="async" loading="lazy" srcset="https://cdn.clever-cloud.com/uploads/2021/08/ssl-post-1.jpg 1400w, https://cdn.clever-cloud.com/uploads/2021/08/ssl-post-1-300x116.jpg 300w, https://cdn.clever-cloud.com/uploads/2021/08/ssl-post-1-1024x395.jpg 1024w, https://cdn.clever-cloud.com/uploads/2021/08/ssl-post-1-768x296.jpg 768w, https://cdn.clever-cloud.com/uploads/2021/08/ssl-post-1-1368x528.jpg 1368w" sizes="auto, (max-width: 1400px) 100vw, 1400px" /></p><p>To protect your web app or API, there is almost only one way at this time: TLS. But users and browsers don&#39;t always use TLS by default. So what you want is to redirect them to a TLS encrypted version of your site if they try to connect via plain http.</p>
<span id="more-2803"></span>
<p>Here is how to do it with Play Framework 2.4 in scala:</p>
<h2 id="play-and-http-filters">Play! and HTTP filters</h2>
<p>We will start by creating a &quot;TLSFilter.scala&quot; file and write a TLSFilter class in it:</p>
<pre><code class="language-scala">class TLSFilter extends Filter {
  def apply(nextFilter: RequestHeader =&gt; Future[Result])
    (requestHeader: RequestHeader): Future[Result] = {
      if(!requestHeader.secure)
        Future.successful(Results.MovedPermanently(&quot;https://&quot; + requestHeader.host + requestHeader.uri))
      else
        nextFilter(requestHeader).map(_.withHeaders(&quot;Strict-Transport-Security&quot; -&gt; &quot;max-age=31536000; includeSubDomains&quot;))
  }
}
</code></pre>
<p>This part is easy: we implement the apply function by just checking the <code>secure</code> value of <code>RequestHeader</code>. If the connection is not <em>secure</em>, we need to redirect the client to the same url only with &quot;https&quot; as the protocol. If the connection is <em>secure</em>, we pass the request to the next header. Nothing simpler.</p>
<p>Note that we use <code>requestHeader.host</code> instead of <code>requestHeader.domain</code> because the <code>host</code> value is actually the value of the <code>Host</code> header as set by the client, with optional port and stuff.</p>
<p>Note that we create a <code>Filter</code> implementation and not an <code>EssentialFilter</code> one because we do not care about the body.</p>
<p>Next, you need to create a <code>HttpFilters</code> implementation that will hold the instance of your <code>TLSFilter</code>:</p>
<pre><code class="language-scala">// In TLSFilter.scala
class MyFilters extends HttpFilters {
  val filters = Seq(new TLSFilter)
}
</code></pre>
<p>And finally, you need to tell Play! to use your <code>Filters</code> class:</p>
<pre><code class="language-properties"># In conf/application.conf
play.http.filters=my.package.MyFilters
</code></pre>
<p>Now it will check your requests and permamently redirect the clients to HTTPS.</p>
<p>But, how does Play! know that the request is <em>secure</em>?</p>
<h2 id="reverse-proxies-where-did-the-s-go">Reverse proxies: where did the &#39;s&#39; go?</h2>
<p>Now, we need to ensure that Play! knows to differentiate a secured connection from a plain one. If you configured HTTPS in your application, it&#39;s quite simple to understand. But it is not always the case:</p>
<p>You most probably did <em>not</em>, configure TLS in your application. And <em>that</em> is because <em>you deployed it on a very powerful and developer-friendly PaaS</em>. So, chances are your Play! application is getting requests in plain HTTP, because the TLS encryption ended at the front reverse-proxy that&#39;s piping the request towards your app.</p>
<p>How then is your application going to know that the connection is secured? Enter the non-standard and the standard ways.</p>
<h3 id="x-forwarded-proto">X-Forwarded-Proto</h3>
<p>The first, non-standard but widely used (e.g. at Clever Cloud) way to know if the request handled by the reverse proxy in front came in a secure channel is to check the <code>X-Forwarded-Proto</code> HTTP header. Like all non-standard headers, you can recognize it by the <code>X-</code> at the beginning of the name.</p>
<p>This header describes how the final client is communicating with the reverse-proxy.</p>
<p>It takes two values: <strong>http</strong> and <strong>https</strong>. You can check for that header in your application. But we will see below that Play! can do it by itself.</p>
<h3 id="rfc-2739">RFC 2739</h3>
<p>Also called the <em>Forwarded HTTP Extension</em>, it standardize the way that a proxy tells the final endpoint what is going on between the final client and itself. It&#39;s been published in June 2014.</p>
<p>You can read it here: <a href="https://tools.ietf.org/html/rfc7239">https://tools.ietf.org/html/rfc7239</a>. But the only thing that is relevant for us is the <code>proto</code> parameter. Like <code>X-Forwarded-Proto</code> earlier, its values that interest us are <strong>http</strong> and <strong>https</strong>. Like for the other one, Play! can handle those values for you, if you ask nicely.</p>
<h2 id="how-to-make-play-handle-forwarded-headers">How to make Play! handle Forwarded headers?</h2>
<p>At the time of this writing, Play! framework support for <code>Forwarded</code> headers have known many states:</p>
<ul>
<li>In Play! 1.x, you need to add <code>XForwardedSupport=all</code> in your application.conf</li>
<li>In Play! 2.0 to 2.3, you need to add <code>trustxforwarded=true</code> in your application.conf</li>
</ul>
<p>Both these ways only support the <code>X-Forwarded-Proto</code> header.</p>
<p>Now, in Play! 2.4.x, the philosophy is different:</p>
<ul>
<li>Define the version of the Forwarded header you want to use: <code>play.http.forwarded.version=x-forwarded|rfc7239</code></li>
<li>Set the proxies you trust: <code>play.http.forwarded.trustedProxies=[&quot;proxy_ip1&quot;,&quot;proxy_ip2&quot;,…]</code>.</li>
</ul>
<p>Of course <code>proxy_ipX</code> can be an actual IP or a subnet mask, like &quot;0.0.0.0&quot; or &quot;::&quot; to trust every IPv4 or v6, respectively. Defaults are &quot;127.0.0.1&quot; and &quot;::FF&quot;.</p>
<p>Also, as the <code>X-Forwarded-Proto</code> header is the one that is widely used in the world, the <code>version</code> default value is &quot;x-forwarded&quot;.</p>
<h2 id="what-the-hell-is-strict-transport-security">What the hell is Strict-Transport-Security?</h2>
<p>As you read the filter code, you must have seen that in the case the request is already in HTTPS, we still add a header to the response: <code>Strict-Transport-Security: max-age=31536000</code>.</p>
<p>This is the HTTP Strict Transport Security (HSTS) header. What it does is basically telling the client (most likely a browser): &quot;Next time (and for the next 31536000 seconds), if your user tries to load the unencrypted version of the site, don&#39;t wait for me to redirect you and use https already&quot;.</p>
<p>The browser (meaning: chrome &gt;= 4.0.211.0, firefox &gt;= 4.0, Opera &gt;= 12, IE &gt;= 11) will then save the website and automatically replace &quot;http&quot; by &quot;https&quot; in the requests the next times.</p>
<p>This mechanism is documented here: <a href="https://www.rfc-editor.org/rfc/rfc6797.txt">https://www.rfc-editor.org/rfc/rfc6797.txt</a>.</p>
<p>You <em>MUST</em> set the <code>max-age</code> value. You can also add <code>includeSubDomains</code> (after a &quot;;&quot; of course), which means &quot;if you get that header while requesting domain.com, please use HTTPS when requesting *.domain.com too&quot;. It is a good practice to always add <code>includeSubDomains</code> just in case.</p>
<p>Please note that the STS header can only be set if the website is already TLS protected. You <em>MUST NOT</em> set this header on a non-TLS response.</p>
<p>If you want the browsers to use HSTS before the first request, you can register your domain to be included in browsers <em>preload lists</em>. To achieve that, register your domain here: <a href="https://hstspreload.appspot.com/">https://hstspreload.appspot.com/</a>. Also add the <code>preload</code> value to the header, like that: <code>Strict-Transport-Security: max-age=31536000; preload</code>.</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1400" height="540" src="https://cdn.clever-cloud.com/uploads/2021/08/ssl-post-1.jpg" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="ssl post 1" decoding="async" loading="lazy" srcset="https://cdn.clever-cloud.com/uploads/2021/08/ssl-post-1.jpg 1400w, https://cdn.clever-cloud.com/uploads/2021/08/ssl-post-1-300x116.jpg 300w, https://cdn.clever-cloud.com/uploads/2021/08/ssl-post-1-1024x395.jpg 1024w, https://cdn.clever-cloud.com/uploads/2021/08/ssl-post-1-768x296.jpg 768w, https://cdn.clever-cloud.com/uploads/2021/08/ssl-post-1-1368x528.jpg 1368w" sizes="auto, (max-width: 1400px) 100vw, 1400px" /></p><p>To protect your web app or API, there is almost only one way at this time: TLS. But users and browsers don&#39;t always use TLS by default. So what you want is to redirect them to a TLS encrypted version of your site if they try to connect via plain http.</p>
<span id="more-2803"></span>
<p>Here is how to do it with Play Framework 2.4 in scala:</p>
<h2 id="play-and-http-filters">Play! and HTTP filters</h2>
<p>We will start by creating a &quot;TLSFilter.scala&quot; file and write a TLSFilter class in it:</p>
<pre><code class="language-scala">class TLSFilter extends Filter {
  def apply(nextFilter: RequestHeader =&gt; Future[Result])
    (requestHeader: RequestHeader): Future[Result] = {
      if(!requestHeader.secure)
        Future.successful(Results.MovedPermanently(&quot;https://&quot; + requestHeader.host + requestHeader.uri))
      else
        nextFilter(requestHeader).map(_.withHeaders(&quot;Strict-Transport-Security&quot; -&gt; &quot;max-age=31536000; includeSubDomains&quot;))
  }
}
</code></pre>
<p>This part is easy: we implement the apply function by just checking the <code>secure</code> value of <code>RequestHeader</code>. If the connection is not <em>secure</em>, we need to redirect the client to the same url only with &quot;https&quot; as the protocol. If the connection is <em>secure</em>, we pass the request to the next header. Nothing simpler.</p>
<p>Note that we use <code>requestHeader.host</code> instead of <code>requestHeader.domain</code> because the <code>host</code> value is actually the value of the <code>Host</code> header as set by the client, with optional port and stuff.</p>
<p>Note that we create a <code>Filter</code> implementation and not an <code>EssentialFilter</code> one because we do not care about the body.</p>
<p>Next, you need to create a <code>HttpFilters</code> implementation that will hold the instance of your <code>TLSFilter</code>:</p>
<pre><code class="language-scala">// In TLSFilter.scala
class MyFilters extends HttpFilters {
  val filters = Seq(new TLSFilter)
}
</code></pre>
<p>And finally, you need to tell Play! to use your <code>Filters</code> class:</p>
<pre><code class="language-properties"># In conf/application.conf
play.http.filters=my.package.MyFilters
</code></pre>
<p>Now it will check your requests and permamently redirect the clients to HTTPS.</p>
<p>But, how does Play! know that the request is <em>secure</em>?</p>
<h2 id="reverse-proxies-where-did-the-s-go">Reverse proxies: where did the &#39;s&#39; go?</h2>
<p>Now, we need to ensure that Play! knows to differentiate a secured connection from a plain one. If you configured HTTPS in your application, it&#39;s quite simple to understand. But it is not always the case:</p>
<p>You most probably did <em>not</em>, configure TLS in your application. And <em>that</em> is because <em>you deployed it on a very powerful and developer-friendly PaaS</em>. So, chances are your Play! application is getting requests in plain HTTP, because the TLS encryption ended at the front reverse-proxy that&#39;s piping the request towards your app.</p>
<p>How then is your application going to know that the connection is secured? Enter the non-standard and the standard ways.</p>
<h3 id="x-forwarded-proto">X-Forwarded-Proto</h3>
<p>The first, non-standard but widely used (e.g. at Clever Cloud) way to know if the request handled by the reverse proxy in front came in a secure channel is to check the <code>X-Forwarded-Proto</code> HTTP header. Like all non-standard headers, you can recognize it by the <code>X-</code> at the beginning of the name.</p>
<p>This header describes how the final client is communicating with the reverse-proxy.</p>
<p>It takes two values: <strong>http</strong> and <strong>https</strong>. You can check for that header in your application. But we will see below that Play! can do it by itself.</p>
<h3 id="rfc-2739">RFC 2739</h3>
<p>Also called the <em>Forwarded HTTP Extension</em>, it standardize the way that a proxy tells the final endpoint what is going on between the final client and itself. It&#39;s been published in June 2014.</p>
<p>You can read it here: <a href="https://tools.ietf.org/html/rfc7239">https://tools.ietf.org/html/rfc7239</a>. But the only thing that is relevant for us is the <code>proto</code> parameter. Like <code>X-Forwarded-Proto</code> earlier, its values that interest us are <strong>http</strong> and <strong>https</strong>. Like for the other one, Play! can handle those values for you, if you ask nicely.</p>
<h2 id="how-to-make-play-handle-forwarded-headers">How to make Play! handle Forwarded headers?</h2>
<p>At the time of this writing, Play! framework support for <code>Forwarded</code> headers have known many states:</p>
<ul>
<li>In Play! 1.x, you need to add <code>XForwardedSupport=all</code> in your application.conf</li>
<li>In Play! 2.0 to 2.3, you need to add <code>trustxforwarded=true</code> in your application.conf</li>
</ul>
<p>Both these ways only support the <code>X-Forwarded-Proto</code> header.</p>
<p>Now, in Play! 2.4.x, the philosophy is different:</p>
<ul>
<li>Define the version of the Forwarded header you want to use: <code>play.http.forwarded.version=x-forwarded|rfc7239</code></li>
<li>Set the proxies you trust: <code>play.http.forwarded.trustedProxies=[&quot;proxy_ip1&quot;,&quot;proxy_ip2&quot;,…]</code>.</li>
</ul>
<p>Of course <code>proxy_ipX</code> can be an actual IP or a subnet mask, like &quot;0.0.0.0&quot; or &quot;::&quot; to trust every IPv4 or v6, respectively. Defaults are &quot;127.0.0.1&quot; and &quot;::FF&quot;.</p>
<p>Also, as the <code>X-Forwarded-Proto</code> header is the one that is widely used in the world, the <code>version</code> default value is &quot;x-forwarded&quot;.</p>
<h2 id="what-the-hell-is-strict-transport-security">What the hell is Strict-Transport-Security?</h2>
<p>As you read the filter code, you must have seen that in the case the request is already in HTTPS, we still add a header to the response: <code>Strict-Transport-Security: max-age=31536000</code>.</p>
<p>This is the HTTP Strict Transport Security (HSTS) header. What it does is basically telling the client (most likely a browser): &quot;Next time (and for the next 31536000 seconds), if your user tries to load the unencrypted version of the site, don&#39;t wait for me to redirect you and use https already&quot;.</p>
<p>The browser (meaning: chrome &gt;= 4.0.211.0, firefox &gt;= 4.0, Opera &gt;= 12, IE &gt;= 11) will then save the website and automatically replace &quot;http&quot; by &quot;https&quot; in the requests the next times.</p>
<p>This mechanism is documented here: <a href="https://www.rfc-editor.org/rfc/rfc6797.txt">https://www.rfc-editor.org/rfc/rfc6797.txt</a>.</p>
<p>You <em>MUST</em> set the <code>max-age</code> value. You can also add <code>includeSubDomains</code> (after a &quot;;&quot; of course), which means &quot;if you get that header while requesting domain.com, please use HTTPS when requesting *.domain.com too&quot;. It is a good practice to always add <code>includeSubDomains</code> just in case.</p>
<p>Please note that the STS header can only be set if the website is already TLS protected. You <em>MUST NOT</em> set this header on a non-TLS response.</p>
<p>If you want the browsers to use HSTS before the first request, you can register your domain to be included in browsers <em>preload lists</em>. To achieve that, register your domain here: <a href="https://hstspreload.appspot.com/">https://hstspreload.appspot.com/</a>. Also add the <code>preload</code> value to the header, like that: <code>Strict-Transport-Security: max-age=31536000; preload</code>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
