<?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>VS Code Remote SSH: Automatically Invoke VS Code as Your Editor from External Terminals</title>
      <dc:creator>upa_rupa</dc:creator>
      <pubDate>Tue, 28 Apr 2026 06:53:06 +0000</pubDate>
      <link>https://dev.to/upa_rupa/vs-code-remote-ssh-automatically-invoke-vs-code-as-your-editor-from-external-terminals-2ie8</link>
      <guid>https://dev.to/upa_rupa/vs-code-remote-ssh-automatically-invoke-vs-code-as-your-editor-from-external-terminals-2ie8</guid>
      <description>&lt;h2&gt;
  
  
  The Problem to Solve
&lt;/h2&gt;

&lt;p&gt;VS Code Remote (SSH) is powerful, but there are situations where you want to handle terminal operations in your favorite external terminal like iTerm2 instead of the integrated terminal.&lt;/p&gt;

&lt;p&gt;However, when working from an external terminal, you can't seamlessly use VS Code as your editor out of the box.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Open files with &lt;code&gt;code &amp;lt;filename&amp;gt;&lt;/code&gt; from external terminals like iTerm2, Alacritty, or WezTerm&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use VS Code as the editor for &lt;code&gt;git commit&lt;/code&gt; or &lt;code&gt;crontab -e&lt;/code&gt;, but automatically fall back to another editor when VS Code isn't running&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article introduces a wrapper script that solves both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background: Why &lt;code&gt;code&lt;/code&gt; Doesn't Work from Terminals Outside VS Code
&lt;/h2&gt;

&lt;p&gt;Inside VS Code's integrated terminal, environment variables for communicating with the VS Code server (&lt;code&gt;VSCODE_IPC_HOOK_CLI&lt;/code&gt;) and paths to dedicated executables are set automatically.&lt;/p&gt;

&lt;p&gt;These aren't inherited by external terminals, though. So simply running the &lt;code&gt;code&lt;/code&gt; command won't actually "open the file in a VS Code window."&lt;/p&gt;

&lt;p&gt;This issue is well known, and there are existing articles offering solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.vinnie.work/blog/2024-06-29-controlling-vscode-from-tmux" rel="noopener noreferrer"&gt;Controlling VSCode From Tmux | Vinnie dot Work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoft/vscode-remote-release/issues/3983" rel="noopener noreferrer"&gt;Set up &lt;code&gt;code&lt;/code&gt; command line usage in external terminal | microsoft/vscode-remote-release#3983&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A common approach is to write the socket lookup in &lt;code&gt;.zshrc&lt;/code&gt; or &lt;code&gt;.bashrc&lt;/code&gt; — both references above take this route. The downside is that it's evaluated once at shell startup, so it can't keep up when VS Code is started or restarted afterward. The Vinnie article works around this by wrapping the lookup in a shell function you can re-run manually, but neither reference handles the case where VS Code isn't running at all.&lt;/p&gt;

&lt;p&gt;In this article, we'll put the lookup in a standalone script that re-evaluates on every invocation and gracefully falls back to another editor when VS Code isn't running.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: A Wrapper Script Called &lt;code&gt;editor&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;First, create a directory to hold the script (e.g., &lt;code&gt;~/bin&lt;/code&gt;) and save the following as &lt;code&gt;editor&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the Script
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Find the most recent IPC socket&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;VSCODE_IPC_HOOK_CLI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; /run/user/&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/vscode-ipc-&lt;span class="k"&gt;*&lt;/span&gt;.sock 2&amp;gt;/dev/null | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# Get the path to the VS Code CLI executable&lt;/span&gt;
&lt;span class="nv"&gt;vscode_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-td&lt;/span&gt; ~/.vscode-server/cli/servers/&lt;span class="k"&gt;*&lt;/span&gt;/server/bin/remote-cli/code 2&amp;gt;/dev/null | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# When VS Code is available&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VSCODE_IPC_HOOK_CLI&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$vscode_code&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$# &lt;/span&gt;&lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# Running `code` with no arguments often just exits silently,&lt;/span&gt;
    &lt;span class="c"&gt;# so prompt the user to specify a file when invoked as an editor&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"When using VS Code, please specify a file: editor &amp;lt;filename&amp;gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="k"&gt;fi&lt;/span&gt;
  &lt;span class="c"&gt;# VS Code may be hidden behind other windows or take a moment to surface&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Opening in VS Code..."&lt;/span&gt;
  &lt;span class="c"&gt;# --wait blocks until the file is closed in VS Code&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$vscode_code&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--wait&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
  &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="c"&gt;# If VS Code isn't available, fall back to another editor (nano, vim, etc.)&lt;/span&gt;
  nano &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Granting Execute Permission
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/bin/editor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Shell Configuration
&lt;/h3&gt;

&lt;p&gt;Make the script callable from anywhere and register it as the system's default editor. Add the following to your &lt;code&gt;.zshrc&lt;/code&gt; or &lt;code&gt;.bashrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add ~/bin to PATH (if not already)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Register as the default editor&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EDITOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/bin/editor"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;VISUAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/bin/editor"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Auto-discovering the Latest Socket
&lt;/h4&gt;

&lt;p&gt;You could hardcode the socket path in &lt;code&gt;.zshrc&lt;/code&gt;, but it changes every time VS Code restarts and breaks. This script looks under &lt;code&gt;/run/user/&lt;/code&gt; on every invocation, so it adapts to whatever the current connection state is.&lt;/p&gt;

&lt;h4&gt;
  
  
  Use from External Terminals
&lt;/h4&gt;

&lt;p&gt;With this script in place, you don't have to think about whether you're inside VS Code's integrated terminal. Just type &lt;code&gt;editor&lt;/code&gt; in your favorite external terminal and VS Code opens the file.&lt;/p&gt;

&lt;h4&gt;
  
  
  Handling No-Argument Invocation
&lt;/h4&gt;

&lt;p&gt;Running &lt;code&gt;code&lt;/code&gt; with no arguments simply exits. That's confusing when invoked as an editor — nothing happens — so the script prints a hint instead.&lt;/p&gt;

&lt;p&gt;Programs differ in whether they read &lt;code&gt;EDITOR&lt;/code&gt; or &lt;code&gt;VISUAL&lt;/code&gt;. Many tools, including &lt;code&gt;git&lt;/code&gt; and &lt;code&gt;crontab&lt;/code&gt;, prefer &lt;code&gt;VISUAL&lt;/code&gt;, but some only check &lt;code&gt;EDITOR&lt;/code&gt;. Setting both is the most reliable approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Helps
&lt;/h2&gt;

&lt;p&gt;Once &lt;code&gt;EDITOR&lt;/code&gt;/&lt;code&gt;VISUAL&lt;/code&gt; point at this script, VS Code is automatically used in situations like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;git commit&lt;/code&gt;&lt;/strong&gt; — Editing commit messages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;git rebase -i&lt;/code&gt;&lt;/strong&gt; — Interactive rebase operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;crontab -e&lt;/code&gt;&lt;/strong&gt; — Editing crontabs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Ctrl+x, Ctrl+e&lt;/code&gt;&lt;/strong&gt; — Bash/Zsh command-line editing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code's &lt;code&gt;Ctrl+X, Ctrl+E&lt;/code&gt;&lt;/strong&gt; — Launching an external editor for prompt input&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If VS Code isn't running when you invoke any of these, &lt;code&gt;nano&lt;/code&gt; (or whatever you configured) takes over, so your workflow doesn't stall.&lt;/p&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;&lt;code&gt;editor file.txt&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;editor&lt;/code&gt; (no arguments)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VS Code running&lt;/td&gt;
&lt;td&gt;Opens in VS Code&lt;/td&gt;
&lt;td&gt;Shows a guidance message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VS Code not running&lt;/td&gt;
&lt;td&gt;Opens in nano&lt;/td&gt;
&lt;td&gt;Opens in nano&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;I love VS Code, but iTerm2's tmux integration (&lt;code&gt;tmux -CC&lt;/code&gt;) is so handy that I often skip the integrated terminal entirely. Even then, I still want VS Code for actual file editing, and this wrapper made that workflow much smoother. Getting Copilot completions while editing crontabs is a quietly nice bonus.&lt;/p&gt;

&lt;p&gt;It's a small piece of configuration, but if you're a fellow VS Code user in the same situation, hopefully this is useful.&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-remote-editor-wrapper" rel="noopener noreferrer"&gt;archelon-inc.jp&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>ssh</category>
      <category>linux</category>
    </item>
    <item>
      <title>VS Code Remote SSH：外部ターミナルから VS Code をエディタとしてスマートに呼び出す</title>
      <dc:creator>upa_rupa</dc:creator>
      <pubDate>Tue, 28 Apr 2026 06:51:33 +0000</pubDate>
      <link>https://dev.to/upa_rupa/vs-code-remote-sshwai-bu-taminarukara-vs-code-woedeitatositesumatonihu-bichu-su-3445</link>
      <guid>https://dev.to/upa_rupa/vs-code-remote-sshwai-bu-taminarukara-vs-code-woedeitatositesumatonihu-bichu-su-3445</guid>
      <description>&lt;h1&gt;
  
  
  VS Code Remote SSH：外部ターミナルから VS Code をエディタとしてスマートに呼び出す
