<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: upa_rupa</title>
    <description>The latest articles on DEV Community by upa_rupa (@upa_rupa).</description>
    <link>https://dev.to/upa_rupa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3850920%2Fab72f636-b73b-4119-8e55-521257ff3406.jpg</url>
      <title>DEV Community: upa_rupa</title>
      <link>https://dev.to/upa_rupa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/upa_rupa"/>
    <language>en</language>
    <item>
      <title>How to Match Only `/` in CloudFront Using `/?*`</title>
      <dc:creator>upa_rupa</dc:creator>
      <pubDate>Tue, 21 Apr 2026 10:30:52 +0000</pubDate>
      <link>https://dev.to/upa_rupa/how-to-match-only-in-cloudfront-using--14gh</link>
      <guid>https://dev.to/upa_rupa/how-to-match-only-in-cloudfront-using--14gh</guid>
      <description>&lt;h1&gt;
  
  
  How to Match Only &lt;code&gt;/&lt;/code&gt; in CloudFront Using &lt;code&gt;/?*&lt;/code&gt;
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When building a site on AWS CloudFront, a common requirement arises: "I want to route only the root (&lt;code&gt;/&lt;/code&gt;) to a specific origin, or apply unique logic strictly to that path."&lt;/p&gt;

&lt;p&gt;The challenge is that CloudFront’s path patterns lack a way to match exactly &lt;code&gt;/&lt;/code&gt;. Typically, you end up relying on the Default Behavior (&lt;code&gt;*&lt;/code&gt;), which captures everything from scripts to styles. This makes it difficult to separate caching strategies or route requests to different origins cleanly.&lt;/p&gt;

&lt;p&gt;This article explores a clean way to solve this by taking advantage of the specific behavior of the &lt;code&gt;?&lt;/code&gt; wildcard: &lt;strong&gt;using the &lt;code&gt;/?*&lt;/code&gt; pattern to isolate the root path.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Constraint: CloudFront Path Patterns
&lt;/h2&gt;

&lt;p&gt;CloudFront’s routing relies on path patterns with specific limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No regular expressions&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two available wildcards&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;*&lt;/code&gt;: Matches any string of 0 or more characters.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;?&lt;/code&gt;: Matches &lt;strong&gt;exactly one character&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Priority-based evaluation&lt;/strong&gt;: The first pattern to match a request wins.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Within these constraints, the goal is to find a way to express “everything except the root” so the root itself can be handled independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Pattern Works
&lt;/h2&gt;

&lt;p&gt;The key lies in the strict "exactly one character" rule of the &lt;code&gt;?&lt;/code&gt; wildcard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Breaking Down the Pattern
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;/?*&lt;/code&gt; pattern works as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;/&lt;/code&gt; — A literal slash.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;?&lt;/code&gt; — &lt;strong&gt;Exactly one character&lt;/strong&gt; (required).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;*&lt;/code&gt; — Any string of 0 or more characters.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means that &lt;code&gt;/?*&lt;/code&gt; matches &lt;strong&gt;any path that has at least one character following the initial slash.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  💡 The Mechanism
&lt;/h3&gt;

