You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LitElement应用部署Nginx后加载失败,MIME类型错误求助

问题

将LitElement静态网页应用部署至Nginx服务器后,在Google Chrome中打开时页面持续加载但无内容,浏览器控制台出现两个错误:

  1. Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec.
  2. workflow/:1 ServiceWorker registration failed: DOMException: Failed to register a ServiceWorker for scope ('https://frontend.net/') with script ('https://frontend.net/sw.js'): The script has an unsupported MIME type ('text/html').

该应用在本地通过Node.js的serve工具运行完全正常,相关配置文件如下:

nginx.conf

worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /tmp/nginx.pid;

events {
    worker_connections  1024;
}

http {
    proxy_temp_path /tmp/proxy_temp;
    client_body_temp_path /tmp/client_temp;
    fastcgi_temp_path /tmp/fastcgi_temp;
    uwsgi_temp_path /tmp/uwsgi_temp;
    scgi_temp_path /tmp/scgi_temp;

    include       /etc/nginx/mime.types;
    types {
      application/javascript js mjs;
    }
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       8080;
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        server_name  localhost;
        server_tokens off;

        location / {
            try_files $uri $uri/ /index.html;
        }

        error_page   404 405 406 500 502 503 504  /error_frontend.html;
        location = /error_aria.html {
            root   /usr/share/nginx/html;
        }
    }
}

rollup.config.js

import nodeResolve from '@rollup/plugin-node-resolve';
import babel from '@rollup/plugin-babel';
import html from '@web/rollup-plugin-html';
import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets';
import esbuild from 'rollup-plugin-esbuild';
import { generateSW } from 'rollup-plugin-workbox';
import path from 'path';

export default {
  input:  path.join(__dirname, './index.html'),
  output: {
    entryFileNames: '[hash].js',
    chunkFileNames: '[hash].js',
    assetFileNames: '[hash][extname]',
    format: 'es',
    dir: 'dist',
  },
  preserveEntrySignatures: false,

  plugins: [
    /** Enable using HTML as rollup entrypoint */
    html({
      minify: true,
      injectServiceWorker: true,
      serviceWorkerPath: 'dist/sw.js',
    }),
    /** Resolve bare module imports */
    nodeResolve(),
    /** Minify JS, compile JS to a lower language target */
    esbuild({
      minify: true,
      target: ['chrome64', 'firefox67', 'safari11.1'],
    }),    
    /** Bundle assets references via import.meta.url */
    importMetaAssets(),
    /** Minify html and css tagged template literals */
    babel({
      plugins: [
        [
          require.resolve('babel-plugin-template-html-minifier'),
          {
            modules: { lit: ['html', { name: 'css', encapsulation: 'style' }] },
            failOnError: false,
            strictCSS: true,
            htmlMinifier: {
              collapseWhitespace: true,
              conservativeCollapse: true,
              removeComments: true,
              caseSensitive: true,
              minifyCSS: true,
            },
          },
        ],
      ],
    }),
    /** Create and inject a service worker */
    generateSW({
      globIgnores: ['polyfills/*.js', 'nomodule-*.js'],
      navigateFallback: './index.html',
      // where to output the generated sw
      swDest: path.join('dist', 'sw.js'),
      // directory to match patterns against to be precached
      globDirectory: path.join('dist'),
      // cache any html js and css by default
      globPatterns: ['**/*.{html,js,css,webmanifest}'],
      skipWaiting: true,
      clientsClaim: true,
      runtimeCaching: [{ urlPattern: 'polyfills/*.js', handler: 'CacheFirst' }],
    }),
  ],
  context: "window"
};

index.html

<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
  <meta name="Description" content="Put your description here.">
  <meta http-equiv="Pragma" content="no-cache" />
    <meta http-equiv="Expires" content="0" />
  <base href="/">

  <style>
    html,
    body {
      margin: 0;
      padding: 0;
      font-family: sans-serif;
      background-color: #ededed;
    }
  </style>
  <title>frontend</title>

  <script>
    window.process = {
      env: {
        NODE_ENV: 'production',
        REDIRECTION_URL: 'https://frontend.net/',
      }
    };
  </script>

</head>

<body>
  <frontend></frontend>

  <script type="module" src="./out-tsc/src/frontend.js"></script>
</body>

</html>
解决方案

两个错误的核心原因是Nginx返回了错误的MIME类型:请求JS模块文件和Service Worker文件时,Nginx未找到对应文件,转而返回index.html,导致浏览器接收到text/html类型响应,但预期是application/javascript。

具体原因

  1. 部署文件不匹配:本地开发的文件结构与Nginx部署路径不一致。Rollup编译产物输出到dist目录,但部署时可能未将dist下的所有文件复制到Nginx的root目录(/usr/share/nginx/html),而是直接复制了源码文件。
  2. Nginx规则导致的 fallback:try_files $uri $uri/ /index.html规则会在文件不存在时直接返回index.html,浏览器将HTML当作JS模块解析,触发MIME类型错误。

修复步骤

步骤1:部署Rollup编译后的完整产物

执行rollup -c完成编译后,将dist目录下的所有文件复制到Nginx的root目录(/usr/share/nginx/html),不要直接复制源码目录的文件。

步骤2:使用Rollup生成的index.html

@web/rollup-plugin-html插件会自动处理JS模块的引用路径,编译后的dist/index.html已包含正确的哈希文件名引用,直接使用该文件即可,不要手动修改。

步骤3:优化Nginx配置(可选)

调整配置,确保静态资源返回正确的MIME类型,同时区分路由请求和静态资源请求:

http {
    include       /etc/nginx/mime.types;
    types {
        application/javascript js mjs;
        text/javascript js;
    }
    default_type  application/octet-stream;

    server {
        listen       8080;
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        server_name  localhost;
        server_tokens off;

        location / {
            try_files $uri $uri/ @fallback;
        }

        # 仅路由请求返回index.html,静态资源不存在则返回404
        location @fallback {
            rewrite ^ /index.html last;
        }

        # 静态资源缓存配置
        location ~* \.(js|css|html|png|jpg|webmanifest)$ {
            expires 1y;
            add_header Cache-Control "public, immutable";
        }

        error_page   404 405 406 500 502 503 504  /error_frontend.html;
        location = /error_frontend.html {
            root   /usr/share/nginx/html;
        }
    }
}

步骤4:验证Service Worker路径

确保dist/sw.js存在,Rollup的generateSW插件会自动注入正确的Service Worker路径,只要dist目录完整部署即可。


内容的提问来源于stack exchange,提问作者Piotr Treska

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 13:35:21