create-react-appでhttps使いつつexpressにproxyするときにsecure: trueを有効にする方法


前提

  • create-react-app(以下cra)を使ったSPA
  • craのhttpsを有効化している
  • expressを使ったサーバー
  • SPAはexpressでホスティングする
  • 本番環境はheroku想定
    • https終端が前段にいて、httpでexpressサーバーが動く形
  • 開発時はcraのproxy 機能を使ってsame originとして動かすようにしている

どんな問題が起きるのか

craがhttps終端としてのプロキシサーバーになっていて、その後ろにhttpのexpressサーバーがいるという構成になるため、cookieなどの設定は本番と同等で問題なく動く理屈になるはずである

express-sessionの設定は以下のような実装とする

app.use(session({
  store: redisStore,
  name: 'connect.sid',
  secret: 'abkl;aew',
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: true,
    httpOnly: true,
    path: '/',
    sameSite: 'strict',
  },
}))

httpsじゃないとcookieが付かないようにsecure属性を有効にしている

この状態でcraのproxyを挟むと何故かcookieがセットされないという問題が発生する

解決策

craの機能でsetupProxy.jsに http-proxy-middleware を使ってカスタマイズが出来るので、そこで xfwd: true を設定する

setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = app => {
  app.use(
    ['/api'],
    createProxyMiddleware({
      target: 'http://localhost:3100',
      xfwd: true,
      changeOrigin: true,
    }),
  );
};

何が起きていたのか

express-sessionのドキュメントで以下のようなサンプルコードがある

var app = express()
app.set('trust proxy', 1) // trust first proxy
app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true,
  cookie: { secure: true }
}))

ここに記載されている app.set('trust proxy', 1) という設定によって、 X-Forwarded- 系のヘッダを利用してホスト名などを解決するようにexpressの動作が変更される(ref: プロキシーの背後の Express

craのプロキシはデフォルトで X-Forwarded- ヘッダを付与しないため、ホスト名などを使ってsecure判定をしているexpress-sessionがcookieを付与しない、という振る舞いをしていたのであった

app.set('trust proxy', 1) をおまじない的に書いてたのでハマり散らかして大変だった・・・