Table of Contents#
- Understanding the Error
- Common Causes in React/Redux + Node.js/Koa + Passport.js Stacks
- Step-by-Step Fixes
- Verification
- Conclusion
- References
Understanding the Error#
What’s a MIME Type?#
MIME (Multipurpose Internet Mail Extensions) types tell browsers how to interpret files. For example:
text/htmlfor HTML files,application/javascriptfor JavaScript,text/cssfor CSS.
Browsers enforce strict MIME type checking for security: if a script tag references a file with a MIME type like text/html, the browser refuses to execute it (since HTML isn’t executable code).
Why Does This Happen in Your Stack?#
Your React/Redux app (a Single-Page Application, SPA) relies on client-side routing (e.g., React Router). When the browser requests a JavaScript bundle (e.g., main.js), it expects a application/javascript MIME type. But if your Koa backend or Passport.js authentication redirects the request (e.g., to a login page) or serves an HTML error page (e.g., 404), the response MIME type becomes text/html—triggering the error.
Common Causes in the Stack#
Let’s break down the most likely culprits in your specific tech stack:
1. Koa Backend Routing Conflicts#
Koa may misroute requests for static assets (JS/CSS) to HTML pages. For example:
- If you haven’t configured a catch-all route for SPA client-side routing, Koa might return a 404 HTML page instead of your React bundle.
- API routes defined after static file middleware could override asset requests.
2. Misconfigured Webpack Dev Server#
Webpack Dev Server (WDS) serves bundled assets in development. Issues here include:
- Incorrect
publicPathorcontentBasesettings, causing WDS to serve assets from the wrong path. - Missing
historyApiFallback(critical for SPAs), leading to 404 errors on client-side routes.
3. Passport.js Authentication Redirects#
Passport.js redirects unauthenticated users to a login page (e.g., /login), which returns HTML. If:
- Your auth middleware is applied to static asset routes (e.g.,
/static/js/main.js), - An unauthenticated request for
main.jsgets redirected to/login(HTML),
the browser receives HTML instead of JavaScript—hence the MIME type error.
4. CORS or MIME Type Header Issues#
- CORS misconfiguration can block browsers from reading MIME type headers.
- Middleware (e.g., custom error handlers) might override MIME type headers for static assets.
Step-by-Step Fixes#
Let’s resolve each cause with actionable fixes.
Fix 1: Correct Koa Backend Routing for SPAs#
Koa must prioritize API routes, serve static assets, and then fall back to index.html for client-side routes. Here’s how:
Step 1. Serve Static Files with koa-static#
First, use koa-static to serve your React build assets (JS, CSS, images). Install it:
npm install koa-static --save Then, configure Koa to serve static files before defining routes:
// server/src/app.js
const Koa = require('koa');
const static = require('koa-static');
const path = require('path');
const app = new Koa();
// Serve static assets (React build files)
const staticPath = path.join(__dirname, '../../client/build'); // Path to your React build folder
app.use(static(staticPath));
// Define API routes FIRST (before catch-all)
app.use(require('./routes/api')); // Your API routes (e.g., /api/users)
// Catch-all route: Serve index.html for client-side SPA routes
app.use(async (ctx) => {
// Only handle GET requests for SPA routes
if (ctx.method === 'GET' && !ctx.path.startsWith('/api')) {
ctx.type = 'text/html';
ctx.body = fs.readFileSync(path.join(staticPath, 'index.html'), 'utf8');
}
}); Key Notes:#
- Order Matters: API routes (
/api/*) must be defined before the catch-all route. Otherwise, Koa will serveindex.htmlfor API requests! - Use
fs.readFileSyncto readindex.html(ensurefsis required:const fs = require('fs')).
Fix 2: Configure Webpack Dev Server Properly#
In development, Webpack Dev Server (WDS) bundles and serves assets. Fix WDS with these settings:
Step 1. Update webpack.config.js#
Ensure your Webpack config includes:
publicPath: '/': Serves assets from the root path.historyApiFallback: true: Redirects 404s toindex.html(critical for SPAs).proxy: Forwards API requests to your Koa backend (avoids CORS issues).
Example webpack.config.js:
// client/webpack.config.js
module.exports = {
// ... other config (entry, output, loaders)
devServer: {
port: 3000, // Frontend dev port
hot: true, // Enable HMR
open: true,
historyApiFallback: true, // Fix 404s on client-side routes
publicPath: '/', // Serve assets from root
proxy: {
// Proxy API requests to Koa backend (e.g., /api/* → http://localhost:4000/api/*)
'/api': 'http://localhost:4000',
// Proxy auth routes (e.g., /login) if needed
'/login': 'http://localhost:4000'
}
}
}; Step 2. Verify package.json Script#
Ensure your start script uses WDS:
// client/package.json
"scripts": {
"start": "webpack serve --config webpack.config.js"
} Fix 3: Adjust Passport.js Authentication Flow#
Prevent Passport from redirecting static asset requests. Apply auth middleware only to API routes.
Step 1. Scope Auth Middleware to API Routes#
Modify your Koa app to apply Passport auth middleware only to /api/* routes:
// server/src/app.js
const passport = require('koa-passport');
const authMiddleware = require('./middleware/auth'); // Your Passport auth middleware
// Apply auth ONLY to API routes
app.use(async (ctx, next) => {
if (ctx.path.startsWith('/api')) {
await authMiddleware(ctx, next); // e.g., passport.authenticate('jwt')
} else {
await next(); // Skip auth for static assets/login
}
}); Step 2. Ensure Login/Static Routes Are Unprotected#
Your login page (/login) and static assets (/static/*) should be accessible without authentication. Confirm:
koa-staticserves/static/js/main.jsunprotected.- The
/loginroute returns HTML (no auth required).
Fix 4: Verify MIME Types and CORS Headers#
Ensure Koa serves assets with correct MIME types and CORS headers.
Step 1. Validate MIME Types with koa-static#
koa-static automatically sets MIME types for common files (e.g., .js → application/javascript). To confirm, check the Content-Type header in your browser’s DevTools (Network tab) for main.js:

Step 2. Fix CORS with koa-cors#
If your frontend (port 3000) and backend (port 4000) run on different ports, enable CORS:
Install koa-cors:
npm install koa-cors --save Configure CORS in Koa:
// server/src/app.js
const cors = require('koa-cors');
app.use(cors({
origin: 'http://localhost:3000', // Allow frontend origin
allowHeaders: ['Content-Type', 'Authorization']
})); Verification#
Test if the error is fixed:
- Clear Browser Cache: Old cached assets can cause false positives.
- Restart Servers: Stop and restart both Koa backend and Webpack Dev Server.
- Check DevTools Network Tab:
- Request
http://localhost:3000/static/js/main.js(or your bundle path). - Ensure the
Statusis 200 OK. - Check
Content-Typeheader: Should beapplication/javascript. - Verify the response is JavaScript, not HTML.
- Request
Conclusion#
The "Refused to execute script due to MIME type" error is almost always a routing or configuration issue. By:
- Prioritizing API routes and using a catch-all for SPAs in Koa,
- Configuring Webpack Dev Server with
historyApiFallbackandproxy, - Scoping Passport auth to API routes,
- Ensuring proper CORS and MIME type headers,
you’ll resolve the error and keep your React/Redux app running smoothly.