&lt;/h1&gt;

&lt;h2&gt;
  
  
  解決したい課題
&lt;/h2&gt;

&lt;p&gt;VS Code Remote（SSH）は強力ですが、ターミナル操作だけは iTerm2 などのお気に入りの外部ターミナルで、行いたい場面があります。&lt;/p&gt;

&lt;p&gt;しかし、外部ターミナルからだと、そのままでは VS Code をシームレスにエディタとして利用できません。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;iTerm2, Alacritty, WezTerm などの外部ターミナルから &lt;code&gt;code ファイル名&lt;/code&gt; でファイルを開きたい&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;git commit&lt;/code&gt; や &lt;code&gt;crontab -e&lt;/code&gt; のエディタとして VS Code を使いたい。ただし、VS Code が未起動なら別のエディタを自動で起動したい&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;これらを解決するラッパースクリプトを紹介します。&lt;/p&gt;

&lt;h2&gt;
  
  
  背景：なぜ VS Code 外のターミナルから &lt;code&gt;code&lt;/code&gt; が動かないのか
&lt;/h2&gt;

&lt;p&gt;VS Code 内の統合ターミナルでは、VS Code サーバーと通信するための環境変数（&lt;code&gt;VSCODE_IPC_HOOK_CLI&lt;/code&gt;）や、専用の実行ファイルへのパスが自動で設定されています。&lt;/p&gt;

&lt;p&gt;しかし、外部ターミナルにはこれらの情報が引き継がれません。そのため、単に code コマンドを実行しても「VS Code のウィンドウでファイルを開く」という動作にならないのです。&lt;/p&gt;

&lt;p&gt;この問題自体はよく知られており、解決策を紹介する記事もあります。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://zenn.dev/shirou/articles/vscode-remote-open-file" rel="noopener noreferrer"&gt;VSCode Remote SSHで別Shellからファイルを開く | Zenn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://qiita.com/sskmy1024y/items/b3bdfa4b7c2c243e69bb" rel="noopener noreferrer"&gt;VSCodeで、ローカルのcodeコマンドと、RemoteSSH時のcodeコマンドを共存させる方法 | Qiita&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;よくある解決策は &lt;code&gt;.zshrc&lt;/code&gt; や &lt;code&gt;.bashrc&lt;/code&gt; にソケット探索の処理を書くことですが、シェル起動時に一度だけ評価されるため、VS Code を後から起動・再起動した場合に追従できません。Zenn の記事のように独立したスクリプトにすれば毎回評価できますが、VS Code が起動していないときのフォールバックは考慮されていません。&lt;/p&gt;

&lt;p&gt;本記事では、実行のたびに VS Code の通信用ソケットを探索し、適切なエディタを選択するスクリプトを作成します。&lt;/p&gt;

&lt;h2&gt;
  
  
  解決策：ラッパースクリプト &lt;code&gt;editor&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;まず、スクリプトを配置するためのディレクトリ（例：&lt;code&gt;~/bin&lt;/code&gt;）を作成し、そこに &lt;code&gt;editor&lt;/code&gt; という名前で以下のスクリプトを保存します。&lt;/p&gt;

&lt;h3&gt;
  
  
  スクリプトの作成
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# 最新の通信用ソケットを探索&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;VSCODE_IPC_HOOK_CLI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; /run/user/&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;/vscode-ipc-&lt;span class="k"&gt;*&lt;/span&gt;.sock 2&amp;gt;/dev/null | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;# VS Code の CLI 実行ファイルのパスを取得&lt;/span&gt;
&lt;span class="nv"&gt;vscode_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-td&lt;/span&gt; ~/.vscode-server/cli/servers/&lt;span class="k"&gt;*&lt;/span&gt;/server/bin/remote-cli/code 2&amp;gt;/dev/null | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# VS Code が利用可能な場合&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VSCODE_IPC_HOOK_CLI&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$vscode_code&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$# &lt;/span&gt;&lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# 引数なしの code コマンドはそのまま終了することが多いため、&lt;/span&gt;
    &lt;span class="c"&gt;# エディタとして呼び出された場合はファイル指定を促すようにします&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"VS Code使用時は、ファイルを指定してください: editor &amp;lt;ファイル名&amp;gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="k"&gt;fi&lt;/span&gt;
  &lt;span class="c"&gt;# VS Code が背後に隠れている場合や、起動のラグを考慮してメッセージを表示&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Opening in VS Code..."&lt;/span&gt;
  &lt;span class="c"&gt;# --wait をつけることで、VS Code 上で閉じるまでコマンドを待機させます&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$vscode_code&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--wait&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
  &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="c"&gt;# VS Code が見つからない場合は、代替エディタ（nano, vim 等）を起動&lt;/span&gt;
  nano &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  実行権限の付与
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/bin/editor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  環境設定
&lt;/h3&gt;

&lt;p&gt;作成したスクリプトをどこからでも呼び出せるようにし、システムの標準エディタとして登録します。&lt;code&gt;.zshrc&lt;/code&gt; や &lt;code&gt;.bashrc&lt;/code&gt; に以下を追記してください。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ~/bin にパスを通す（未設定の場合）&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# 標準エディタとして登録&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EDITOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/bin/editor"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;VISUAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/bin/editor"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h4&gt;
  
  
  最新のソケットを自動探索
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;.zshrc&lt;/code&gt; などに直接ソケットパスを書く方法もありますが、それだと VS Code を再起動した際にパスが変わり、動かなくなることがあります。このスクリプトは実行のたびに &lt;code&gt;/run/user/&lt;/code&gt; 下のソケットを探しに行くため、接続状況の変化に柔軟に対応できます。&lt;/p&gt;

&lt;h4&gt;
  
  
  外部ターミナルからの利用
&lt;/h4&gt;

&lt;p&gt;このスクリプトを通すことで、VS Code の「統合ターミナル」の中であるかどうかを意識せず、使い慣れた外部ターミナルから &lt;code&gt;editor&lt;/code&gt; と打つだけで VS Code を呼び出せるようになります。&lt;/p&gt;

&lt;h4&gt;
  
  
  引数なし実行のハンドリング
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;code&lt;/code&gt; コマンドを引数なしで実行すると、単に終了してしまう挙動になります。&lt;code&gt;editor&lt;/code&gt; スクリプト経由で呼ばれた際に何も起きないと混乱するため、ファイル指定がない場合はメッセージを表示するようにしています。&lt;/p&gt;

&lt;p&gt;環境変数 &lt;code&gt;EDITOR&lt;/code&gt; と &lt;code&gt;VISUAL&lt;/code&gt; のどちらを参照するかはプログラムによって異なります。&lt;code&gt;git&lt;/code&gt; や &lt;code&gt;crontab&lt;/code&gt; をはじめ、多くのツールは &lt;code&gt;VISUAL&lt;/code&gt; を優先しますが、一部のツールや環境によっては &lt;code&gt;EDITOR&lt;/code&gt; のみが参照されることもあります。どちらの変数から呼ばれても同じ挙動になるよう、両方に設定しておくのが最も確実です。&lt;/p&gt;

&lt;h2&gt;
  
  
  活用シーン
&lt;/h2&gt;

&lt;p&gt;このスクリプトを環境変数 &lt;code&gt;EDITOR&lt;/code&gt;/&lt;code&gt;VISUAL&lt;/code&gt; に設定しておくと、以下のような場面で自動的に VS Code が使われます。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;git commit&lt;/code&gt;&lt;/strong&gt; — コミットメッセージの編集&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;git rebase -i&lt;/code&gt;&lt;/strong&gt; — インタラクティブリベースの操作&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;crontab -e&lt;/code&gt;&lt;/strong&gt; — crontab の編集&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Ctrl+x, Ctrl+e&lt;/code&gt;&lt;/strong&gt; — (Bash/Zsh の行編集)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code の &lt;code&gt;Ctrl+X, Ctrl+E&lt;/code&gt;&lt;/strong&gt; — プロンプト入力時の外部エディタ起動&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;もし VS Code を立ち上げていない状態でこれらのコマンドを打っても、自動的に &lt;code&gt;nano&lt;/code&gt;（または設定したエディタ）が立ち上がるため、作業が止まる心配もありません。&lt;/p&gt;

&lt;h2&gt;
  
  
  動作まとめ
&lt;/h2&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;code&gt;editor file.txt&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;editor&lt;/code&gt;（引数なし）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VS Code 起動中&lt;/td&gt;
&lt;td&gt;VS Code で開く&lt;/td&gt;
&lt;td&gt;ガイドメッセージを表示&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VS Code 未起動&lt;/td&gt;
&lt;td&gt;nano で開く&lt;/td&gt;
&lt;td&gt;nano で開く&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  おわりに
&lt;/h2&gt;

&lt;p&gt;VS Code 大好きなのですが、iTerm2のtmux統合(tmux -CC)が便利なので、VS Code内のターミナルを使わないことも多かったのです。そういう時でも、ファイル編集時はVS Codeを使いたかったので、とても便利になりました。&lt;br&gt;
crontab編集時に、copilotが補完してくれるのも地味に便利です。&lt;/p&gt;

