Cloudflare Transform Rules で Location レスポンスヘッダのリダイレクト先 URL を http から https に書き換える

# Location レスポンスヘッダ
リダイレクト先の URL が指定されます。

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Location

> Location レスポンスヘッダーはリダイレクト先の URL を示します。 3xx (リダイレクト) または 201 (created) ステータスレスポンスを返すときのみ意味を成します。
>
>リダイレクトの場合、 HTTP メソッドは元のメソッドとリダイレクトの種類によって、 Location で示されたページにアクセスする新しいリクエストを生成するために使用します。
>
>ディレクティブ
>`<url>`
>(リクエスト URL からの) 相対 URL、または絶対 URL。

# Transform Rules なしのリダイレクト通信フロー
例えば、`example.com` ドメインで動くアプリ内で、絶対 URL としてリダイレクト先 `http://redirect.dest.example.com/` を指定している場合、以下のようなフローになります。

- サーバーからの `Location` レスポンスヘッダ `http://...` をクライアントに返す
- Cloudflare に `http://...` 接続すると [Always Use HTTPS](https://developers.cloudflare.com/ssl/edge-certificates/additional-options/always-use-https/) により `https://...` としてリダイレクト
- その後、クライアントブラウザからサーバーに `https://...` としてリクエストを開始

```mermaid:
%%{init:{'themeCSS':' g:nth-of-type(2) rect.actor { fill: orange; }; g:nth-of-type(5) rect.actor { fill: orange; };'}}%%
sequenceDiagram
    Client->>Cloudflare: HTTP Request
    Cloudflare->>Server: HTTP Request
    Server->>Cloudflare: HTTP 302 Response<br>location: http://redirect.dest.example.com/
    Cloudflare->>Client: HTTP 302 Response<br>location: http://redirect.dest.example.com/
    Client->>Cloudflare: HTTP Request (http://...)
    Cloudflare-->Cloudflare: Always Use HTTPS
    Cloudflare->>Client: HTTP 302 Response<br>location: https://redirect.dest.example.com/
    Client->>Cloudflare: HTTP Request (https://...)
    Cloudflare->>Server: HTTP Request
    participant Cloudflare
```

この場合、`http://...` 接続時にノーリファラーとなり、サイトによってはエラーになってしまう懸念があります。

https://mediaexceed.co.jp/marketing/no-referrer/

>ノーリファラーになる原因
⑦https→httpサイトへのアクセス
SSL(暗号化)に対応しているhttpsサイトから対応していないhttpのサイトにアクセスがあった場合、参照元の情報が渡されないため、ノーリファラーとなってしまいます。

https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Referrer-Policy

>ディレクティブ
`strict-origin-when-cross-origin` (既定値)
同一オリジンのリクエストを行う際はオリジン、パス、クエリー文字列を送信します。オリジン間リクエストでは、プロトコルのセキュリティ水準が同じである場合 (HTTPS→HTTPS) にのみオリジンを送信します。**安全性の低下する移動先 (HTTPS→HTTP) には Referer ヘッダーを送信しません。**

# Transform Rules ありのリダイレクト通信フロー
そのため、Transform Rules で `Location` レスポンスヘッダのリダイレクト先 URL を `http` から `https` に書き換えることで、クライアントブラウザでは常に `https` 接続となり、ノーリファラーによるエラーを解消できます。

https://developers.cloudflare.com/rules/transform/response-header-modification/

```mermaid:
%%{init:{'themeCSS':' g:nth-of-type(2) rect.actor { fill: orange; }; g:nth-of-type(5) rect.actor { fill: orange; };'}}%%
sequenceDiagram
    Client->>Cloudflare: HTTP Request
    Cloudflare->>Server: HTTP Request
    Server->>Cloudflare: HTTP 302 Response<br>location: http://redirect.dest.example.com/
    Cloudflare-->Cloudflare: Transform Rules<br>(Modify Response Header)
    Cloudflare->>Client: HTTP 302 Response<br>location: https://redirect.dest.example.com/
    Client->>Cloudflare: HTTP Request (https://...)
    Cloudflare->>Server: HTTP Request
    participant Cloudflare
```

# Transform Rules の設定例
例えば Transform Rules で HTTP Response Header Modification Rule を以下のように設定できます。

https://developers.cloudflare.com/rules/transform/response-header-modification/reference/fields-functions/

- If incoming requests match… 
    - Custom filter expression
        - Response Status Code = `302`
        - Hostname = `302.example.com`
- Then... Modify response header
    - `Set dynamic`
    - Header name = `location`
    - Value = `regex_replace(http.response.headers["location"][0], "http://", "https://")`


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/54fb4936-e8a0-0f42-e077-782dd3ecb3d2.png)

# 動作確認
以下の Workers を準備し、 Routes を `302.example.com` に紐付けて確認します。

https://developers.cloudflare.com/workers/configuration/routing/routes/

```javascript:worker.js
export default {
  async fetch(request, env, ctx) {
    return new Response('Hello World!', {
      status: 302,
      headers: {
        "location": "http://google.co.jp/",
      },
    });
  },
};
```

以下のように Transform Rules で `Location` レスポンスヘッダのリダイレクト先 URL を `http` から `https` に書き換えることができました。

```shell:
# Transform Rules なし
% curl -sIXGET 'https://302.example.com/' | grep -i location
location: http://google.co.jp/
# Transform Rules あり
% curl -sIXGET 'https://302.example.com/' | grep -i location
location: https://google.co.jp/
```

# まとめ

実際には、サイトやアプリのサーバー側で `http://...` の絶対 URL 指定でリダイレクトを返さないように修正できれば、ベストです。

ただし、今回は Cloudflare Transform Rules を使えば、修正にかかるリソースや時間を避けて、通信の挙動を最適化することで、すぐにサイトやアプリを継続して使用可能な状態にできることを確認しました。

# 参考
https://qiita.com/sunao_0039/items/8b8ca79d167fc1bc0363

https://stackoverflow.com/questions/63587556/color-change-of-one-element-in-a-mermaid-sequence-diagram