できない.dev

Express で「Cannot GET /パス」と表示されルートにアクセスできない

「Cannot GET /パス」は、メソッドとパスに一致するルートが無いときに Express が返す 404。
POST のルートをブラウザで開いた、Router のマウントパスの二重指定、express.static の相対パスのずれ、SPA のパスの直接表示が主な原因だ。

公開:

要約

ブラウザに次の 1 行だけが表示されるのは、Express がすべてのミドルウェアとルートを順に試し、どれも応答しなかったときに返す既定の 404 だ。

Cannot GET /about

公式 FAQ(新しいタブで開く) のとおり、Express では 404 はエラーとして扱われないので、エラー処理のミドルウェアには届かない。
表示の GET はメソッド、その後ろは Express が受け取ったパスなので、まずこの 2 つを登録したルートと突き合わせる。

よくある原因

  1. メソッドが違う: app.post('/users', ...) だけを定義し、ブラウザで /users を開くと GET で届くので Cannot GET /users になる。
  2. マウントパスが二重になっている: app.use('/api', router) でマウントしたルーターの中で router.get('/api/items', ...) と書くと、実際の URL は /api/api/items になり、/api/items は 404 になる。
    ルーターのパスは、マウントパスを取り除いた残りと照合される。
  3. 静的ファイルのディレクトリがずれている: 公式ガイド(新しいタブで開く)にあるとおり、express.static('public') の相対パスはサーバーのファイルの場所ではなく、node を起動したディレクトリを基準に解決される。
    プロジェクトのルートで node src/server.js と起動すると、src/public ではなく ./public を探す。
    ファイルが無いと express.static は次の処理へ渡すので、最後に Cannot GET が返る。
  4. SPA のパスを直接開いている: React Router や Vue Router のパスはブラウザの中で切り替わるだけで、サーバー側にルートは無い。
    トップから遷移すると表示できるのに、/about をリロードすると Cannot GET /about になる。

解決策

1. メソッドとパスを確かめる

curl -i -X POST http://localhost:3000/users

POST のルートは、curl や fetch() で POST を送って確かめる。

2. ルーターのパスはマウントパスを除いて書く

const router = express.Router();
router.get("/items", (req, res) => res.json([])); // /api/items で応答する
 
app.use("/api", router);

3. 静的ファイルは絶対パスで指定する

const path = require("path");
 
app.use(express.static(path.join(__dirname, "public")));

__dirname はそのファイルがあるディレクトリなので、起動した場所に左右されない。
ES Modules で __dirname が使えない場合は Node.js の ESM で __dirname が使えない場合の対処を参照。

4. SPA はフォールバックで index.html を返す

const path = require("path");
const dist = path.join(__dirname, "dist");
 
apiRouter.use((req, res) => res.status(404).json({ error: "not found" })); // API の 404
app.use("/api", apiRouter); // API を先に登録する
app.use(express.static(dist));
app.get("/{*splat}", (req, res) => {
  res.sendFile(path.join(dist, "index.html"));
});

Express 5 ではパスに名前の無い * を書くと起動時に例外になるので、トップの / も含めてすべてに一致させるには /{*splat} と書く(Express 4 では *)。
フォールバックは存在しない /api/ のパスにも index.html を返すので、API のルーターの末尾には 404 を返すミドルウェアを置いておく。

この記事は役立ちましたか?