&lt;p&gt;ちょっとした設定ですが、同じようにVS Codeを愛用している方の参考になれば嬉しいです。&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>ssh</category>
      <category>linux</category>
    </item>
    <item>
      <title>The Nine-Year Journey to the Orca Emoji (U+1FACD) — How a Single Character Moved the World</title>
      <dc:creator>upa_rupa</dc:creator>
      <pubDate>Thu, 23 Apr 2026 05:18:26 +0000</pubDate>
      <link>https://dev.to/upa_rupa/the-nine-year-journey-to-the-orca-emoji-u1facd-how-a-single-character-moved-the-world-elg</link>
      <guid>https://dev.to/upa_rupa/the-nine-year-journey-to-the-orca-emoji-u1facd-how-a-single-character-moved-the-world-elg</guid>
      <description>&lt;p&gt;I love orcas, so I always felt a bit sad that while we had whale (🐋) and dolphin (🐬) emojis, there was no orca. Then recently, I came across news that the orca emoji would be introduced in iOS 26.4&lt;sup&gt;[1]&lt;/sup&gt;. Since emoji aren't an Apple-proprietary thing — they're defined by an international standard called Unicode — I knew this had to be a bigger story. So I dug in.&lt;/p&gt;

&lt;p&gt;What I found was the story behind the new orca emoji &lt;span&gt;🫍&lt;/span&gt;: a nine-year campaign by people around the world pushing for its adoption into the Unicode standard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic Info
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Code Point&lt;/td&gt;
&lt;td&gt;U+1FACD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;ORCA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block&lt;/td&gt;
&lt;td&gt;Symbols and Pictographs Extended-A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standard Version&lt;/td&gt;
&lt;td&gt;Unicode 17.0 / Emoji 17.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release Date&lt;/td&gt;
&lt;td&gt;September 9, 2025&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This emoji was added as a new standalone code point&lt;sup&gt;[2]&lt;/sup&gt; — not expressed by combining existing emoji, but assigned its own unique code: &lt;code&gt;U+1FACD&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2016–2017: Voices Rise Around the World
&lt;/h2&gt;

&lt;p&gt;The orca emoji story begins well before any official proposal, and in multiple places at once.&lt;/p&gt;

&lt;p&gt;In August 2016, Jökull Ingi Þorvaldsson from Iceland started an online petition on Change.org called "Make a Killer Whale Emoji," asking Apple to add an orca emoji&lt;sup&gt;[4]&lt;/sup&gt;. It gathered 39 signatures.&lt;/p&gt;

&lt;p&gt;The following February (2017), Christoph Päper, a Unicode contributor from Germany, opened an issue titled "Orca emoji" on the GitHub Unicode proposals tracker (Crissov/unicode-proposals)&lt;sup&gt;[3]&lt;/sup&gt;. His point: we have whale (🐋) and dolphin (🐬) emoji — why not orca? The issue included a link to the Change.org petition above.&lt;/p&gt;

&lt;p&gt;The voices were there. But voices alone don't create emoji. A formal proposal had to be submitted to the Unicode Consortium.&lt;/p&gt;

&lt;h2&gt;
  
  
  February 2019: A Third Person Steps Up
&lt;/h2&gt;

&lt;p&gt;In 2019, a Spanish developer named &lt;strong&gt;Marcos Del Sol Vives&lt;/strong&gt; turned these feelings into a formal proposal&lt;sup&gt;[5]&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;On February 20th, Marcos submitted a proposal for the orca emoji to the Unicode Consortium's Emoji Subcommittee (ESC). He had no connection to Jökull in Iceland or Christoph in Germany — he was a third, independent voice.&lt;/p&gt;

&lt;p&gt;Then in September 2020, Lukas Ewert in Germany (also independently) launched another Change.org petition, "Make Orcas an Emoji," collecting 356 signatures&lt;sup&gt;[15]&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;People around the world, strangers to each other, were all asking for the same thing: &lt;strong&gt;an orca emoji&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Marcos's proposal followed the format Unicode requires and made a compelling case&lt;sup&gt;[6]&lt;/sup&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Search Popularity Comparison
&lt;/h3&gt;

&lt;p&gt;Using Bing search trends, Marcos showed that "orca" had roughly the same search popularity as "elephant." Elephants already have an emoji (🐘) — orcas don't. He quantified the gap.&lt;/p&gt;

&lt;h3&gt;
  
  
  No Existing Emoji Covers It
&lt;/h3&gt;

&lt;p&gt;Unicode's proposal review includes an "exclusion factors" section, where proposers must argue why their submission shouldn't be excluded. Marcos addressed this directly&lt;sup&gt;[6]&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;"Can't an existing emoji substitute?" — No. Orcas are commonly called "killer whales" but are scientifically members of the dolphin family, and they look quite different from whales. "Too specialized?" — Pufferfish, crickets, and swans already have emoji, so orca can't be considered too specialized. "Just a passing trend?" — Orcas have existed on Earth for about 11 million years, the proposal notes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sample Images
&lt;/h3&gt;

&lt;p&gt;Following Unicode Consortium requirements, Marcos prepared sample images at 18×18 and 72×72 pixels, in both black-and-white and color.&lt;/p&gt;

&lt;h2&gt;
  
  
  2019–2023: The Gate Is Closed
&lt;/h2&gt;

&lt;p&gt;From here, Marcos's proposal entered a long wait.&lt;/p&gt;

&lt;p&gt;The proposal was sent in 2019. But the formal document number &lt;strong&gt;L2/24-249&lt;/strong&gt; wasn't registered in the Unicode document registry until &lt;strong&gt;2024&lt;/strong&gt;&lt;sup&gt;[6]&lt;/sup&gt;. There is no orca-related document anywhere in the 2019 Document Register&lt;sup&gt;[7]&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;So what happened during those five years? Looking into it, what emerged wasn't simple neglect — it was a deliberate decision on Unicode's part.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Iron Rule: Once Added, Never Removed
&lt;/h3&gt;

&lt;p&gt;First, some background: Unicode has an absolute rule called the &lt;strong&gt;Stability Policy&lt;/strong&gt;&lt;sup&gt;[17]&lt;/sup&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Once a character is encoded, it will not be moved or removed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means that once a code point is assigned, it stays there as long as humanity uses this standard — forever. Mistakes cannot be undone. This is the foundation of Unicode's caution around adding emoji.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Strategic Pause — Shifting to Quality Over Quantity
&lt;/h3&gt;

&lt;p&gt;In 2020, the COVID-19 pandemic pushed back the Unicode 14.0 release by six months&lt;sup&gt;[2]&lt;/sup&gt;. Then in autumn 2022, the UTC (Unicode Technical Committee) announced that Unicode 15.1 would be a limited release.&lt;/p&gt;

&lt;p&gt;ESC chair Jennifer Daniel saw this as an opportunity. Her January 2023 blog post, "Breaking the Cycle"&lt;sup&gt;[18]&lt;/sup&gt;, stated:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;emoji categories are about to hit or have hit a level of saturation.&lt;/p&gt;

&lt;p&gt;the ESC approves fewer and fewer emoji proposals every year.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The ESC used this pause to tackle longstanding issues: unifying skin tone variations, redesigning family emoji, and improving bidirectional text support. They also decided to &lt;strong&gt;temporarily delay the Unicode 17.0 submission window until April 2024&lt;/strong&gt;&lt;sup&gt;[18]&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;This wasn't a shutdown from neglect. It was a &lt;strong&gt;deliberate pause to redefine the emoji addition process itself&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Marcos's Proposal, Waiting at the Gate
&lt;/h3&gt;

&lt;p&gt;There is no record of the orca emoji being rejected. It doesn't appear on Charlotte Buff's list of rejected emoji proposals&lt;sup&gt;[10]&lt;/sup&gt;, nor in the official Unicode non-approval notice archive&lt;sup&gt;[11]&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Marcos's proposal wasn't denied. The gate was simply closed.&lt;/p&gt;

&lt;h2&gt;
  
  
  2024: The Gate Reopens
&lt;/h2&gt;

&lt;p&gt;On April 2, 2024, submissions reopened with new guidelines&lt;sup&gt;[8]&lt;/sup&gt;. ESC chair Jennifer Daniel announced the reopening&lt;sup&gt;[9]&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;When the gate opened, Marcos's proposal — waiting since 2019 — finally received its formal document number, &lt;strong&gt;L2/24-249&lt;/strong&gt;&lt;sup&gt;[6]&lt;/sup&gt;. Later that year in November, the ESC proposed 164 new emoji candidates to the UTC, including the orca&lt;sup&gt;[12]&lt;/sup&gt;. Of those 164: 9 new code points and roughly 155 skin tone variations of existing emoji. One of the new code points — the Apple Core — was ultimately withdrawn, and the remaining 163 were approved as Emoji 17.0.&lt;/p&gt;