&lt;p&gt;By assigning &lt;code&gt;/?*&lt;/code&gt; a &lt;strong&gt;higher priority&lt;/strong&gt;, you ensure that only the root request (&lt;code&gt;/&lt;/code&gt;)—which has zero characters after the slash—does not match and falls through to the Default Behavior (&lt;code&gt;*&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Matching Results
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Matches &lt;code&gt;/?*&lt;/code&gt;?&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/index.html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;Matches &lt;code&gt;/&lt;/code&gt; + &lt;code&gt;i&lt;/code&gt; + &lt;code&gt;ndex.html&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/main.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;Matches &lt;code&gt;/&lt;/code&gt; + &lt;code&gt;m&lt;/code&gt; + &lt;code&gt;ain.js&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/assets/style.css&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;At least one character follows the slash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;/&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ &lt;strong&gt;No&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Zero characters follow the slash&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Fine-Grained Routing: SSR vs. Static Assets
&lt;/h2&gt;

&lt;p&gt;The most practical application is a &lt;strong&gt;hybrid setup&lt;/strong&gt; (e.g., Astro, Next.js), where you need to separate dynamic rendering from static delivery.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/&lt;/code&gt; (Root)&lt;/strong&gt;: Route to a dynamic origin (Lambda, App Runner, etc.) to serve dynamic content and generate SEO metadata.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything else (&lt;code&gt;/assets/*&lt;/code&gt;, &lt;code&gt;/favicon.ico&lt;/code&gt;, etc.)&lt;/strong&gt;: Serve directly from S3 for maximum performance and lower costs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without this pattern, you’d need to manually enumerate every file extension. With &lt;code&gt;/?*&lt;/code&gt;, you get a robust, catch-all solution for assets.&lt;/p&gt;

&lt;h3&gt;
  
  
  CDK Implementation Example
&lt;/h3&gt;

&lt;p&gt;This approach keeps your infrastructure-as-code clean and easy to reason about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cloudfront&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-cloudfront&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;origins&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-cloudfront-origins&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;distribution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cloudfront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Distribution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyHybridDist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Default (*) Behavior — Lowest Priority&lt;/span&gt;
  &lt;span class="c1"&gt;// Only the root (/) falls through to here.&lt;/span&gt;
  &lt;span class="na"&gt;defaultBehavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;origins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HttpOrigin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ssr-api.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="na"&gt;cachePolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cloudfront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CachePolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CACHING_DISABLED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// SSR requires fresh data&lt;/span&gt;
    &lt;span class="na"&gt;viewerProtocolPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cloudfront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ViewerProtocolPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIRECT_TO_HTTPS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;additionalBehaviors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 2. Everything except root (/?*) — Higher Priority&lt;/span&gt;
    &lt;span class="c1"&gt;// Serve and cache all static assets from S3.&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/?*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;origins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3BucketOrigin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withOriginAccessIdentity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;assetBucket&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;cachePolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cloudfront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CachePolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CACHING_OPTIMIZED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;viewerProtocolPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cloudfront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ViewerProtocolPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIRECT_TO_HTTPS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Strategic Advantages
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Maintenance Asset Routing&lt;/strong&gt;: No need to update your config when adding new file types (&lt;code&gt;.webp&lt;/code&gt;, &lt;code&gt;.avif&lt;/code&gt;, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architectural Clarity&lt;/strong&gt;: The logic for your "Entry Point" and your "Assets" is split cleanly at the edge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimized Caching&lt;/strong&gt;: Apply a &lt;code&gt;no-cache&lt;/code&gt; policy to your dynamic root while keeping aggressive, long-term caching for everything else.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  ⚠️ A Subtle but Crucial Limitation
&lt;/h2&gt;

&lt;p&gt;When combining this with CloudFront Functions, keep in mind:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rewriting &lt;code&gt;request.uri&lt;/code&gt; within a Function does not trigger a re-evaluation of cache behaviors.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If a request hits the Default Behavior (&lt;code&gt;*&lt;/code&gt;) and your Function rewrites the URI to &lt;code&gt;/index.html&lt;/code&gt;, CloudFront will &lt;strong&gt;not&lt;/strong&gt; move that request over to the &lt;code&gt;/?*&lt;/code&gt; behavior. Execution stays within the behavior that matched the original request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;?&lt;/code&gt; wildcard in CloudFront is often overlooked, but it can be surprisingly powerful. Its strictness allows for an elegant implementation of &lt;strong&gt;path-based negation&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Whether you are managing SSR setups, multilingual redirects, or maintenance pages, &lt;code&gt;/?*&lt;/code&gt; is a reliable tool for handling the root path as a first-class citizen in your routing logic.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was originally published in Japanese on &lt;a href="https://archelon-inc.jp/blog/cloudfront-root-pattern" rel="noopener noreferrer"&gt;archelon-inc.jp&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudfront</category>
      <category>cdk</category>
      <category>typescript</category>
    </item>
    <item>
      <title>The "Reply to Author" Pitfall in Google Groups — Why Your Messages Seem to Disappear</title>
      <dc:creator>upa_rupa</dc:creator>
      <pubDate>Tue, 21 Apr 2026 10:01:18 +0000</pubDate>
      <link>https://dev.to/upa_rupa/the-reply-to-author-pitfall-in-google-groups-why-your-messages-seem-to-disappear-3mkh</link>
      <guid>https://dev.to/upa_rupa/the-reply-to-author-pitfall-in-google-groups-why-your-messages-seem-to-disappear-3mkh</guid>
      <description>&lt;h1&gt;
  
  
  The "Reply to Author" Pitfall in Google Groups — Why Your Messages Seem to Disappear
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the Google Groups web interface, you’ll see three main reply options:&lt;/p&gt;

&lt;p&gt;"Reply to all," "Reply to author," and "Forward."&lt;/p&gt;

&lt;p&gt;Have you ever used &lt;strong&gt;"Reply to author"&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;There’s one important thing to keep in mind: &lt;strong&gt;the messages you send aren’t automatically saved for you to access later.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Happens
&lt;/h2&gt;

&lt;p&gt;When you click "Reply to author" in Google Groups, it opens a window to send a direct email to the original poster. You write your message, hit send—and then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It won’t appear in the group archive&lt;/strong&gt; — because it’s treated as a private email.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It won’t appear in your Gmail Sent folder&lt;/strong&gt; — because the message is sent through Google Groups rather than your own Gmail account.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, &lt;strong&gt;you won’t have any way to review that message afterward.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since there’s no record in your "Sent" folder or the group history, you can’t double-check what you wrote or confirm it was successfully delivered. You’re left waiting for a response without a paper trail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does This Happen?
&lt;/h2&gt;

&lt;p&gt;Google Groups was originally designed as a &lt;strong&gt;bulletin board system&lt;/strong&gt;, but many people use it like a mailing list. Because the interface feels like email, it’s natural to expect sent messages to be saved—this is where the confusion starts.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;How it's handled&lt;/th&gt;
&lt;th&gt;History&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reply to all&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Posted to the entire group&lt;/td&gt;
&lt;td&gt;Saved in the group archive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reply to author&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sent as a private message&lt;/td&gt;
&lt;td&gt;Not saved in the group or Gmail&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"Reply to author" sends a direct email through the Google Groups platform. Since your own email client isn't the one sending it, no copy is kept in your Sent folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch Out for Delivery Settings
&lt;/h2&gt;

&lt;p&gt;Your email delivery settings can make this even easier to run into.&lt;/p&gt;

&lt;p&gt;If your subscription is set to &lt;strong&gt;"Digest,"&lt;/strong&gt; multiple posts are bundled into a single email. To reply to a specific post, you often have to open the Google Groups web interface—which makes it much more likely you’ll click "Reply to author" by mistake.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Known Issue for Users Worldwide
&lt;/h2&gt;

&lt;p&gt;This is a well-documented issue. The Google Help Community contains many threads from users reporting this exact confusion.&lt;/p&gt;

&lt;p&gt;For example, a thread titled "How can I see replies to author (a la Sent Messages)" has over 130 "I have the same question" votes. A Gold Product Expert confirmed that Google Groups does not provide a Sent folder or a built-in way to review these messages.&lt;/p&gt;

&lt;p&gt;Even the official help page, "Compose or reply to a message," explains how to use the feature but doesn't explicitly mention that your messages won't be saved.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Avoid This
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Reliable Fix: Reply via Gmail
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Avoid using "Reply to author" on the web interface.&lt;/strong&gt; Instead, open the notification email in your Gmail inbox and reply directly from there.&lt;/p&gt;

&lt;p&gt;When you send from Gmail, the message is saved in your Sent folder just like any other email.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Workarounds
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BCC yourself&lt;/strong&gt; (if the option is available)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy and save your message&lt;/strong&gt; before hitting send&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manually add your own email address&lt;/strong&gt; as a recipient&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The "Reply to author" button might look convenient, but it lacks the basic record-keeping we expect from modern email. To ensure you always have a copy of your correspondence, &lt;strong&gt;get into the habit of replying directly from your email client.&lt;/strong&gt; Don't let your important messages become "sent and forgotten" without a trace!&lt;/p&gt;

</description>
      <category>googleworkspace</category>
      <category>gmail</category>
      <category>tips</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Getting 404 Errors After Building a Teams Tab App? HTML Caching Might Be the Cause</title>
      <dc:creator>upa_rupa</dc:creator>
      <pubDate>Tue, 31 Mar 2026 10:47:14 +0000</pubDate>
      <link>https://dev.to/upa_rupa/getting-404-errors-after-building-a-teams-tab-app-html-caching-might-be-the-cause-1i98</link>
      <guid>https://dev.to/upa_rupa/getting-404-errors-after-building-a-teams-tab-app-html-caching-might-be-the-cause-1i98</guid>
      <description>&lt;h1&gt;
  
  
  Getting 404 Errors After Building a Teams Tab App? HTML Caching Might Be the Cause
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When developing Microsoft Teams tab apps, there's a frustrating issue you may run into during local development.&lt;/p&gt;

&lt;p&gt;After modifying frontend code and rebuilding, Vite/Rollup adds a content hash to filenames (e.g., &lt;code&gt;TeamsInitializer.bBRVpIft.js&lt;/code&gt;). However, if the browser (Teams WebView) has cached the HTML, the old HTML continues to reference the old hashed filenames, which results in &lt;strong&gt;404 errors&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this article, we'll share a solution we discovered by digging into the internal structure of the Teams SDK v2 local server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Solutions and Their Drawbacks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Approach 1: Remove Hashes from Filenames
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// astro.config.mjs or vite.config.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;vite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;rollupOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;entryFileNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assets/[name].js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;chunkFileNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assets/[name].js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;assetFileNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assets/[name].[ext]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Drawback&lt;/strong&gt;: Removing hashes means the browser may cache files indefinitely, preventing code updates from being reflected. You lose the benefits of cache busting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 2: Use the Dev Server (HMR)
&lt;/h3&gt;

&lt;p&gt;Using Vite or Astro's dev server with HMR (Hot Module Replacement) avoids caching issues altogether.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Drawback&lt;/strong&gt;: Teams apps are typically configured so that the Teams client (WebView) accesses a specific port, making it difficult to use the dev server's separate port directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Investigating the Teams SDK v2 Internals
&lt;/h2&gt;

&lt;p&gt;When looking into the &lt;code&gt;@microsoft/teams.apps&lt;/code&gt; package in Teams SDK v2, we discovered that &lt;code&gt;HttpPlugin&lt;/code&gt; uses &lt;strong&gt;Express&lt;/strong&gt; internally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Excerpt from node_modules/@microsoft/teams.apps/dist/plugins/http/plugin.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;__importDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;HttpPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HttpPlugin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;express_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)();&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ← use() is exposed!&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;express_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key findings:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;HttpPlugin&lt;/code&gt; holds an internal Express instance&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;use()&lt;/code&gt; method is exposed and bound to Express's &lt;code&gt;app.use()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;This means &lt;strong&gt;we can add arbitrary Express middleware&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Solution: Set Cache-Control Headers via Middleware
&lt;/h2&gt;

&lt;p&gt;Leveraging this discovery, we add middleware to disable HTML caching only during local development.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/index.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HttpPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@microsoft/teams.apps&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ConsoleLogger&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@microsoft/teams.common/logging&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sslOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SSL_KEY_FILE&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SSL_KEY_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SSL_CRT_FILE&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SSL_CRT_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;httpPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;sslOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cert&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;sslOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sslOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Disable HTML caching for local development only&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RUNNING_ON_AZURE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;httpPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/tabs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Disable caching for HTML files and index.html requests&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-cache, no-store, must-revalidate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Pragma&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-cache&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Expires&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IPlugin&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;httpPlugin&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tab&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;debug&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;home&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3978&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Points
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Environment-based control&lt;/strong&gt;: The middleware is only added when the &lt;code&gt;RUNNING_ON_AZURE&lt;/code&gt; environment variable is not set (i.e., during local development)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path matching&lt;/strong&gt;: Paths ending in &lt;code&gt;.html&lt;/code&gt;, the root path, and paths without extensions (SPA routing) are treated as HTML requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache-busting headers&lt;/strong&gt;: Three headers — &lt;code&gt;Cache-Control&lt;/code&gt;, &lt;code&gt;Pragma&lt;/code&gt;, and &lt;code&gt;Expires&lt;/code&gt; — are set to ensure caching is fully disabled&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Benefits of This Approach
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Hashed filenames are preserved&lt;/strong&gt;: JS/CSS files keep their hashes, so production environments' caching efficiency is unaffected&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No impact on production environments&lt;/strong&gt;: Since it's controlled by an environment variable, the middleware is not added during Azure deployments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensible&lt;/strong&gt;: The same technique can be used to add logging, authentication, custom headers, and more&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Use Case: Adding Other Middleware
&lt;/h2&gt;

&lt;p&gt;Using this technique, you can add various middleware to the Teams SDK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Add request logging&lt;/span&gt;
&lt;span class="nx"&gt;httpPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Add custom headers&lt;/span&gt;
&lt;span class="nx"&gt;httpPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-Custom-Header&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Add authentication for specific paths&lt;/span&gt;
&lt;span class="nx"&gt;httpPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This approach relies on the internal implementation details of Teams SDK v2 (&lt;code&gt;@microsoft/teams.apps&lt;/code&gt; v2.x)&lt;/li&gt;
&lt;li&gt;The API may change in future versions&lt;/li&gt;
&lt;li&gt;This usage is not documented in the official documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We discovered that Teams SDK v2's &lt;code&gt;HttpPlugin&lt;/code&gt; uses Express internally and exposes the &lt;code&gt;use()&lt;/code&gt; method. By taking advantage of this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML caching issues during local development are resolved&lt;/li&gt;
&lt;li&gt;Cache-busting via hashed filenames is preserved&lt;/li&gt;
&lt;li&gt;Development experience is improved without affecting production environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We hope this helps anyone working with Teams SDK v2 who runs into the same issue.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was originally published in Japanese at &lt;a href="https://archelon-inc.jp/blog/teams-sdk-v2-httpplugin-cache-control" rel="noopener noreferrer"&gt;archelon-inc.jp&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>microsoftteams</category>
      <category>javascript</category>
      <category>express</category>
      <category>caching</category>
    </item>
    <item>
      <title>How to Change the Emoji Font to Noto Color Emoji in VS Code on macOS</title>
      <dc:creator>upa_rupa</dc:creator>
      <pubDate>Tue, 31 Mar 2026 10:26:33 +0000</pubDate>
      <link>https://dev.to/upa_rupa/how-to-change-the-emoji-font-to-noto-color-emoji-in-vs-code-on-macos-6ka</link>
      <guid>https://dev.to/upa_rupa/how-to-change-the-emoji-font-to-noto-color-emoji-in-vs-code-on-macos-6ka</guid>
      <description>&lt;h1&gt;
  
  
  How to Change the Emoji Font to Noto Color Emoji in VS Code on macOS
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Have you ever wanted to change the look of emojis displayed in VS Code?&lt;/p&gt;

&lt;p&gt;On macOS, &lt;strong&gt;Apple Color Emoji&lt;/strong&gt; is used by default for emoji rendering. While Apple's emoji designs are polished, some people prefer Google's &lt;strong&gt;Noto Color Emoji&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, many online sources claim that "Noto Color Emoji doesn't work in VS Code on macOS."&lt;/p&gt;

&lt;p&gt;In this article, we'll show you &lt;strong&gt;how to use Noto Color Emoji in VS Code on macOS&lt;/strong&gt;, based on steps we tested and verified on macOS (Apple Silicon).&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;It's surprisingly simple — just two steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the Noto Color Emoji font&lt;/li&gt;
&lt;li&gt;Update the font settings in VS Code's &lt;code&gt;settings.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;No Custom CSS extensions or system-level font configuration changes are needed.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Install Noto Color Emoji
&lt;/h3&gt;

&lt;p&gt;Install it using Homebrew:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;font-noto-color-emoji
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Update VS Code Font Settings
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;settings.json&lt;/code&gt; and configure as follows:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Editor:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"editor.fontFamily"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your Font Name, monospace, Noto Color Emoji"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Terminal:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"terminal.integrated.fontFamily"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your Font Name, monospace, Noto Color Emoji"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart VS Code after making these changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Point: Don't Use Single Quotes
&lt;/h3&gt;

&lt;p&gt;Normally, font names containing spaces are often wrapped in single quotes (e.g., &lt;code&gt;'Noto Color Emoji'&lt;/code&gt;). However, in our testing, it worked correctly &lt;strong&gt;without single quotes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When quotes are added, macOS's system font fallback may prioritize Apple Color Emoji. Omitting the quotes allows VS Code (which is Chromium-based) to correctly resolve and use Noto Color Emoji.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approaches That Didn't Work
&lt;/h2&gt;

&lt;p&gt;During testing, we also tried the following approaches, but they were ultimately unnecessary:&lt;/p&gt;

&lt;h3&gt;
  
  
  Overriding with Custom CSS Extension
&lt;/h3&gt;

&lt;p&gt;You might consider using &lt;code&gt;vscode_custom_css.imports&lt;/code&gt; to replace Apple Color Emoji with Noto Color Emoji via &lt;code&gt;@font-face&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'Apple Color Emoji'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'Noto Color Emoji'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this worked for the editor area, it doesn't affect the terminal because the terminal renders via Canvas/WebGL, which means CSS overrides are not applied. In the end, the &lt;code&gt;editor.fontFamily&lt;/code&gt; setting alone was sufficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  System-Level Font Changes on macOS
&lt;/h3&gt;

&lt;p&gt;Modifying CoreText's fallback settings is another option, but it risks breaking with macOS updates and affects all applications, so we don't recommend it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Method Isn't Well Known
&lt;/h2&gt;

&lt;p&gt;VS Code is an Electron (Chromium)-based application. Chromium's font fallback handling can behave differently from macOS native applications.&lt;/p&gt;

&lt;p&gt;Many articles explain that "Apple Color Emoji is prioritized at the system level on macOS, so it can't be changed." This is generally true for native macOS applications. However, in Chromium-based apps like VS Code, &lt;code&gt;font-family&lt;/code&gt; specifications are more directly reflected in font selection, which is why this method works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Environment
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS&lt;/strong&gt;: Darwin arm64 25.1.0 (Apple Silicon)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VS Code&lt;/strong&gt;: 1.109.4 (Universal)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Electron&lt;/strong&gt;: 39.3.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chromium&lt;/strong&gt;: 142.0.7444.265&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Noto Color Emoji&lt;/strong&gt;: Installed via Homebrew&lt;/li&gt;
&lt;li&gt;Also verified working over Remote SSH connections&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Changing the emoji font in VS Code on macOS turned out to be much easier than expected. Simply add &lt;code&gt;Noto Color Emoji&lt;/code&gt; (without quotes) to &lt;code&gt;editor.fontFamily&lt;/code&gt; and &lt;code&gt;terminal.integrated.fontFamily&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Despite widespread claims online that "it's not possible on macOS," by taking advantage of the fact that VS Code is Chromium-based makes it achievable with a simple settings change.&lt;/p&gt;

&lt;p&gt;If you'd like to change the look of your emojis, give it a try!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was originally published in Japanese at &lt;a href="https://archelon-inc.jp/blog/vscode-noto-color-emoji-macos" rel="noopener noreferrer"&gt;archelon-inc.jp&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;References&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fonts.google.com/noto/specimen/Noto+Color+Emoji" rel="noopener noreferrer"&gt;Noto Color Emoji - Google Fonts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://qiita.com/wraith13/items/7d8072ef7a5e57de6f5a" rel="noopener noreferrer"&gt;VS Code でのカラー絵文字表示 - Qiita (Japanese)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zenn.dev/terrierscript/articles/2021-04-18-mac-vscode" rel="noopener noreferrer"&gt;MacのVSCodeで絵文字をちゃんと表示したい - Zenn (Japanese)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vscode</category>
      <category>macos</category>
      <category>font</category>
      <category>emoji</category>
    </item>
    <item>
      <title>CloudFront パスパターンで「ルートだけ」を特定するテクニック</title>
      <dc:creator>upa_rupa</dc:creator>
      <pubDate>Tue, 31 Mar 2026 00:46:29 +0000</pubDate>
      <link>https://dev.to/upa_rupa/cloudfront-pasupatanderutodake-wote-ding-surutekunituku-2pc7</link>
      <guid>https://dev.to/upa_rupa/cloudfront-pasupatanderutodake-wote-ding-surutekunituku-2pc7</guid>
      <description>&lt;h1&gt;
  
  
  CloudFront パスパターンで「ルートだけ」を特定するテクニック
&lt;/h1&gt;

&lt;h2&gt;
  
  
  はじめに
&lt;/h2&gt;

&lt;p&gt;AWS CloudFrontでWebサイトを構築する際、「ルート（&lt;code&gt;/&lt;/code&gt;）だけを特別なオリジンに向けたい、あるいは特別な処理をさせたい」という要件に直面することがあります。&lt;/p&gt;

&lt;p&gt;しかし、CloudFrontのパスパターン設定には「&lt;code&gt;/&lt;/code&gt; のみ」をピンポイントで指定する記法がなく、通常は Default Behavior (&lt;code&gt;*&lt;/code&gt;) を使うしかありません。これではルート以外のアセット（JS/CSS/画像）まで同じ設定に巻き込まれてしまい、キャッシュ戦略やオリジンの分離が難しくなります。&lt;/p&gt;

&lt;p&gt;本記事では、CloudFrontのワイルドカード仕様を逆手に取った、&lt;strong&gt;&lt;code&gt;/?*&lt;/code&gt; パターンを使ってルートを浮かび上がらせるテクニック&lt;/strong&gt;をご紹介します。&lt;/p&gt;

&lt;h2&gt;
  
  
  背景：CloudFront パスパターンの制約
&lt;/h2&gt;

&lt;p&gt;CloudFrontのキャッシュビヘイビアでは、パスパターンを使ってルーティングを制御しますが、以下の制約があります。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;正規表現は使えない&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;使用可能なワイルドカード&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;*&lt;/code&gt;: 0文字以上の任意の文字列にマッチ&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;?&lt;/code&gt;: &lt;strong&gt;正確に1文字&lt;/strong&gt;にマッチ&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;パターンの評価&lt;/strong&gt;: 設定の上（優先度が高い順）から評価され、最初にマッチしたものが適用される&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;この「正規表現が使えない」という制約の中で、「ルート以外すべて」を表現する方法が鍵となります。&lt;/p&gt;

&lt;h2&gt;
  
  
  解決策：&lt;code&gt;/?*&lt;/code&gt; パターンの正体
&lt;/h2&gt;

&lt;p&gt;ここで活用するのが、&lt;code&gt;?&lt;/code&gt; ワイルドカードの「&lt;strong&gt;正確に1文字&lt;/strong&gt;」という仕様です。&lt;/p&gt;

&lt;h3&gt;
  
  
  パターンの意味
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;/?*&lt;/code&gt; というパターンは、以下のように解釈されます。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;/&lt;/code&gt; : スラッシュ（固定）&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;?&lt;/code&gt; : &lt;strong&gt;正確に1文字&lt;/strong&gt;（必須）&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;*&lt;/code&gt; : 0文字以上の任意の文字列&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;つまり、&lt;code&gt;/?*&lt;/code&gt; は &lt;strong&gt;「スラッシュの後に少なくとも1文字以上あるパス」&lt;/strong&gt; にマッチします。&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  💡 ここがポイント
&lt;/h3&gt;

&lt;p&gt;このパターンを優先度「高」で設定することで、&lt;strong&gt;Default Behavior (&lt;code&gt;*&lt;/code&gt;) にはルートリクエスト（&lt;code&gt;/&lt;/code&gt;）だけを落とし込む&lt;/strong&gt;ことが可能になります。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  マッチングの結果
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;パス&lt;/th&gt;
&lt;th&gt;マッチ判定&lt;/th&gt;
&lt;th&gt;理由&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/index.html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ マッチ&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/&lt;/code&gt; + &lt;code&gt;i&lt;/code&gt; + &lt;code&gt;ndex.html&lt;/code&gt;（1文字以上の継続あり）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/main.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ マッチ&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/&lt;/code&gt; + &lt;code&gt;m&lt;/code&gt; + &lt;code&gt;ain.js&lt;/code&gt;（1文字以上の継続あり）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/assets/style.css&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ マッチ&lt;/td&gt;
&lt;td&gt;スラッシュの後に「1文字以上」が存在する&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;/&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ &lt;strong&gt;不適合&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;スラッシュの後に文字がゼロ個のため&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  実用的なユースケース：SSR + 静的アセットの分離
&lt;/h2&gt;

&lt;p&gt;最も汎用的な活用例は、&lt;strong&gt;Astro&lt;/strong&gt; や &lt;strong&gt;Next.js&lt;/strong&gt; などを用いた「SSR（動的生成）と静的配信のハイブリッド構成」です。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/&lt;/code&gt; (ルート)&lt;/strong&gt;: 最新記事のリストやSEO用のメタタグを動的に生成したい（Lambda / App Runner 等の SSR）&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;それ以外の全パス (&lt;code&gt;/assets/*&lt;/code&gt;, &lt;code&gt;/favicon.ico&lt;/code&gt; 等)&lt;/strong&gt;: S3から直接、高速かつ安価に配信したい&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;通常なら拡張子ごとにビヘイビアを定義する必要がありますが、&lt;code&gt;/?*&lt;/code&gt; なら一撃で分離できます。&lt;/p&gt;

&lt;h3&gt;
  
  
  CDKでの実装例
&lt;/h3&gt;

&lt;p&gt;このテクニックを用いると、インフラ構成コード（CDK）も非常にシンプルになります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cloudfront&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-cloudfront&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;origins&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-cloudfront-origins&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;distribution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cloudfront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Distribution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyHybridDist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Default (*) ビヘイビア：優先度が最も低い&lt;/span&gt;
  &lt;span class="c1"&gt;// ここには / だけが落ちてくるため、SSRサーバー（動的オリジン）へ向ける&lt;/span&gt;
  &lt;span class="na"&gt;defaultBehavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;origins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HttpOrigin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ssr-api.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="na"&gt;cachePolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cloudfront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CachePolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CACHING_DISABLED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// SSRは基本キャッシュなし&lt;/span&gt;
    &lt;span class="na"&gt;viewerProtocolPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cloudfront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ViewerProtocolPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIRECT_TO_HTTPS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;additionalBehaviors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 2. ルート以外のすべて (/?*)：優先度を高く設定&lt;/span&gt;
    &lt;span class="c1"&gt;// アセット類をすべて S3 から配信する&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/?*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;origins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;S3BucketOrigin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withOriginAccessIdentity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;assetBucket&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;cachePolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cloudfront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CachePolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CACHING_OPTIMIZED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 静的ファイルは強力にキャッシュ&lt;/span&gt;
      &lt;span class="na"&gt;viewerProtocolPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cloudfront&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ViewerProtocolPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIRECT_TO_HTTPS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  この手法のメリット
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;拡張子の列挙が不要&lt;/strong&gt;: &lt;code&gt;*.js&lt;/code&gt;, &lt;code&gt;*.css&lt;/code&gt;, &lt;code&gt;*.webp&lt;/code&gt; ...と延々と設定を増やす必要がありません。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;責務の分離 (Separation of Concerns)&lt;/strong&gt;: 「動的なトップページ」と「不変の静的アセット」という、性質の異なるリソースをパスのレベルで完全に分離できます。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;キャッシュ戦略の最適化&lt;/strong&gt;: ルートHTMLには &lt;code&gt;no-cache&lt;/code&gt; を、アセット類には長期間のキャッシュを、といった切り分けが一行のパターン定義で完結します。&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;メンテナンス性&lt;/strong&gt;: 新しいファイル形式を追加しても、CloudFrontの設定変更は不要です。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  ⚠️ 注意点：CloudFront Functions との組み合わせ
&lt;/h2&gt;

&lt;p&gt;このテクニックを使用する際、CloudFront Functions でパスの書き換え（Rewriting）を行う場合は注意が必要です。&lt;/p&gt;

&lt;p&gt;CloudFrontの仕様上、&lt;strong&gt;Function内で &lt;code&gt;request.uri&lt;/code&gt; を書き換えても、キャッシュビヘイビアの再評価（再マッチング）は行われません。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;例えば、Default Behavior (&lt;code&gt;*&lt;/code&gt;) に落ちてきたリクエストを Function で &lt;code&gt;/index.html&lt;/code&gt; に書き換えたとしても、そのリクエストが &lt;code&gt;/?*&lt;/code&gt; ビヘイビアに移動することはありません。あくまで「最初にマッチしたビヘイビアの設定」で処理が継続されます。&lt;/p&gt;

&lt;h2&gt;
  
  
  まとめ
&lt;/h2&gt;

&lt;p&gt;CloudFrontの &lt;code&gt;?&lt;/code&gt; ワイルドカードは地味な存在ですが、「正確に1文字」という厳密さを利用することで、標準機能だけでは難しい「パスの否定論理」を組み立てることができます。&lt;/p&gt;

&lt;p&gt;「ルートだけを特別扱いしたい」という要件は、SSR構成、多言語リダイレクト、あるいはメンテナンス表示など、実務で頻繁に発生します。この &lt;code&gt;/?*&lt;/code&gt; テクニックを、シンプルかつ堅牢なインフラ設計の引き出しとして持っておくと非常に便利です。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考資料&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/DownloadDistValuesCacheBehavior.html#DownloadDistValuesCacheBehaviorPathPattern" rel="noopener noreferrer"&gt;AWS CloudFront - キャッシュビヘイビアの設定 (PathPattern 仕様)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://advancedweb.hu/how-cloudfront-routing-works/" rel="noopener noreferrer"&gt;How CloudFront routing works - Advanced Web Machinery&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>cloudfront</category>
      <category>cdk</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Google グループ「投稿者に返信」の落とし穴 ― 送ったメッセージが消える？</title>
      <dc:creator>upa_rupa</dc:creator>
      <pubDate>Tue, 31 Mar 2026 00:43:59 +0000</pubDate>
      <link>https://dev.to/upa_rupa/google-guruputou-gao-zhe-nifan-xin-noluo-tosixue-song-tutametusezigaxiao-eru-nnj</link>
      <guid>https://dev.to/upa_rupa/google-guruputou-gao-zhe-nifan-xin-noluo-tosixue-song-tutametusezigaxiao-eru-nnj</guid>
      <description>&lt;h1&gt;
  
  
  Google グループ「投稿者に返信」の落とし穴 ― 送ったメッセージが消える？
&lt;/h1&gt;

&lt;h2&gt;
  
  
  はじめに
&lt;/h2&gt;

&lt;p&gt;Google グループのウェブ画面には、返信ボタンが3つ並んでいます。&lt;/p&gt;

&lt;p&gt;「全員に返信」「投稿者に返信」「転送」。&lt;/p&gt;

&lt;p&gt;このうち &lt;strong&gt;「投稿者に返信」&lt;/strong&gt; を使ったことはありますか？&lt;/p&gt;

&lt;p&gt;実はこのボタン、送信した内容が &lt;strong&gt;どこにも保存されない&lt;/strong&gt; という、非常に気づきにくい落とし穴があります。&lt;/p&gt;

&lt;h2&gt;
  
  
  何が起きるのか
&lt;/h2&gt;

&lt;p&gt;Google グループのウェブ画面で「投稿者に返信」をクリックすると、投稿者個人宛のメール作成画面が開きます。ここでメッセージを書いて送信すると……&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;グループのアーカイブには残らない&lt;/strong&gt; — 個人宛の私信として扱われるため&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gmail の送信済みフォルダにも残らない&lt;/strong&gt; — Gmail ではなく Google グループのシステムが代行送信しているため&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;つまり、&lt;strong&gt;送信した瞬間に、送信者本人ですら二度と内容を読み返せなくなります。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;それだけではありません。送信済みフォルダにも履歴にも何も残らないということは、&lt;strong&gt;そもそも自分のメッセージが正しく送れたのかどうかすら確認できない&lt;/strong&gt;のです。「相手に届いているだろうか」「内容に間違いはなかっただろうか」――そんな不安を抱えたまま、相手からの反応を待つしかありません。&lt;/p&gt;

&lt;h2&gt;
  
  
  なぜこうなるのか
&lt;/h2&gt;

&lt;p&gt;Google グループは「掲示板（アーカイブ）」として設計されています。しかし実際には、多くの組織がメーリングリストとして利用しています。メールソフトのような感覚で使っているからこそ、「送信済み」に残るはずだという思い込みが生まれ、この落とし穴にハマりやすくなるのです。&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;操作&lt;/th&gt;
&lt;th&gt;処理のされ方&lt;/th&gt;
&lt;th&gt;履歴&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;全員に返信&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;掲示板への書き込み&lt;/td&gt;
&lt;td&gt;グループのアーカイブに残る&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;投稿者に返信&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;掲示板の外での私信&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;どこにも残らない&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;「投稿者に返信」は、掲示板を通さずにシステムが直接メールを飛ばします。Gmail を介していないため、Gmail 側の「送信済み」フォルダにコピーを保存する処理が走らないのです。&lt;/p&gt;

&lt;h2&gt;
  
  
  メール配信設定にも注意
&lt;/h2&gt;

&lt;p&gt;Google グループのメール配信設定も関連する話題です。&lt;/p&gt;

&lt;p&gt;配信設定を「ダイジェスト」にしていると、グループへの投稿がまとめて届くため、個別のメッセージへの返信操作を Google グループのウェブ画面から行いがちです。結果として「投稿者に返信」の罠にハマりやすくなります。&lt;/p&gt;

&lt;h2&gt;
  
  
  世界中のユーザーが困っている
&lt;/h2&gt;

&lt;p&gt;この問題は世界中で知られており、Google の公式ヘルプコミュニティには同様の質問が多数寄せられています。&lt;/p&gt;

&lt;p&gt;英語版のヘルプコミュニティでは、「&lt;a href="https://support.google.com/groups/thread/137962058" rel="noopener noreferrer"&gt;How can I see replies to author (a la Sent Messages)&lt;/a&gt;」というスレッドに 130人以上のユーザーが「同じ質問がある」と回答しています。Gold Product Expert の回答は &lt;strong&gt;「Google グループには送信済みフォルダが存在せず、投稿者への返信を確認する方法はない」&lt;/strong&gt; というもので、現時点で仕様改善には至っていません。&lt;/p&gt;

&lt;p&gt;なお、Google の公式ヘルプ「&lt;a href="https://support.google.com/groups/answer/1046523?hl=ja" rel="noopener noreferrer"&gt;メッセージを作成する、メッセージに返信する&lt;/a&gt;」では「投稿者に返信」の操作方法は説明されていますが、送信内容が保存されないことについては一切触れられていません。&lt;/p&gt;

&lt;h2&gt;
  
  
  対策
&lt;/h2&gt;

&lt;h3&gt;
  
  
  鉄則：Gmail から返信する
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Google グループのウェブ画面にある「投稿者に返信」ボタンは使わない。&lt;/strong&gt; 必ず、自分の Gmail に届いた通知メールに対して、Gmail アプリ・画面から返信してください。&lt;/p&gt;

&lt;p&gt;Gmail から送信すれば、「送信済み」フォルダにしっかり残ります。&lt;/p&gt;

&lt;h3&gt;
  
  
  その他の自衛策
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;返信画面で &lt;strong&gt;自分を BCC に含める&lt;/strong&gt;（チェックボックスがある場合）&lt;/li&gt;
&lt;li&gt;送信前に内容をコピーしておく&lt;/li&gt;
&lt;li&gt;宛先に自分のアドレスを追加する&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  まとめ
&lt;/h2&gt;

&lt;p&gt;Google グループの「投稿者に返信」は、一見便利に見えて実は大きな落とし穴が潜んでいます。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;送信内容はグループにも Gmail にも残らない&lt;/li&gt;
&lt;li&gt;これは Google の仕様であり、現時点で改善の見通しはない&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gmail から返信する&lt;/strong&gt;のが唯一確実な回避策&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;仕事のやり取りで「送った内容が確認できない」のは致命的です。ぜひこの仕様を知っておいて、大切なメッセージが消えてしまう前に対策してください。&lt;/p&gt;

</description>
      <category>googleworkspace</category>
      <category>googlegroups</category>
      <category>email</category>
      <category>tips</category>
    </item>
    <item>
      <title>Teams タブアプリでビルド後に404が出る？HTMLキャッシュが原因かも</title>
      <dc:creator>upa_rupa</dc:creator>
      <pubDate>Mon, 30 Mar 2026 12:15:44 +0000</pubDate>
      <link>https://dev.to/upa_rupa/teams-tabuapuridebirudohou-ni404gachu-ruhtmlkiyatusiyugayuan-yin-kamo-4loh</link>
      <guid>https://dev.to/upa_rupa/teams-tabuapuridebirudohou-ni404gachu-ruhtmlkiyatusiyugayuan-yin-kamo-4loh</guid>
      <description>&lt;h1&gt;
  
  
  Teams タブアプリでビルド後に404が出る？HTMLキャッシュが原因かも
&lt;/h1&gt;

&lt;h2&gt;
  
  
  はじめに
&lt;/h2&gt;

&lt;p&gt;Microsoft Teams タブアプリを開発していると、ローカル開発時に困る問題があります。&lt;/p&gt;

&lt;p&gt;フロントエンドのコードを変更してビルドすると、Vite/Rollup がファイル名にハッシュを付けます（例: &lt;code&gt;TeamsInitializer.bBRVpIft.js&lt;/code&gt;）。しかし、ブラウザ（Teams の WebView）が HTML をキャッシュしていると、古い HTML が古いハッシュ付きファイル名を参照し続けて &lt;strong&gt;404 エラー&lt;/strong&gt; が発生します。&lt;/p&gt;

&lt;p&gt;この記事では、Teams SDK v2 のローカルサーバーの内部構造を調査して見つけた解決方法を紹介します。&lt;/p&gt;

&lt;h2&gt;
  
  
  一般的な解決策とその問題点
&lt;/h2&gt;

&lt;h3&gt;
  
  
  方法1: ファイル名からハッシュを除去する
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// astro.config.mjs または vite.config.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;vite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;rollupOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;entryFileNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assets/[name].js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;chunkFileNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assets/[name].js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;assetFileNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assets/[name].[ext]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;問題点&lt;/strong&gt;: ハッシュを外すと、ブラウザがファイルをキャッシュしてしまい、コード更新が反映されないことがあります。キャッシュバスティングの仕組みを失うことになります。&lt;/p&gt;

&lt;h3&gt;
  
  
  方法2: 開発サーバー（HMR）を使う
&lt;/h3&gt;

&lt;p&gt;Vite や Astro の開発サーバーを使えば HMR（Hot Module Replacement）でキャッシュ問題は起きません。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;問題点&lt;/strong&gt;: Teams アプリは Teams クライアント（WebView）から特定のポートにアクセスする構成のため、開発サーバーの別ポートを直接使うのは難しい場合があります。&lt;/p&gt;

&lt;h2&gt;
  
  
  Teams SDK v2 の内部構造を調査する
&lt;/h2&gt;

&lt;p&gt;Teams SDK v2 の &lt;code&gt;@microsoft/teams.apps&lt;/code&gt; パッケージを調べてみると、&lt;code&gt;HttpPlugin&lt;/code&gt; が内部で &lt;strong&gt;Express&lt;/strong&gt; を使っていることがわかりました。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// node_modules/@microsoft/teams.apps/dist/plugins/http/plugin.js より抜粋&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;__importDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;HttpPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HttpPlugin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;express_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)();&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ← use が公開されている！&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;express_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;重要な発見：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;HttpPlugin&lt;/code&gt; は内部で Express インスタンスを持っている&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;use()&lt;/code&gt; メソッドが公開されており、Express の &lt;code&gt;app.use()&lt;/code&gt; にバインドされている&lt;/li&gt;
&lt;li&gt;つまり、&lt;strong&gt;任意の Express ミドルウェアを追加できる&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  解決策: ミドルウェアでキャッシュ制御ヘッダーを設定
&lt;/h2&gt;

&lt;p&gt;この発見を活かして、ローカル開発時のみ HTML ファイルのキャッシュを無効化するミドルウェアを追加します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/index.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HttpPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@microsoft/teams.apps&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ConsoleLogger&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@microsoft/teams.common/logging&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sslOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SSL_KEY_FILE&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SSL_KEY_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SSL_CRT_FILE&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SSL_CRT_FILE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;httpPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;sslOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cert&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;sslOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sslOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ローカル開発時のみ、HTMLファイルのキャッシュを無効化&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RUNNING_ON_AZURE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;httpPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/tabs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// HTMLファイルとindex.htmlへのリクエストはキャッシュを無効化&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-cache, no-store, must-revalidate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Pragma&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-cache&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Expires&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IPlugin&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;httpPlugin&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tab&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;debug&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;home&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3978&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ポイント解説
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;環境変数で制御&lt;/strong&gt;: &lt;code&gt;RUNNING_ON_AZURE&lt;/code&gt; 環境変数がない場合（ローカル開発時）のみミドルウェアを追加&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;パス判定&lt;/strong&gt;: &lt;code&gt;.html&lt;/code&gt; で終わるパス、ルートパス、拡張子がないパス（SPA のルーティング）をHTML リクエストとみなす&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;キャッシュ無効化ヘッダー&lt;/strong&gt;: &lt;code&gt;Cache-Control&lt;/code&gt;, &lt;code&gt;Pragma&lt;/code&gt;, &lt;code&gt;Expires&lt;/code&gt; の3つを設定して確実にキャッシュを無効化&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  この方法のメリット
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;ハッシュ付きファイル名を維持&lt;/strong&gt;: JS/CSS ファイルはハッシュ付きのままなので、本番環境でのキャッシュ効率は変わらない&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;本番環境に影響なし&lt;/strong&gt;: 環境変数で制御するため、Azure デプロイ時にはミドルウェアが追加されない&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;他のミドルウェアも追加可能&lt;/strong&gt;: 同じ手法でロギング、認証、カスタムヘッダーなども追加できる&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  応用: 他のミドルウェアを追加する
&lt;/h2&gt;

&lt;p&gt;この手法を使えば、様々なミドルウェアを Teams SDK に追加できます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// リクエストログを追加&lt;/span&gt;
&lt;span class="nx"&gt;httpPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// カスタムヘッダーを追加&lt;/span&gt;
&lt;span class="nx"&gt;httpPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-Custom-Header&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// 特定パスに認証を追加&lt;/span&gt;
&lt;span class="nx"&gt;httpPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  注意点
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;この方法は Teams SDK v2 (&lt;code&gt;@microsoft/teams.apps&lt;/code&gt; v2.x) の内部実装に依存しています&lt;/li&gt;
&lt;li&gt;将来のバージョンで API が変わる可能性があります&lt;/li&gt;
&lt;li&gt;公式ドキュメントには記載されていない使い方です&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  まとめ
&lt;/h2&gt;

&lt;p&gt;Teams SDK v2 の &lt;code&gt;HttpPlugin&lt;/code&gt; が内部で Express を使っており、&lt;code&gt;use()&lt;/code&gt; メソッドが公開されていることを発見しました。これを活用することで：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ローカル開発時の HTML キャッシュ問題を解決&lt;/li&gt;
&lt;li&gt;ハッシュ付きファイル名によるキャッシュ無効化の仕組みを維持&lt;/li&gt;
&lt;li&gt;本番環境に影響を与えずに開発体験を改善&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Teams SDK v2 を使っている方で同じ問題に悩んでいる方の参考になれば幸いです。&lt;/p&gt;

</description>
      <category>microsoftteams</category>
      <category>javascript</category>
      <category>express</category>
      <category>caching</category>
    </item>
    <item>
      <title>macOSのVSCodeで絵文字フォントをNoto Color Emojiに変更する方法</title>
      <dc:creator>upa_rupa</dc:creator>
      <pubDate>Mon, 30 Mar 2026 11:04:54 +0000</pubDate>
      <link>https://dev.to/upa_rupa/macosnovscodedehui-wen-zi-huontowonoto-color-emojinibian-geng-surufang-fa-44fc</link>
      <guid>https://dev.to/upa_rupa/macosnovscodedehui-wen-zi-huontowonoto-color-emojinibian-geng-surufang-fa-44fc</guid>
      <description>&lt;h1&gt;
  
  
  macOSのVSCodeで絵文字フォントをNoto Color Emojiに変更する方法
&lt;/h1&gt;

&lt;h2&gt;
  
  
  はじめに
&lt;/h2&gt;

&lt;p&gt;VSCodeで表示される絵文字の見た目を変えたいと思ったことはありませんか？&lt;/p&gt;

&lt;p&gt;macOSでは、絵文字はデフォルトで &lt;strong&gt;Apple Color Emoji&lt;/strong&gt; が使用されます。Appleの絵文字デザインも素敵ですが、Googleの &lt;strong&gt;Noto Color Emoji&lt;/strong&gt; のほうが好みという方もいるでしょう。&lt;/p&gt;

&lt;p&gt;しかし、ネット上の情報では「macOSではNoto Color EmojiはVSCodeで使えない」という記述が多く見られます。&lt;/p&gt;

&lt;p&gt;本記事では、&lt;strong&gt;macOSのVSCodeでNoto Color Emojiを使う方法&lt;/strong&gt;を、実際に動作確認した手順でご紹介します。&lt;/p&gt;

&lt;h2&gt;
  
  
  結論
&lt;/h2&gt;

&lt;p&gt;意外にもシンプルで、以下の2ステップだけです：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Noto Color Emoji フォントをmacOSにインストール&lt;/li&gt;
&lt;li&gt;VSCodeの &lt;code&gt;settings.json&lt;/code&gt; でフォント設定を変更&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Custom CSSの拡張機能やシステムレベルのフォント設定変更は不要です。&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  手順
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Noto Color Emoji のインストール
&lt;/h3&gt;

&lt;p&gt;Homebrewを使ってインストールします：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;font-noto-color-emoji
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. VSCodeのフォント設定を変更
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;settings.json&lt;/code&gt; を開き、以下のように設定します：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;エディタ部分：&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"editor.fontFamily"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"お好みのフォント, monospace, Noto Color Emoji"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ターミナル部分：&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"terminal.integrated.fontFamily"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"お好みのフォント, monospace, Noto Color Emoji"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;設定後、VSCodeを再起動すれば完了です。&lt;/p&gt;

&lt;h3&gt;
  
  
  ポイント：シングルクォートを付けない
&lt;/h3&gt;

&lt;p&gt;通常、スペースを含むフォント名はシングルクォートで囲みます（例：&lt;code&gt;'Noto Color Emoji'&lt;/code&gt;）。しかし、今回の検証では &lt;strong&gt;シングルクォートを外した状態&lt;/strong&gt; で正しく動作しました。&lt;/p&gt;

&lt;p&gt;クォートを付けた場合、macOSのシステムフォールバックにより Apple Color Emoji が優先されてしまう場合があります。クォートなしで指定することで、VSCode（Chromiumベース）のフォント解決が Noto Color Emoji を正しく認識するようです。&lt;/p&gt;

&lt;h2&gt;
  
  
  うまくいかないアプローチ
&lt;/h2&gt;

&lt;p&gt;検証の過程で、以下のアプローチも試しましたが、こちらは不要でした：&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom CSS拡張機能による上書き
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;vscode_custom_css.imports&lt;/code&gt; を使って &lt;code&gt;@font-face&lt;/code&gt; で Apple Color Emoji を Noto Color Emoji に差し替えるアプローチも考えられます：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'Apple Color Emoji'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;'Noto Color Emoji'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;エディタ部分ではこの方法も機能しましたが、ターミナル部分は Canvas/WebGL で描画されるため CSS が効きません。結局、&lt;code&gt;editor.fontFamily&lt;/code&gt; の設定だけで十分でした。&lt;/p&gt;

&lt;h3&gt;
  
  
  macOSのシステムレベルでのフォント変更
&lt;/h3&gt;

&lt;p&gt;CoreTextのフォールバック設定を変更する方法もありますが、macOSアップデートで壊れるリスクがあり、他のアプリにも影響するためおすすめしません。&lt;/p&gt;

&lt;h2&gt;
  
  
  なぜこの方法がネット上で知られていないのか
&lt;/h2&gt;

&lt;p&gt;VSCodeはElectron（Chromium）ベースのアプリケーションです。Chromiumのフォントフォールバック処理は、macOSのネイティブアプリとは異なる挙動をする場合があります。&lt;/p&gt;

&lt;p&gt;多くの記事では「macOSではApple Color Emojiがシステムレベルで優先されるため変更できない」と説明されていますが、これはネイティブアプリでの話です。VSCodeのようなChromiumベースのアプリでは、&lt;code&gt;font-family&lt;/code&gt; の指定がより直接的にフォント選択に反映されるため、この方法が機能すると考えられます。&lt;/p&gt;

&lt;h2&gt;
  
  
  動作確認環境
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS&lt;/strong&gt;: Darwin arm64 25.1.0（Apple Silicon）&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VSCode&lt;/strong&gt;: 1.109.4 (Universal)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Electron&lt;/strong&gt;: 39.3.0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chromium&lt;/strong&gt;: 142.0.7444.265&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Noto Color Emoji&lt;/strong&gt;: Homebrew経由でインストール&lt;/li&gt;
&lt;li&gt;リモートSSH接続環境でも動作確認済み&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  まとめ
&lt;/h2&gt;

&lt;p&gt;macOSのVSCodeで絵文字フォントを変更するのは、思った以上に簡単でした。&lt;code&gt;editor.fontFamily&lt;/code&gt; と &lt;code&gt;terminal.integrated.fontFamily&lt;/code&gt; に &lt;code&gt;Noto Color Emoji&lt;/code&gt;（クォートなし）を追加するだけです。&lt;/p&gt;

&lt;p&gt;ネット上では「macOSでは不可能」という情報が多いですが、VSCodeがChromiumベースであることを活かせば、シンプルな設定変更で実現できます。&lt;/p&gt;

&lt;p&gt;絵文字の雰囲気を変えたい方は、ぜひ試してみてください。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;参考資料&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fonts.google.com/noto/specimen/Noto+Color+Emoji" rel="noopener noreferrer"&gt;Noto Color Emoji - Google Fonts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://qiita.com/wraith13/items/7d8072ef7a5e57de6f5a" rel="noopener noreferrer"&gt;VS Code でのカラー絵文字表示 - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zenn.dev/terrierscript/articles/2021-04-18-mac-vscode" rel="noopener noreferrer"&gt;MacのVSCodeで絵文字をちゃんと表示したい - Zenn&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vscode</category>
      <category>macos</category>
      <category>font</category>
    </item>
  </channel>
</rss>
