<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Jimmy Morin — Blog</title><description>Field notes on cloud architecture, Terraform, AWS, SRE and FinOps — practical write-ups from real infrastructure work.</description><link>https://jimmymorin.cloud</link><language>en</language><item><title>Privacy-friendly site analytics with CloudFront logs and Athena</title><link>https://jimmymorin.cloud/posts/cloudfront-logs-athena-analytics</link><guid isPermaLink="true">https://jimmymorin.cloud/posts/cloudfront-logs-athena-analytics</guid><description>No tracking script, no cookie banner: query your CloudFront access logs with Athena to see what people read, for a few cents a month.</description><pubDate>Thu, 08 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For a personal site, a JavaScript analytics tag is often overkill: it slows pages down, needs a consent banner in the EU, and gets blocked by a large share of visitors anyway. CloudFront already records every request. You just need a way to ask questions of those logs.&lt;/p&gt;
&lt;h2 id=&quot;the-setup&quot;&gt;The setup&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Enable &lt;strong&gt;standard logging&lt;/strong&gt; on the distribution, writing to a dedicated S3 bucket.&lt;/li&gt;
&lt;li&gt;Create an &lt;strong&gt;Athena&lt;/strong&gt; table over that bucket.&lt;/li&gt;
&lt;li&gt;Query with SQL when you are curious.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There is nothing to run between queries, so the cost is S3 storage plus Athena’s per-query data scanned — negligible at personal-site traffic. Add an S3 lifecycle rule so old logs expire.&lt;/p&gt;
&lt;h2 id=&quot;the-table&quot;&gt;The table&lt;/h2&gt;
&lt;p&gt;CloudFront standard logs are gzipped, tab-separated files with two header lines. Columns must be declared in order; trailing ones you don’t need can be left off.&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot; data-language=&quot;sql&quot;&gt;&lt;code is:raw=&quot;&quot; class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; EXTERNAL &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;EXISTS&lt;/span&gt; cloudfront_logs &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;        &lt;span class=&quot;token keyword&quot;&gt;DATE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token identifier&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;time&lt;span class=&quot;token punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;        STRING&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  location      STRING&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  bytes         &lt;span class=&quot;token keyword&quot;&gt;BIGINT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  request_ip    STRING&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  method        STRING&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  host          STRING&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  uri           STRING&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;status&lt;/span&gt;        &lt;span class=&quot;token keyword&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  referrer      STRING&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  user_agent    STRING&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  query_string  STRING&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  cookie        STRING&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  result_type   STRING&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  request_id    STRING&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  host_header   STRING&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  request_protocol STRING&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  request_bytes &lt;span class=&quot;token keyword&quot;&gt;BIGINT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  time_taken    &lt;span class=&quot;token keyword&quot;&gt;FLOAT&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ROW&lt;/span&gt; FORMAT DELIMITED &lt;span class=&quot;token keyword&quot;&gt;FIELDS&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TERMINATED&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;\t&apos;&lt;/span&gt;
LOCATION &lt;span class=&quot;token string&quot;&gt;&apos;s3://my-cloudfront-logs/site/&apos;&lt;/span&gt;
TBLPROPERTIES &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;skip.header.line.count&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;2&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;questions-worth-asking&quot;&gt;Questions worth asking&lt;/h2&gt;
&lt;p&gt;Most-read articles over the last 30 days, ignoring assets and bots:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot; data-language=&quot;sql&quot;&gt;&lt;code is:raw=&quot;&quot; class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; uri&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; views
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; cloudfront_logs
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;date&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;current_date&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INTERVAL&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;30&apos;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DAY&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; uri &lt;span class=&quot;token operator&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/blog/%&apos;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; uri &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;%.%&apos;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; lower&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user_agent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;%bot%&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;GROUP&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; uri
&lt;span class=&quot;token keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; views &lt;span class=&quot;token keyword&quot;&gt;DESC&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Swap the &lt;code&gt;GROUP BY&lt;/code&gt; for &lt;code&gt;referrer&lt;/code&gt; to see where readers come from, or for &lt;code&gt;result_type&lt;/code&gt; to check your cache hit ratio.&lt;/p&gt;
&lt;h2 id=&quot;trade-offs&quot;&gt;Trade-offs&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;It counts &lt;strong&gt;requests&lt;/strong&gt;, not people. That’s fine for “what do readers care about?”.&lt;/li&gt;
&lt;li&gt;Logs land with a delay, so this is not real-time.&lt;/li&gt;
&lt;li&gt;IP addresses are personal data under GDPR: keep retention short and restrict who can query the bucket.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a blog, that’s a fair deal: no script, no cookies, full ownership of the data.&lt;/p&gt;</content:encoded><category>aws</category><category>cloudfront</category><category>athena</category><category>finops</category></item><item><title>Why your CSS won’t load from S3 behind CloudFront</title><link>https://jimmymorin.cloud/posts/s3-cloudfront-css-mime-types-terraform</link><guid isPermaLink="true">https://jimmymorin.cloud/posts/s3-cloudfront-css-mime-types-terraform</guid><description>Unstyled pages after a Terraform deploy to S3 + CloudFront? The culprit is usually the Content-Type. Here is how to set it reliably for every file.</description><pubDate>Thu, 01 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You deploy a static site to S3, put CloudFront in front of it, open the URL — and get a page of unstyled HTML. The CSS file is there, the path is right, the browser even downloads it. It just refuses to apply it.&lt;/p&gt;
&lt;h2 id=&quot;the-symptom&quot;&gt;The symptom&lt;/h2&gt;
&lt;p&gt;Open the browser console and you will see something like:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot; data-language=&quot;text&quot;&gt;&lt;code is:raw=&quot;&quot; class=&quot;language-text&quot;&gt;Refused to apply style from &apos;https://example.com/_astro/index.css&apos;
because its MIME type (&apos;binary/octet-stream&apos;) is not a supported stylesheet MIME type.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The browser is doing exactly what it should. With &lt;code&gt;X-Content-Type-Options: nosniff&lt;/code&gt; (which you &lt;em&gt;want&lt;/em&gt; on), it will only apply a stylesheet served as &lt;code&gt;text/css&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-cause&quot;&gt;The cause&lt;/h2&gt;
&lt;p&gt;When Terraform uploads files with &lt;code&gt;aws_s3_object&lt;/code&gt;, S3 stores whatever &lt;code&gt;content_type&lt;/code&gt; you give it. Give it nothing and you get &lt;code&gt;binary/octet-stream&lt;/code&gt; — for every file. CloudFront then faithfully serves that header to the browser.&lt;/p&gt;
&lt;h2 id=&quot;the-fix-a-mime-map&quot;&gt;The fix: a MIME map&lt;/h2&gt;
&lt;p&gt;Derive the extension from each file name and look it up in a map:&lt;/p&gt;
&lt;pre class=&quot;language-hcl&quot; data-language=&quot;hcl&quot;&gt;&lt;code is:raw=&quot;&quot; class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;site_dir&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/../../apps/web/dist&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;mime_types&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;html&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;text/html; charset=utf-8&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;css&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;text/css; charset=utf-8&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;js&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;text/javascript; charset=utf-8&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;json&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;xml&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/xml&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;txt&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;text/plain; charset=utf-8&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;md&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;text/markdown; charset=utf-8&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;svg&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;image/svg+xml&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;png&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;image/png&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;webp&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;image/webp&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;avif&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;image/avif&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;woff2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;font/woff2&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_s3_object&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;site&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;for_each&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; fileset(local.site_dir, &lt;span class=&quot;token string&quot;&gt;&quot;**&quot;&lt;/span&gt;)

  &lt;span class=&quot;token property&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_s3_bucket.site.id
  &lt;span class=&quot;token property&quot;&gt;key&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value
  &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;site_dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;each&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;etag&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; filemd5(&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;local&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;site_dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;each&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;)

  &lt;span class=&quot;token property&quot;&gt;content_type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; lookup(
    local.mime_types,
    element(split(&lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;, each.value), length(split(&lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;, each.value)) - &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;),
    &lt;span class=&quot;token string&quot;&gt;&quot;application/octet-stream&quot;&lt;/span&gt;,
  )
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;element(split(...))&lt;/code&gt; expression takes the &lt;strong&gt;last&lt;/strong&gt; segment after a dot, so &lt;code&gt;index.4f2a.css&lt;/code&gt; resolves to &lt;code&gt;css&lt;/code&gt;, not &lt;code&gt;4f2a&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;check-it&quot;&gt;Check it&lt;/h2&gt;
&lt;pre class=&quot;language-bash&quot; data-language=&quot;bash&quot;&gt;&lt;code is:raw=&quot;&quot; class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-sI&lt;/span&gt; https://example.com/_astro/index.css &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; content-type
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If CloudFront still serves the old header, remember it cached the old object: invalidate the path (or deploy with hashed file names, which Astro does for you).&lt;/p&gt;
&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;S3 never guesses MIME types for you when uploading through the API.&lt;/li&gt;
&lt;li&gt;Keep &lt;code&gt;nosniff&lt;/code&gt; on; fix the metadata instead.&lt;/li&gt;
&lt;li&gt;Put the MIME map in your static-site module once, and every site you deploy gets it right.&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>aws</category><category>terraform</category><category>s3</category><category>cloudfront</category></item></channel></rss>