&lt;p&gt;No objections were submitted during the Public Review Issue (PRI #515) for Emoji 17.0 candidates&lt;sup&gt;[16]&lt;/sup&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  September 9, 2025: Official Adoption
&lt;/h2&gt;

&lt;p&gt;As part of Unicode 17.0 / Emoji 17.0, the orca emoji was officially approved&lt;sup&gt;[2]&lt;/sup&gt;. About &lt;strong&gt;nine years&lt;/strong&gt; from the first online petition; about &lt;strong&gt;six and a half years&lt;/strong&gt; from Marcos's proposal.&lt;/p&gt;

&lt;p&gt;Marcos himself hasn't spoken publicly much about the details of this journey. His site orca.pet records the fact of the proposal and the fact of adoption — simply&lt;sup&gt;[5]&lt;/sup&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Emoji Approval Process: Open, but Careful
&lt;/h2&gt;

&lt;p&gt;Unicode's emoji approval process is designed to be transparent&lt;sup&gt;[13]&lt;/sup&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Submit a proposal&lt;/strong&gt;: Anyone can submit an emoji proposal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ESC review&lt;/strong&gt;: The Emoji Subcommittee evaluates proposals and decides whether to recommend them to the UTC&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UTC deliberation&lt;/strong&gt;: Discussed by the technical committee; meeting notes are published&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Draft candidate list&lt;/strong&gt;: Before final approval, a candidate list is published as a Public Review Issue (PRI), open for public feedback, which is also published&lt;sup&gt;[16]&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Official release&lt;/strong&gt;: Released as a new version of the Unicode standard&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All proposals are published as PDFs on unicode.org and available for anyone to read&lt;sup&gt;[6]&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Given that an assigned code point can never be removed, this caution has good reason. As the orca's case shows, years between proposal and adoption aren't unusual. But that's not negligence — it's a reflection of the weight of defining a standard that will be used, permanently, across the world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Platform Support Status (as of March 2026)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Google Noto Color Emoji&lt;/td&gt;
&lt;td&gt;Supported (v2.051, released September 12, 2025)&lt;sup&gt;[14]&lt;/sup&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apple (iOS / macOS)&lt;/td&gt;
&lt;td&gt;In beta with iOS 26.4 / macOS 26.4; stable release expected March–April 2026&lt;sup&gt;[1]&lt;/sup&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;X (formerly Twitter)&lt;/td&gt;
&lt;td&gt;Supported (Twemoji v17.0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft (Windows)&lt;/td&gt;
&lt;td&gt;Not yet supported; as of March 2026, just reached Emoji 16.0 support&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Unicode defines the code point and meaning, but the visual design is left to each platform (Apple, Google, Samsung, etc.). That's why the same &lt;code&gt;U+1FACD&lt;/code&gt; looks different on iPhone versus Android.&lt;/p&gt;

&lt;h2&gt;
  
  
  Display Test with Noto Color Emoji
&lt;/h2&gt;

&lt;p&gt;This article loads Google's &lt;strong&gt;Noto Color Emoji&lt;/strong&gt; font to test rendering of the new orca emoji.&lt;/p&gt;

&lt;p&gt;&lt;span&gt;🫍&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Even if your browser or OS doesn't support Unicode 17.0 emoji yet, you should see it rendered via Noto Color Emoji. If you can see a large orca above, it's working.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;The orca emoji (🫍) was born from independent voices in Iceland, Germany, and Spain — and took nine years to make it into the Unicode standard&lt;/li&gt;
&lt;li&gt;Proposer Marcos Del Sol Vives built a case backed by search data and comparative arguments&lt;/li&gt;
&lt;li&gt;The five-year gap wasn't neglect — it was a deliberate pause for Unicode to confront emoji saturation and redefine the addition process&lt;/li&gt;
&lt;li&gt;Unicode's emoji approval process is transparent, but the Stability Policy demands care&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every emoji we casually use has a story behind it. And those stories are often less about triumph than about patience and a series of coincidences stacking up over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li id="ref-1"&gt;iPhone Mania, "iOS26.4で利用可能になる新絵文字のデザインが明らかに！ (Japanese)" March 10, 2026. &lt;a href="https://iphone-mania.jp/ios-600850/" rel="noopener noreferrer"&gt;https://iphone-mania.jp/ios-600850/&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-2"&gt;The Unicode Blog, "Unicode 17.0 Release Announcement," September 9, 2025. &lt;a href="http://blog.unicode.org/2025/09/unicode-170-release-announcement.html" rel="noopener noreferrer"&gt;http://blog.unicode.org/2025/09/unicode-170-release-announcement.html&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-3"&gt;Crissov/unicode-proposals, "Issue #103: Orca emoji," GitHub, February 5, 2017. &lt;a href="https://github.com/Crissov/unicode-proposals/issues/103" rel="noopener noreferrer"&gt;https://github.com/Crissov/unicode-proposals/issues/103&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-4"&gt;Jökull Ingi Þorvaldsson, "Make a Killer Whale Emoji," Change.org, August 2016. &lt;a href="https://www.change.org/p/apple-make-a-killer-whale-emoji-in-apple-s-emoji-board" rel="noopener noreferrer"&gt;https://www.change.org/p/apple-make-a-killer-whale-emoji-in-apple-s-emoji-board&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-5"&gt;Marcos Del Sol Vives, "Orca emoji," orca.pet. &lt;a href="https://orca.pet/emoji/" rel="noopener noreferrer"&gt;https://orca.pet/emoji/&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-6"&gt;Marcos Del Sol Vives, "Proposal for Orca Emoji," Unicode Document L2/24-249, 2024. &lt;a href="https://www.unicode.org/L2/L2024/24249-orca-emoji.pdf" rel="noopener noreferrer"&gt;https://www.unicode.org/L2/L2024/24249-orca-emoji.pdf&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-7"&gt;The Unicode Consortium, "UTC Document Register — 2019." &lt;a href="https://www.unicode.org/L2/L2019/Register-2019.html" rel="noopener noreferrer"&gt;https://www.unicode.org/L2/L2019/Register-2019.html&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-8"&gt;The Unicode Blog, "Emoji Submissions Intake Process Re-opening," March 2024. &lt;a href="http://blog.unicode.org/2024/03/emoji-submissions-intake-process-re.html" rel="noopener noreferrer"&gt;http://blog.unicode.org/2024/03/emoji-submissions-intake-process-re.html&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-9"&gt;Jennifer Daniel, "Emoji submissions re-opening," Substack. &lt;a href="https://jenniferdaniel.substack.com/p/emoji-submissions-re-opening-april" rel="noopener noreferrer"&gt;https://jenniferdaniel.substack.com/p/emoji-submissions-re-opening-april&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-10"&gt;Charlotte Buff, "Rejected Emoji Proposals." &lt;a href="https://charlottebuff.com/unicode/misc/rejected-emoji-proposals/" rel="noopener noreferrer"&gt;https://charlottebuff.com/unicode/misc/rejected-emoji-proposals/&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-11"&gt;The Unicode Consortium, "Archive of Notices of Non-Approval." &lt;a href="https://www.unicode.org/alloc/nonapprovals.html" rel="noopener noreferrer"&gt;https://www.unicode.org/alloc/nonapprovals.html&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-12"&gt;Emojipedia Blog, "What's New In Unicode 17.0." &lt;a href="https://blog.emojipedia.org/whats-new-in-unicode-17-0/" rel="noopener noreferrer"&gt;https://blog.emojipedia.org/whats-new-in-unicode-17-0/&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-13"&gt;The Unicode Consortium, "Submitting Emoji Proposals." &lt;a href="https://unicode.org/emoji/proposals.html" rel="noopener noreferrer"&gt;https://unicode.org/emoji/proposals.html&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-14"&gt;Emojipedia Blog, "Google Debuts Emoji 17.0 Support." &lt;a href="https://blog.emojipedia.org/google-debuts-emoji-17-0-support/" rel="noopener noreferrer"&gt;https://blog.emojipedia.org/google-debuts-emoji-17-0-support/&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-15"&gt;Lukas Ewert, "Make Orcas an Emoji," Change.org, September 2020. &lt;a href="https://www.change.org/p/apple-make-orcas-an-emoji" rel="noopener noreferrer"&gt;https://www.change.org/p/apple-make-orcas-an-emoji&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-16"&gt;The Unicode Consortium, "PRI #515: Unicode Emoji 17.0 Alpha Repertoire." &lt;a href="https://www.unicode.org/review/pri515/" rel="noopener noreferrer"&gt;https://www.unicode.org/review/pri515/&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-17"&gt;The Unicode Consortium, "Unicode Character Encoding Stability Policies." &lt;a href="https://www.unicode.org/policies/stability_policy.html" rel="noopener noreferrer"&gt;https://www.unicode.org/policies/stability_policy.html&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-18"&gt;Jennifer Daniel, "Breaking the Cycle," The Unicode Blog, March 8, 2024 (originally published January 17, 2023). &lt;a href="http://blog.unicode.org/2024/03/breaking-cycle.html" rel="noopener noreferrer"&gt;http://blog.unicode.org/2024/03/breaking-cycle.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;




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

</description>
      <category>unicode</category>
      <category>emoji</category>
      <category>macos</category>
      <category>orca</category>
    </item>
    <item>
      <title>シャチ絵文字（U+1FACD）がUnicodeに採用されるまで — 1文字のために世界が動いた9年越しの軌跡</title>
      <dc:creator>upa_rupa</dc:creator>
      <pubDate>Thu, 23 Apr 2026 05:18:22 +0000</pubDate>
      <link>https://dev.to/upa_rupa/siyatihui-wen-zi-u1facdgaunicodenicai-yong-sarerumade-1wen-zi-notamenishi-jie-gadong-ita9nian-yue-sinogui-ji-1dbn</link>
      <guid>https://dev.to/upa_rupa/siyatihui-wen-zi-u1facdgaunicodenicai-yong-sarerumade-1wen-zi-notamenishi-jie-gadong-ita9nian-yue-sinogui-ji-1dbn</guid>
      <description>&lt;h1&gt;
  
  
  シャチ絵文字（U+1FACD）がUnicodeに採用されるまで — 1文字のために世界が動いた9年越しの軌跡
&lt;/h1&gt;

&lt;p&gt;私はシャチが好きである。なので、クジラ（&lt;span&gt;🐋&lt;/span&gt;）やイルカ（&lt;span&gt;🐬&lt;/span&gt;）の絵文字はあるのにシャチがないことを残念に思っていた。&lt;br&gt;&lt;br&gt;
しかし最近、iOS 26.4からシャチの絵文字が追加されるというニュースを目にした&lt;sup&gt;[1]&lt;/sup&gt;。絵文字はApple独自のものではなくUnicodeという国際標準で定められているはずだから、これはもっと大きな話のはずだ — そう思って調べてみた。&lt;/p&gt;

&lt;p&gt;すると見えてきたのは、新しいシャチの絵文字 &lt;span&gt;🫍&lt;/span&gt; の裏にある、9年にわたって世界各地の人々がUnicode標準への採用を求め続けた物語だった。&lt;/p&gt;

&lt;h2&gt;
  
  
  シャチ絵文字の基本情報
&lt;/h2&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;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;コードポイント&lt;/td&gt;
&lt;td&gt;U+1FACD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;名称&lt;/td&gt;
&lt;td&gt;ORCA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;収録ブロック&lt;/td&gt;
&lt;td&gt;Symbols and Pictographs Extended-A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;標準バージョン&lt;/td&gt;
&lt;td&gt;Unicode 17.0 / Emoji 17.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;リリース日&lt;/td&gt;
&lt;td&gt;2025年9月9日&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;この絵文字は単独の新規コードポイントとして追加された&lt;sup&gt;[2]&lt;/sup&gt;。既存の絵文字を組み合わせて表現するのではなく、&lt;code&gt;U+1FACD&lt;/code&gt; という固有のコードが割り当てられている。&lt;/p&gt;

&lt;h2&gt;
  
  
  2016年〜2017年 — 世界各地から湧き上がった声
&lt;/h2&gt;

&lt;p&gt;シャチ絵文字の物語は、正式な提案よりもずっと前に、しかも別々の場所で始まっている。&lt;/p&gt;

&lt;p&gt;2016年8月、アイスランドの Jökull Ingi Þorvaldsson がChange.orgで「Make a Killer Whale Emoji」というオンライン署名活動を開始した&lt;sup&gt;[4]&lt;/sup&gt;。Appleに対してシャチの絵文字を作るよう求めるこの署名には39名が賛同した。&lt;/p&gt;

&lt;p&gt;翌2017年2月、ドイツのUnicodeコントリビューターである Christoph Päper が、GitHubのUnicode提案トラッカー（Crissov/unicode-proposals）に「Orca emoji」のissueを立てた&lt;sup&gt;[3]&lt;/sup&gt;。クジラ（&lt;span&gt;🐋&lt;/span&gt;）やイルカ（&lt;span&gt;🐬&lt;/span&gt;）の絵文字は存在するのに、シャチがないのはおかしい — そんな問題提起だった。このissueには前述のChange.org署名へのリンクも添えられていた。&lt;/p&gt;

&lt;p&gt;声は上がった。しかし声だけでは絵文字は生まれない。Unicode Consortiumに正式な提案書を提出する必要がある。&lt;/p&gt;

&lt;h2&gt;
  
  
  2019年2月 — 3人目が動く
&lt;/h2&gt;

&lt;p&gt;2019年、この想いを1本の提案書に変えた人物がいる。スペインの開発者、&lt;strong&gt;Marcos Del Sol Vives&lt;/strong&gt; だ&lt;sup&gt;[5]&lt;/sup&gt;。&lt;/p&gt;

&lt;p&gt;2月20日、MarcosはUnicode Consortiumの絵文字小委員会（Emoji Subcommittee、以下ESC）にシャチ絵文字の提案を送った。アイスランドのJökull、ドイツのChristophとは面識のない、独立した3人目の人物だった。&lt;/p&gt;

&lt;p&gt;さらに2020年9月、ドイツの Lukas Ewert が、これらの動きとは別にChange.org署名「Make Orcas an Emoji」を立ち上げ、356名の賛同を集めている&lt;sup&gt;[15]&lt;/sup&gt;。&lt;/p&gt;

&lt;p&gt;世界各地の、互いに知らない人々が独立して同じことを求めていた。&lt;strong&gt;シャチの絵文字がほしい&lt;/strong&gt;、と。&lt;/p&gt;

&lt;p&gt;Marcosの提案書には、Unicode Consortiumが求めるフォーマットに従った説得力のある内容が含まれていた&lt;sup&gt;[6]&lt;/sup&gt;。&lt;/p&gt;

&lt;h3&gt;
  
  
  検索人気度の比較
&lt;/h3&gt;

&lt;p&gt;Bingでの検索トレンドを使い、「orca」の検索人気度が「elephant（象）」とほぼ同等であることを示した。象にはすでに絵文字（&lt;span&gt;🐘&lt;/span&gt;）があるのに、シャチにはない — この不均衡を数字で可視化した。&lt;/p&gt;

&lt;h3&gt;
  
  
  既存の絵文字では代替できない
&lt;/h3&gt;

&lt;p&gt;Unicode の提案審査には「除外要因」という項目があり、提案者はなぜ除外すべきでないかを自ら論証する必要がある。Marcosの提案書はこれに対して明快だった&lt;sup&gt;[6]&lt;/sup&gt;。&lt;/p&gt;

&lt;p&gt;「既存の絵文字で代用できるのでは？」— できない。シャチは「キラーホエール」と呼ばれるが科学的にはイルカの仲間であり、クジラとは外見がかなり異なる。「特殊すぎるのでは？」— フグ、コオロギ、白鳥といった動物がすでに絵文字になっている以上、シャチが特殊すぎるとは言えない。「一時的な流行では？」— シャチは約1,100万年前から地球にいる、と提案書は答えている。&lt;/p&gt;

&lt;h3&gt;
  
  
  サンプル画像の添付
&lt;/h3&gt;

&lt;p&gt;Unicode Consortiumの規定に従い、18x18ピクセルと72x72ピクセルのサンプル画像を白黒・カラーの両方で用意した。&lt;/p&gt;

&lt;h2&gt;
  
  
  2019年〜2023年 — 閉ざされた門
&lt;/h2&gt;

&lt;p&gt;しかし、ここからMarcosの提案は長い待ち時間に入る。&lt;/p&gt;

&lt;p&gt;提案は2019年に送られた。しかし、正式な提案書番号 &lt;strong&gt;L2/24-249&lt;/strong&gt; がUnicode文書レジストリに登録されたのは &lt;strong&gt;2024年&lt;/strong&gt; のことだった&lt;sup&gt;[6]&lt;/sup&gt;。2019年のDocument Registerにシャチ関連の文書は一件も存在しない&lt;sup&gt;[7]&lt;/sup&gt;。&lt;/p&gt;

&lt;p&gt;この5年間、何が起きていたのか。調べていくと、単なる「放置」ではない、Unicode側の意図的な判断が見えてきた。&lt;/p&gt;

&lt;h3&gt;
  
  
  一度付けたら消せない — Unicodeの鉄の掟
&lt;/h3&gt;

&lt;p&gt;まず前提として、Unicodeには &lt;strong&gt;不変性（Stability Policy）&lt;/strong&gt; という絶対的なルールがある&lt;sup&gt;[17]&lt;/sup&gt;。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Once a character is encoded, it will not be moved or removed.&lt;br&gt;
（一度エンコードされた文字は、移動も削除もされない）&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;つまり、一度コードポイントを割り当てたら、それは人類がこの標準を使い続ける限り永久に残る。間違いがあっても取り消せない。この鉄の掟が、絵文字の追加に対する慎重さの根本にある。&lt;/p&gt;

&lt;h3&gt;
  
  
  戦略的休止 — 「量より質」への転換
&lt;/h3&gt;

&lt;p&gt;2020年、COVID-19の影響でUnicode 14.0のリリースが6ヶ月延期された&lt;sup&gt;[2]&lt;/sup&gt;。そして2022年秋、UTC（Unicode Technical Committee）はUnicode 15.1を限定的なリリースにすると発表した。&lt;/p&gt;

&lt;p&gt;ESCの議長 Jennifer Daniel は、この状況を「機会」と捉えた。彼女が2023年1月に発表したブログ記事「Breaking the Cycle（循環を断ち切る）」&lt;sup&gt;[18]&lt;/sup&gt;には、こう書かれている。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;emoji categories are about to hit or have hit a level of saturation.&lt;br&gt;
（絵文字のカテゴリは飽和に達しつつある、あるいは既に達している）&lt;/p&gt;

&lt;p&gt;the ESC approves fewer and fewer emoji proposals every year.&lt;br&gt;
（ESCは年々、承認する絵文字提案を減らしている）&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ESCはこの休止期間を使い、スキントーン（肌の色）のバリエーション統一、家族絵文字の再設計、書字方向の対応といった積年の課題に取り組んだ。そして &lt;strong&gt;Unicode 17.0の提案受付を2024年4月まで一時的に遅らせる&lt;/strong&gt; と決めた&lt;sup&gt;[18]&lt;/sup&gt;。&lt;/p&gt;

&lt;p&gt;これは怠慢による停止ではなく、絵文字の追加プロセスそのものを再定義するための &lt;strong&gt;意図的な休止&lt;/strong&gt; だった。&lt;/p&gt;

&lt;h3&gt;
  
  
  門の前で待つMarcosの提案
&lt;/h3&gt;

&lt;p&gt;シャチ絵文字が「却下」された記録は見つからない。Charlotte Buffが管理する却下済み絵文字提案リストにも掲載されていない&lt;sup&gt;[10]&lt;/sup&gt;。Unicode公式の非承認通知アーカイブにも見当たらない&lt;sup&gt;[11]&lt;/sup&gt;。&lt;/p&gt;

&lt;p&gt;Marcosの提案は否定されたのではない。門が閉まっていたのだ。&lt;/p&gt;

&lt;h2&gt;
  
  
  2024年 — 門が開く
&lt;/h2&gt;

&lt;p&gt;2024年4月2日、新しいガイドラインとともに提案受付が再開された&lt;sup&gt;[8]&lt;/sup&gt;。ESCの議長Jennifer Danielもこの再開を告知している&lt;sup&gt;[9]&lt;/sup&gt;。&lt;/p&gt;

&lt;p&gt;門が開いたとき、2019年からずっと待っていたMarcosの提案はようやく正式な文書番号 &lt;strong&gt;L2/24-249&lt;/strong&gt; を得た&lt;sup&gt;[6]&lt;/sup&gt;。同年11月、ESCが164個の新絵文字候補をUTC に提案し、シャチもその中に含まれていた&lt;sup&gt;[12]&lt;/sup&gt;。164個の内訳は、新規コードポイント9個と既存絵文字のスキントーンバリエーション約155個。最終的に新規コードポイントのうち「りんごの芯（Apple Core）」1個が取り下げられ、残りの163個がEmoji 17.0として承認された。&lt;/p&gt;

&lt;p&gt;なお、Emoji 17.0の候補に対するPublic Review Issue（PRI #515）では、反対意見は寄せられなかった&lt;sup&gt;[16]&lt;/sup&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  2025年9月9日 — 正式採用
&lt;/h2&gt;

&lt;p&gt;Unicode 17.0 / Emoji 17.0の一部として、シャチ絵文字は正式に承認された&lt;sup&gt;[2]&lt;/sup&gt;。最初のオンライン署名から&lt;strong&gt;約9年&lt;/strong&gt;、Marcosの提案からでも&lt;strong&gt;約6年半&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;提案者のMarcos自身は、この経緯の詳細を公には多く語っていない。自身のサイト orca.pet には、提案の事実と採用の事実が簡潔に記録されているのみだ&lt;sup&gt;[5]&lt;/sup&gt;。&lt;/p&gt;

&lt;h2&gt;
  
  
  Unicodeの承認プロセス — オープン、しかし慎重
&lt;/h2&gt;

&lt;p&gt;Unicodeの絵文字承認プロセスは、透明性が高い設計になっている&lt;sup&gt;[13]&lt;/sup&gt;。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;提案の提出&lt;/strong&gt;: 誰でも絵文字の提案書を提出できる&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ESCでの審査&lt;/strong&gt;: 絵文字小委員会が提案を評価し、UTCに推薦するかを判断する&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UTCでの審議&lt;/strong&gt;: 技術委員会で議論される。議事録も公開されている&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ドラフト候補リストの公開&lt;/strong&gt;: 正式承認前に候補リストが公開され、Public Review Issue（PRI）として一般からのフィードバックを受け付ける。寄せられたフィードバックは公開される&lt;sup&gt;[16]&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;正式リリース&lt;/strong&gt;: Unicode標準の新バージョンとしてリリースされる&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;提案書はすべてunicode.orgでPDFとして公開されており、誰でも読むことができる&lt;sup&gt;[6]&lt;/sup&gt;。&lt;/p&gt;

&lt;p&gt;一度割り当てたコードポイントは永久に消せないという不変性の掟がある以上、この慎重さには理由がある。シャチの事例が示すように、提案から採用まで数年かかることは珍しくない。しかしそれは、怠慢ではなく、世界中で永久に使われる標準を定めることの重さの表れでもある。&lt;/p&gt;

&lt;h2&gt;
  
  
  各プラットフォームの対応状況（2026年3月時点）
&lt;/h2&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;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Google Noto Color Emoji&lt;/td&gt;
&lt;td&gt;対応済み（v2.051、2025年9月12日リリース）&lt;sup&gt;[14]&lt;/sup&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apple（iOS / macOS）&lt;/td&gt;
&lt;td&gt;iOS 26.4 / macOS 26.4 ベータで対応中。正式版は2026年3〜4月予定&lt;sup&gt;[1]&lt;/sup&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;X（旧Twitter）&lt;/td&gt;
&lt;td&gt;対応済み（Twemoji v17.0）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft（Windows）&lt;/td&gt;
&lt;td&gt;未対応。2026年3月時点でEmoji 16.0に対応したばかり&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Unicodeがコードポイントと意味を定めるが、実際の見た目は各プラットフォーム（Apple、Google、Samsung等）がそれぞれ独自にデザインする。同じ &lt;code&gt;U+1FACD&lt;/code&gt; でも、iPhoneとAndroidでは見た目が異なるのはそのためだ。&lt;/p&gt;

&lt;h2&gt;
  
  
  Noto Color Emoji で表示テスト
&lt;/h2&gt;

&lt;p&gt;この記事では、Google の &lt;strong&gt;Noto Color Emoji&lt;/strong&gt; フォントを読み込んで、新しいシャチ絵文字の表示をテストしている。&lt;/p&gt;

&lt;p&gt;&lt;span&gt;🫍&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;お使いのブラウザやOSがUnicode 17.0の絵文字に対応していない場合でも、Noto Color Emojiフォントを通じて表示されるはずだ。上に大きなシャチが見えていれば成功である。&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;シャチ絵文字（&lt;span&gt;🫍&lt;/span&gt;）は、アイスランド、ドイツ、スペインと世界各地の独立した声から始まり、9年かけてUnicode標準に採用された&lt;/li&gt;
&lt;li&gt;提案者の Marcos Del Sol Vives さんは、検索データや比較論証を駆使した提案書を作成した&lt;/li&gt;
&lt;li&gt;5年間の空白は「放置」ではなく、Unicodeが絵文字の飽和に向き合い、追加プロセスを再定義するための戦略的休止だった&lt;/li&gt;
&lt;li&gt;Unicodeの絵文字承認プロセスは透明性が高いが、不変性という鉄の掟ゆえに慎重さが求められる&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;普段何気なく使っている絵文字の一つひとつに、こうした物語がある。そしてその物語は、華々しい成功譚ではなく、忍耐と偶然の積み重ねであることが多いのかもしれない。&lt;/p&gt;

&lt;h2&gt;
  
  
  参考文献
&lt;/h2&gt;

&lt;ol&gt;
&lt;li id="ref-1"&gt;iPhone Mania, "iOS26.4で利用可能になる新絵文字のデザインが明らかに！" 2026年3月10日. &lt;a href="https://iphone-mania.jp/ios-600850/" rel="noopener noreferrer"&gt;https://iphone-mania.jp/ios-600850/&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-2"&gt;The Unicode Blog, "Unicode 17.0 Release Announcement," 2025年9月9日. &lt;a href="http://blog.unicode.org/2025/09/unicode-170-release-announcement.html" rel="noopener noreferrer"&gt;http://blog.unicode.org/2025/09/unicode-170-release-announcement.html&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-3"&gt;Crissov/unicode-proposals, "Issue #103: Orca emoji," GitHub, 2017年2月5日. &lt;a href="https://github.com/Crissov/unicode-proposals/issues/103" rel="noopener noreferrer"&gt;https://github.com/Crissov/unicode-proposals/issues/103&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-4"&gt;Jökull Ingi Þorvaldsson, "Make a Killer Whale Emoji," Change.org, 2016年8月. &lt;a href="https://www.change.org/p/apple-make-a-killer-whale-emoji-in-apple-s-emoji-board" rel="noopener noreferrer"&gt;https://www.change.org/p/apple-make-a-killer-whale-emoji-in-apple-s-emoji-board&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-5"&gt;Marcos Del Sol Vives, "Orca emoji," orca.pet. &lt;a href="https://orca.pet/emoji/" rel="noopener noreferrer"&gt;https://orca.pet/emoji/&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-6"&gt;Marcos Del Sol Vives, "Proposal for Orca Emoji," Unicode Document L2/24-249, 2024年. &lt;a href="https://www.unicode.org/L2/L2024/24249-orca-emoji.pdf" rel="noopener noreferrer"&gt;https://www.unicode.org/L2/L2024/24249-orca-emoji.pdf&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-7"&gt;The Unicode Consortium, "UTC Document Register — 2019." &lt;a href="https://www.unicode.org/L2/L2019/Register-2019.html" rel="noopener noreferrer"&gt;https://www.unicode.org/L2/L2019/Register-2019.html&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-8"&gt;The Unicode Blog, "Emoji Submissions Intake Process Re-opening," 2024年3月. &lt;a href="http://blog.unicode.org/2024/03/emoji-submissions-intake-process-re.html" rel="noopener noreferrer"&gt;http://blog.unicode.org/2024/03/emoji-submissions-intake-process-re.html&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-9"&gt;Jennifer Daniel, "Emoji submissions re-opening," Substack. &lt;a href="https://jenniferdaniel.substack.com/p/emoji-submissions-re-opening-april" rel="noopener noreferrer"&gt;https://jenniferdaniel.substack.com/p/emoji-submissions-re-opening-april&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-10"&gt;Charlotte Buff, "Rejected Emoji Proposals." &lt;a href="https://charlottebuff.com/unicode/misc/rejected-emoji-proposals/" rel="noopener noreferrer"&gt;https://charlottebuff.com/unicode/misc/rejected-emoji-proposals/&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-11"&gt;The Unicode Consortium, "Archive of Notices of Non-Approval." &lt;a href="https://www.unicode.org/alloc/nonapprovals.html" rel="noopener noreferrer"&gt;https://www.unicode.org/alloc/nonapprovals.html&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-12"&gt;Emojipedia Blog, "What's New In Unicode 17.0." &lt;a href="https://blog.emojipedia.org/whats-new-in-unicode-17-0/" rel="noopener noreferrer"&gt;https://blog.emojipedia.org/whats-new-in-unicode-17-0/&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-13"&gt;The Unicode Consortium, "Submitting Emoji Proposals." &lt;a href="https://unicode.org/emoji/proposals.html" rel="noopener noreferrer"&gt;https://unicode.org/emoji/proposals.html&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-14"&gt;Emojipedia Blog, "Google Debuts Emoji 17.0 Support." &lt;a href="https://blog.emojipedia.org/google-debuts-emoji-17-0-support/" rel="noopener noreferrer"&gt;https://blog.emojipedia.org/google-debuts-emoji-17-0-support/&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-15"&gt;Lukas Ewert, "Make Orcas an Emoji," Change.org, 2020年9月. &lt;a href="https://www.change.org/p/apple-make-orcas-an-emoji" rel="noopener noreferrer"&gt;https://www.change.org/p/apple-make-orcas-an-emoji&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-16"&gt;The Unicode Consortium, "PRI #515: Unicode Emoji 17.0 Alpha Repertoire." &lt;a href="https://www.unicode.org/review/pri515/" rel="noopener noreferrer"&gt;https://www.unicode.org/review/pri515/&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-17"&gt;The Unicode Consortium, "Unicode Character Encoding Stability Policies." &lt;a href="https://www.unicode.org/policies/stability_policy.html" rel="noopener noreferrer"&gt;https://www.unicode.org/policies/stability_policy.html&lt;/a&gt;
&lt;/li&gt;
&lt;li id="ref-18"&gt;Jennifer Daniel, "Breaking the Cycle," The Unicode Blog, 2024年3月8日（初出: 2023年1月17日）. &lt;a href="http://blog.unicode.org/2024/03/breaking-cycle.html" rel="noopener noreferrer"&gt;http://blog.unicode.org/2024/03/breaking-cycle.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>unicode</category>
      <category>emoji</category>
      <category>macos</category>
      <category>orca</category>
    </item>
    <item>
      <title>Auto-Configure Git Hooks for Your Entire Team with Just npm install</title>
      <dc:creator>upa_rupa</dc:creator>
      <pubDate>Wed, 22 Apr 2026 08:58:49 +0000</pubDate>
      <link>https://dev.to/upa_rupa/auto-configure-git-hooks-for-your-entire-team-with-just-npm-install-5dpi</link>
      <guid>https://dev.to/upa_rupa/auto-configure-git-hooks-for-your-entire-team-with-just-npm-install-5dpi</guid>
      <description>&lt;h2&gt;
  
  
  No more "Git hooks weren't working on my machine"
&lt;/h2&gt;

&lt;p&gt;In team development, running tests locally before pushing is crucial — it reduces CI wait times and helps you move faster.&lt;/p&gt;

&lt;p&gt;The problem? The &lt;code&gt;.git/hooks/&lt;/code&gt; directory is not tracked by Git. Every developer has to manually set up hooks after cloning a repo, which leads to familiar issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Forgotten setup:&lt;/strong&gt; New team members or people rebuilding their environment skip the manual step&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stale hooks:&lt;/strong&gt; When hook scripts are updated, getting everyone to re-apply the changes manually is a losing battle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No enforcement:&lt;/strong&gt; Hook setup is left to individual discretion, making it hard to guarantee consistent quality across the team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even with CI in place, slow feedback loops hurt if local checks keep getting skipped.&lt;/p&gt;

&lt;p&gt;The fix: &lt;strong&gt;embed Git hook setup into &lt;code&gt;npm install&lt;/code&gt;&lt;/strong&gt; — an operation everyone already runs — so the entire team stays in sync without thinking about it.&lt;/p&gt;

&lt;p&gt;And we'll do it &lt;strong&gt;without any extra libraries like Husky&lt;/strong&gt;, using only Git's built-in features and npm scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Git hooks and GitHub Actions: Better Together
&lt;/h2&gt;

&lt;p&gt;Running tests locally doesn't replace CI — they serve different purposes and work well together.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;GitHub Actions&lt;/th&gt;
&lt;th&gt;Local Git hooks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;When it runs&lt;/td&gt;
&lt;td&gt;After push&lt;/td&gt;
&lt;td&gt;Before push&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feedback speed&lt;/td&gt;
&lt;td&gt;Must wait for CI to spin up&lt;/td&gt;
&lt;td&gt;Instant, right on your machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment consistency&lt;/td&gt;
&lt;td&gt;Unified, reproducible environment&lt;/td&gt;
&lt;td&gt;Depends on local setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enforcement&lt;/td&gt;
&lt;td&gt;Strong (cannot be bypassed)&lt;/td&gt;
&lt;td&gt;Medium (&lt;code&gt;--no-verify&lt;/code&gt; can skip it)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource cost&lt;/td&gt;
&lt;td&gt;Consumes CI minutes&lt;/td&gt;
&lt;td&gt;Only your local machine&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Fast local feedback plus reliable CI checks — together they cover both speed and consistency.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup: core.hooksPath + npm prepare
&lt;/h2&gt;

&lt;p&gt;Just three steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create a hooks directory in your project
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .githooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Write your hook scripts
&lt;/h3&gt;

&lt;p&gt;Example: a &lt;code&gt;pre-push&lt;/code&gt; hook that runs tests before every push&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .githooks/pre-push&lt;/span&gt;
&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🧪 Running tests before push..."&lt;/span&gt;

&lt;span class="c"&gt;# Run project tests&lt;/span&gt;
npm &lt;span class="nb"&gt;test

&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Tests failed. Push aborted."&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ All tests passed. Proceeding with push."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make it executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x .githooks/pre-push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Add a prepare script to package.json
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git config core.hooksPath .githooks"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Works
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How &lt;code&gt;prepare&lt;/code&gt; behaves:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs automatically after &lt;code&gt;npm install&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Also runs after &lt;code&gt;npm ci&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;npm install&lt;/code&gt; right after cloning is second nature — making this a reliable trigger&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What &lt;code&gt;core.hooksPath&lt;/code&gt; does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Points Git to any directory instead of the default &lt;code&gt;.git/hooks/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;By pointing to &lt;code&gt;.githooks/&lt;/code&gt; in the project root, &lt;strong&gt;the hook scripts themselves become Git-tracked&lt;/strong&gt; — updates to scripts propagate to everyone via the repository&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Combined: clone the repo, run &lt;code&gt;npm install&lt;/code&gt;, and Git hooks are active. No extra steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simpler Onboarding for New Team Members
&lt;/h2&gt;

&lt;p&gt;The old way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone the repository&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;"Oh, the README says to set up Git hooks..."&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manually run &lt;code&gt;git config core.hooksPath .githooks&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;(Half the team forgets)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With this setup:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone the repository&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm install&lt;/code&gt; ← &lt;strong&gt;done&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Onboarding instructions become: "just run npm install."&lt;/p&gt;

&lt;h2&gt;
  
  
  Monorepo Support
&lt;/h2&gt;

&lt;p&gt;If you have multiple sub-projects under a single root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project-root/
├── .githooks/
│   └── pre-push
├── frontend/
│   └── package.json
├── backend/
│   └── package.json
└── package.json  ← configure here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure it in the root &lt;code&gt;package.json&lt;/code&gt; and it works as long as you run npm install at the root.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real-World pre-push Hook
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🧪 Running pre-push checks..."&lt;/span&gt;

&lt;span class="c"&gt;# Frontend tests&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"frontend"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"📦 Testing frontend..."&lt;/span&gt;
  &lt;span class="nb"&gt;cd &lt;/span&gt;frontend &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;test
  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Frontend tests failed."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Backend tests&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"backend"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"📦 Testing backend..."&lt;/span&gt;
  &lt;span class="nb"&gt;cd &lt;/span&gt;backend &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;test
  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Backend tests failed."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ All checks passed!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ol&gt;
&lt;li&gt;Don't rely on CI alone — run tests locally for fast feedback&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;.githooks/&lt;/code&gt; directory and place your hook scripts there&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;"prepare": "git config core.hooksPath .githooks"&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Everyone on the team gets Git hooks automatically on &lt;code&gt;npm install&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pushing without running tests becomes the exception, not the norm — and "I forgot to set up hooks" stops happening.&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/npm-install-git-hooks" rel="noopener noreferrer"&gt;archelon-inc.jp&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>npm</category>
      <category>devops</category>
    </item>
    <item>
      <title>npm install だけで Git Hooks を全員に自動設定する方法</title>
      <dc:creator>upa_rupa</dc:creator>
      <pubDate>Wed, 22 Apr 2026 08:58:06 +0000</pubDate>
      <link>https://dev.to/upa_rupa/npm-install-dakede-git-hooks-woquan-yuan-nizi-dong-she-ding-surufang-fa-52m8</link>
      <guid>https://dev.to/upa_rupa/npm-install-dakede-git-hooks-woquan-yuan-nizi-dong-she-ding-surufang-fa-52m8</guid>
      <description>&lt;h1&gt;
  
  
  npm install だけで Git Hooks を全員に自動設定する方法
&lt;/h1&gt;

&lt;h2&gt;
  
  
  「自分だけ Git Hooks が動いていなかった」をゼロにする
&lt;/h2&gt;

&lt;p&gt;チーム開発において、「コードを push する前にローカルでテストを回す」という運用は、CI の待ち時間を減らし、開発サイクルを高速化するために非常に重要です。&lt;/p&gt;

&lt;p&gt;しかし、Git Hooks が格納される &lt;code&gt;.git/hooks/&lt;/code&gt; ディレクトリは Git の管理対象に含まれません。そのため、プロジェクトをクローンした後に各自が手動で設定を再現する必要があり、それが以下のような問題を引き起こします。&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;/li&gt;
&lt;li&gt;
&lt;strong&gt;強制力の欠如：&lt;/strong&gt; 設定の有無が各自の裁量に委ねられ、プロジェクト全体でテスト品質を担保できない&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;せっかく CI を導入していても、ローカルでのチェックが漏れてしまっては、フィードバックの速度が落ちてしまいます。&lt;/p&gt;

&lt;p&gt;そこで、&lt;strong&gt;「npm install という誰もが必ず行う操作」に Git Hooks のセットアップを組み込む&lt;/strong&gt;ことで、意識せずとも全員の環境を同じ状態に保つ――そんな「仕組みによる解決」を目指します。&lt;/p&gt;

&lt;p&gt;具体的には、&lt;strong&gt;Husky などの追加ライブラリを使わず、Git の標準機能と npm scripts だけで完結させる方法&lt;/strong&gt;を解説します。&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Actions とローカルGit Hooks の共存
&lt;/h2&gt;

&lt;p&gt;ローカルでテストを実行したからといって、CIでのテストが不要になるわけではありません。それぞれ役割が異なるため、&lt;strong&gt;併用する&lt;/strong&gt;のがおすすめです。&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;GitHub Actions&lt;/th&gt;
&lt;th&gt;ローカルGit Hooks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;実行タイミング&lt;/td&gt;
&lt;td&gt;push後&lt;/td&gt;
&lt;td&gt;push前&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;フィードバック速度&lt;/td&gt;
&lt;td&gt;CIの起動・実行を待つ必要がある&lt;/td&gt;
&lt;td&gt;手元で即座に結果が分かる&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&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;/td&gt;
&lt;td&gt;高い（回避不可）&lt;/td&gt;
&lt;td&gt;中程度（&lt;code&gt;--no-verify&lt;/code&gt;で回避可能）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;リソースコスト&lt;/td&gt;
&lt;td&gt;CI実行時間を消費&lt;/td&gt;
&lt;td&gt;個人のPCリソースのみ&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ローカルで素早くフィードバックを得つつ、CIで環境差異のない確実なチェックを行う。この組み合わせが開発効率と品質の両面で効果的です。&lt;/p&gt;

&lt;h2&gt;
  
  
  方法：core.hooksPath + npm prepare の組み合わせ
&lt;/h2&gt;

&lt;p&gt;以下の3ステップで実現できます。&lt;/p&gt;

&lt;h3&gt;
  
  
  1. プロジェクトにhooksディレクトリを作成
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .githooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. フックスクリプトを作成
&lt;/h3&gt;

&lt;p&gt;例：push前にテストを実行する &lt;code&gt;pre-push&lt;/code&gt; フック&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .githooks/pre-push&lt;/span&gt;
&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🧪 Running tests before push..."&lt;/span&gt;

&lt;span class="c"&gt;# プロジェクトのテストを実行&lt;/span&gt;
npm &lt;span class="nb"&gt;test

&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Tests failed. Push aborted."&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ All tests passed. Proceeding with push."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;作成後、実行権限を付与します：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x .githooks/pre-push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. package.json に prepare スクリプトを追加
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git config core.hooksPath .githooks"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;これだけです！&lt;/p&gt;

&lt;h2&gt;
  
  
  なぜこれで自動化できるのか
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;prepare&lt;/code&gt; スクリプトの特徴：&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm install&lt;/code&gt; の後に自動実行される&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm ci&lt;/code&gt; でも実行される&lt;/li&gt;
&lt;li&gt;Git cloneした後、最初に &lt;code&gt;npm install&lt;/code&gt; を実行するのは自然な流れ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;core.hooksPath&lt;/code&gt; の特徴：&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.git/hooks/&lt;/code&gt; の代わりに任意のディレクトリをhooks置き場に指定できる&lt;/li&gt;
&lt;li&gt;プロジェクトルートの &lt;code&gt;.githooks/&lt;/code&gt; を指定すれば、&lt;strong&gt;フックのロジック自体を Git 管理下に置ける&lt;/strong&gt;ため、スクリプトの更新もリポジトリ経由で全員に同期されます。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;この2つを組み合わせることで、&lt;strong&gt;リポジトリをcloneして &lt;code&gt;npm install&lt;/code&gt; するだけで、特別な操作なしにGit Hooksが有効になります。&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  新メンバーのオンボーディングが簡単に
&lt;/h2&gt;

&lt;p&gt;従来の方法では：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;リポジトリをclone&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;「あ、READMEにGit Hooksの設定手順が書いてあるな...」&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;手動で &lt;code&gt;git config core.hooksPath .githooks&lt;/code&gt; を実行&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;（忘れる人続出）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;この方法なら：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;リポジトリをclone&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm install&lt;/code&gt; ← &lt;strong&gt;これだけで完了！&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;新メンバーへの説明も「npm installしてね」の一言で済みます。&lt;/p&gt;

&lt;h2&gt;
  
  
  複数プロジェクト（モノレポ）での対応
&lt;/h2&gt;

&lt;p&gt;ルートに複数のサブプロジェクトがある場合：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project-root/
├── .githooks/
│   └── pre-push
├── frontend/
│   └── package.json
├── backend/
│   └── package.json
└── package.json  ← ルートに用意
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ルートの &lt;code&gt;package.json&lt;/code&gt; で設定すれば、どのサブプロジェクトから &lt;code&gt;npm install&lt;/code&gt; しても機能します。&lt;/p&gt;

&lt;h2&gt;
  
  
  実際のpre-pushフック例
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🧪 Running pre-push checks..."&lt;/span&gt;

&lt;span class="c"&gt;# フロントエンドのテスト&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"frontend"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"📦 Testing frontend..."&lt;/span&gt;
  &lt;span class="nb"&gt;cd &lt;/span&gt;frontend &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;test
  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Frontend tests failed."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# バックエンドのテスト&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"backend"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"📦 Testing backend..."&lt;/span&gt;
  &lt;span class="nb"&gt;cd &lt;/span&gt;backend &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;test
  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ Backend tests failed."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ All checks passed!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ol&gt;
&lt;li&gt;GitHub Actionsだけに頼らず、ローカルでも素早くテストを回す&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.githooks/&lt;/code&gt; ディレクトリを作成してフックスクリプトを配置&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;package.json&lt;/code&gt; の &lt;code&gt;prepare&lt;/code&gt; で &lt;code&gt;git config core.hooksPath .githooks&lt;/code&gt; を設定&lt;/li&gt;
&lt;li&gt;これでチーム全員が &lt;code&gt;npm install&lt;/code&gt; するだけで自動的にGit Hooksが有効に！&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;push前に手元でテストを実行する習慣が、自然とチーム全体に広がります。&lt;/strong&gt; 設定忘れによる「テストを通さずにpushしてしまった」という事故も防げますし、CIの待ち時間に悩まされることも減るでしょう。&lt;/p&gt;

</description>
      <category>git</category>
      <category>npm</category>
      <category>devops</category>
    </item>
    <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;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;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;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;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;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;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>
  </channel>
</rss>
