From 25e4035436d06362a96caca990d9ff3c2a5208bd Mon Sep 17 00:00:00 2001 From: jason Date: Mon, 9 Mar 2026 19:10:48 -0500 Subject: [PATCH] fix: Prevent /static and /uploads paths from falling through to React catch-all --- server/index.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/server/index.js b/server/index.js index e327ad3..bba3f59 100644 --- a/server/index.js +++ b/server/index.js @@ -36,10 +36,15 @@ app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); -// Static file serving +// Static asset routes — registered BEFORE React catch-all so they are +// resolved directly and never fall through to index.html app.use('/uploads', express.static(UPLOAD_PATH)); app.use('/static', express.static(STATIC_PATH)); +// Explicit 404 for missing asset files so the catch-all never intercepts them +app.use('/uploads', (req, res) => res.status(404).json({ error: 'Upload not found' })); +app.use('/static', (req, res) => res.status(404).json({ error: 'Static asset not found' })); + // API Routes app.use('/api/dogs', require('./routes/dogs')); app.use('/api/litters', require('./routes/litters')); @@ -53,11 +58,15 @@ app.get('/api/health', (req, res) => { }); // Serve React frontend in production +// The catch-all is intentionally placed AFTER all asset/API routes above. +// express.static(clientBuildPath) handles real build assets (JS/CSS chunks). +// The scoped '*' only fires for HTML5 client-side routes (e.g. /dogs, /litters). if (process.env.NODE_ENV === 'production') { const clientBuildPath = path.join(__dirname, '../client/dist'); app.use(express.static(clientBuildPath)); - - app.get('*', (req, res) => { + + // Only send index.html for non-asset, non-api paths + app.get(/^(?!\/(api|static|uploads)\/).*$/, (req, res) => { res.sendFile(path.join(clientBuildPath, 'index.html')); }); } -- 2.49.1