angular: After upgrading to Angular 8, production builds can't load: Failed to load module script: The server responded with a non-JavaScript MIME type of "text/plain". Strict MIME type checking is enforced for module scripts per HTML spec.

🐞 bug report

Affected Package Angular 8

Is this a regression? YES

Description

My production distribution is being served from Azure Blob Storage static site. Is it possible that serving from the static site is causing this error?

This issue happens in Chrome but not Edge.

After upgrading to Angular 8, production builds can’t load. Browser console error: Failed to load module script: The server responded with a non-JavaScript MIME type of “text/plain”. Strict MIME type checking is enforced for module scripts per HTML spec.

🔬 Minimal Reproduction

Upgrade an Angular 7 app to Angular 8. Do production build, deploy the production build to Azure Blob storage static web site and attempt to open it.

đŸ”„ Exception or Error


Failed to load module script: The server responded with a non-JavaScript MIME type of "text/plain". Strict MIME type checking is enforced for module scripts per HTML spec.


🌍 Your Environment

Angular Version:




Angular CLI: 8.0.1
Node: 11.6.0
OS: win32 x64
Angular: 8.0.0
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.800.1
@angular-devkit/build-angular     0.800.1
@angular-devkit/build-optimizer   0.800.1
@angular-devkit/build-webpack     0.800.1
@angular-devkit/core              8.0.1
@angular-devkit/schematics        8.0.1
@angular/cdk                      7.3.7
@angular/cli                      8.0.1
@angular/material                 7.3.7
@ngtools/webpack                  8.0.1
@schematics/angular               8.0.1
@schematics/update                0.800.1
rxjs                              6.5.2
typescript                        3.4.5
webpack                           4.30.0

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 63
  • Comments: 130 (7 by maintainers)

Commits related to this issue

Most upvoted comments

You must change the property “target” in the file “tsconfig.json” to “es5”. Read this blog entry, “Differential Loading by Default”:

https://blog.angular.io/version-8-of-angular-smaller-bundles-cli-apis-and-alignment-with-the-ecosystem-af0261112a27

This property chooses between modern or legacy JavaScript based on the browser capabilities:

<script type="module" src="
"> // Modern JS

<script nomodule src="
"> // Legacy JS

Hello, we reviewed this issue and determined that it doesn’t fall into the bug report or feature request category. This issue tracker is not suitable for support requests, please repost your issue on StackOverflow using tag angular-cli.

If you are wondering why we don’t resolve support issues via the issue tracker, please check out this explanation.

@Aurora7Sys

I found the source of the problem. Angular 8 removed – type=“text/javascript” from the script tags in index.html. Angular 7 had them which is why it worked with Azure Storage.

We also discovered that the Azure portal manual upload did not add the MIME metadata to .js files that were uploaded. So
 when I manually uploaded my Angular 8 site, it produced the above errors.

I started using Visual Studio Code to publish my Angular 8 apps to Azure Storage and it’s working because the publish extension adds the correct MIME metadata to the files.

Microsoft will look into correcting the Azure portal manual upload to add the MIME metadata so this can’t happen on their server.

@kdawg1406 sounds like a server issue to me. What happens if you serve a production build yourself locally? I bet that works.

@alan-agius4

Why is this closed? This is a regression. ng7 created a proper index.html. At least in some situations, ng8 does not. I have not seen a single “solution” that was not a work-around for an ng8 flaw and unrelated to the consumer’s code. ng8 should not be creating ambiguous script tags.

Still no solution to this problem??

the easy solution just replace target with es5 in tsconfig,json

"target": "es5",

I got the same problem when I host the files on AWS S3. The problem is from the “type=‘module’”, I had to manually change it into “type=‘text/javascript’” to make it work again.

Why “type=‘module’” in Angular 8, I’m wondering? What is the benefit of it over “type=‘text/javascript’” ?

I am not so sure this should be closed or sent off to Stack Overflow just yet. You can reproduce this problem with a brand new version 8.02 “hello world” app generated from the CLI. The exact same version 7 app has always worked just fine and still does up on Azure storage. So, since there is not really anything we can do to reconfigure Azure Storege, what can we do to get Version 8 apps running up there? Something has changed that broke this capability and free hosting is a pretty good reason to use Angular. 😃

Version 8 apps Do work fine on a “real” web server though.

Thanks!

Changing the target to ES5 is not a solution, it is a workaround. It means the typescript compiler can’t use modern javascript features and will have to emulate them. I fix it by changing the generated index.html and add the mime type. Not a lot of work but a nuisance.

@lion9 understand. Full disclosure, my solution worked for me because the VS Code Extension that performs the upload to the Azure Blob Storage applied the correct metadata to the .js files.

Angular 8 does publish the .js files differently. If you compare an Angular 7 index.html with the Angular 8 index.html the scripts at the bottom of the file are different. Angular 7 .js files have the type=“text/javascript” where the Angular 8 index.html script files do not add this.

Angular 7 <script type="text/javascript" src="runtime.26209474bfa8dc87a77c.js"></script>

I believe this is the root of the problem, Recommend contacting GitHub and ask them how you can apply the required metadata to the files.

Maybe you can try and add the type=“text/javascript” to the script tags that don’t have a type attribute? If I remember correctly half of the files have the attribute, the others don’t.

Best regards, Karl

I mean
 100% true story, I just convinced the guy managing this project to use Angular instead of react, and now I look like an ass-hat because we can’t even deploy the application.

This issue is posted on Stack Overflow , but no resolution yet, thanks!

I solved it by creating a task in gulp, I share the steps:

  1. npm install --save-dev gulp
  2. npm install --save-dev gulp-replace
  3. create in the root of the project a file with the name: gulpfile.js
  4. copy the code in the gulpfile.js:
const { src, dest } = require('gulp');
var replace = require('gulp-replace')

exports.default = function (cb) {    
    src(['dist/ng-project/index.html'])
        .pipe(replace('type="module"', 'type="text/javascript"'))
        .pipe(dest('dist/ng-project/'));
    cb();
};
  1. In the packaje.json file, modify the “electron” script as follows: "electron": "ng build --base-href ./ && tsc --p electron && gulp && electron ."
  2. run your project: npm run electron

note: In the main electron file, make sure you go to the dist folder of your index.html file Ejemplo:

win.loadURL(url.format({
        pathname: path.join(__dirname, `/../../dist/ng-project/index.html`),
        protocol: "file:",
        slashes: true
        })
    );

Check that you are trying to load correct filenames. You are simply getting a 404 from backend and the browser cant load the 404 page as JS as a <script>

2020 and this is still an issue and it is even closed?!?! None of the solutions above worked for me, still not sure what is going on, I have an angular app - ssr on nginx

@Erayus I think it has something to do with getting a modern browser to load the file instead of the other version of the file.

I would reach out to the Angular team, you may be causing a problem.

The server metadata needs to be applied to the files, and they will load correctly.

I do think Angular needs much better docs and testing to explain to developers how to deploy Angular 8 apps.

The original issue was caused by a defect within the Azure portal’s blob storage upload functionality. The portal was not properly setting content types when performing the upload. When using object storage to host static sites it is important that the tools used perform a complete upload of the files including content type. First party tools such as the Azure and AWS CLI provide this functionality. If using custom tools (direct API usage, for example) or third-party tools, please ensure that they are also performing these steps.

Version 8.0 of the Angular CLI introduced the differential loading functionality. This leverages the module script functionality in modern browsers to support loading the appropriate scripts for different browsers. Module scripts provide the ability to use the latest ECMAScript functionality within supported browsers and are needed to use native import functionality.

When using module scripts, the HTML specification mandates that the MIME/content type header for the received data be valid. The content type that should be sent by the server for JavaScript files (.js) is text/javascript (or one of the equivalents found here). Most servers and hosting services will do this automatically as JavaScript files are a common file type. If using object storage services for hosting, commonly used first-party tools for those services will also set the appropriate type when uploading. There is no guarantee that a browser will execute a script (module or otherwise) with an invalid content type. This is especially true when using security-related headers such as X-Content-Type-Options=nosniff (details). Even if module scripts are not in use, the correct content type header should still be sent by the server.

Improper or missing content type headers can have a wide range of negative impacts on a site; some of which are security related. For more information on this please see: https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Configuring_server_MIME_types

There is also an unrelated error that some may be receiving that presents itself with the same message but a sent content type of text/html. This error is generally caused by either missing files on the server, an incompatible deploy URL or base HREF application setting for the deployed location, or a misconfigured server rewrite rule for path-based routing. What is happening in this case is that the path-based routing rewrite rule is being triggered due to a file not being present at the requested location. The rewrite rule in turn causes the index HTML file to be sent which has a content type of text/html. If the originally requested file was a script or stylesheet, then the error may be shown since HTML is not a valid script or stylesheet. For information regarding server routing configuration, please see: https://angular.io/guide/deployment#routed-apps-must-fallback-to-indexhtml For information regarding the base HREF, please see: https://angular.io/guide/deployment#the-base-tag

Additional documentation within the deployment guide is in the process of being written to better explain these types of errors as well as steps that can be taken to remedy them.

@thel0ner Agreed this needs to be fixed in the angular CLI. We use S3 for hosting the website as well. The whole benefit of using a SPA is that you don’t have to have a full fledged web server to host your application. HTML should just be formatted text that can be read by any browser.

And honestly, it’s going to be things like this that push people to react/vue

Instead of disabling differential loading by forcing an es5 build, I was able to solve the problem by setting the crossOrigin property on the build. The underlying problem was a Chrome bug preventing authentication headers from being sent with the request for the javascript files.

Settings

Specify crossOrigin for the production build by either modifying the build line item in package.json to be something like "build": "ng build --prod --crossOrigin=anonymous .... or alternatively set the crossOrigin policy in the production configuration of your angular.json file. Either method will have the same effect on the build.

// file: angular.json
{
    "projects": {
        "your-project-name": {
            "architect": {
                "build": {
                    "configurations": {
                        "production": {
                            //....
                            "crossOrigin": "anonymous"
                            //....
                        }
                    }
                }
            }
        }
    }
}

Explanation:

Per this stackoverflow question, it would appear that when older versions of Chrome (issue occurs in Chrome 70, but has been fixed in some build at or before Chrome 78) see a <script src="..." type="module">, Chrome neglects to send authentication headers along with the request for the script file. As such, in many situations, the server is responding with either a 404, or some other authorization exception instead of the requested script resource. The received error page will likely have a MIME type of text, as it’s an error page after all, and not the requested script resource.

Setting the crossorigin attribute on the <script src="..." crossorigin type="module"> causes these older versions of Chrome to properly send along authentication headers, and thus the correct server resources are returned, instead of the aforementioned 404 / authentication error page.

Using the above build parameters forces Angular to add a crossorigin="anonymous" attribute to the <script> tags (note crossorigin="anonymous" is the same as a naked crossorigin attribute mentioned in the stackoverflow question).

I was facing the same issue but after one hour i found out that i was also playing aroud with apache .htaccess for routing the server and there is one rule which is RewriteRule ^ index.html which is the cause of the error of non-JavaScript MIME type of "text/html"

When i have removed that rule and done a hard refresh it is working as it should be, so if you are facing the same issue then also check the .htaccess for any intentional or unintentional error and also don’t forget to hard refresh after changing it, thanks.

That is a webserver related problem. Angular 7 did include a MIME type in the <sripts> tags in index.html: <script type="text/javascript" src="runtime.26209474bfa8dc87a77c.js">

Angular 8 does not include a MIME type in <script> tags in index.html: <script src="runtime-es2015.703a23e48ad83c851e49.js" type=“module”>`

<script src="runtime-es5.465c2333d355155ec5f3.js" nomodule>

It’s there fore up to the webserver what MIME type is returned when the browser does retrieve the .js file. In my case Internet Explorer and Edge could load the app, Firefox and Chrome did refuse to open the app with error “Failed to load module script: The server responded with a non-JavaScript MIME type of “text/x-js”. Strict MIME type checking is enforced for module scripts per HTML spec.”

The solution was to add a .htaccess file into the root directory of the app on the apache webserver to force the server to deliver .js files as MIME type “text/javascript”. Content of .htaccess:

# Force MIME Type text/javascript for all .js files # Needed for Angular 8 since type="text/javascript" is not specified anymore in <script> tags in index.html AddType text/javascript .js

Same issue when packaging with Electron, seems to be resolved by setting type=“text/javascript” instead of type=“module”.

It is a server issue. This issue was noticed only with Alias apache config.

Under apache 2.4:

  • I noticed this issue when deploying under Alias folder
  • BUT I did not notice this issue when deploying directly under DocumentRoot folder

Configuration: apache 2.4 Angular CLI version: 8.3

It appears that we can not run an Angular application (from v8) under Alias folder. The workaround is to deploy under a real subfolder.

Let us consider the final url of my_angular_application https://www.example.com/my_angular_application

  1. The server folder structure us as follows
/folder_path_related_to_www_example_com
---/my_angular_application
------index.html
  1. The build command is as follows ng build --prod --baseHref /my_angular_application/

  2. The apache config is as follows

<VirtualHost *:443>
    ServerName www.example.com
    DocumentRoot /folder_path_related_to_www_example_com

    DirectoryIndex index.html

    # Angular part
    <Directory "/folder_path_related_to_www_example_com/my_angular_application" >
        Require all granted

        # Angular
        RewriteEngine On
        RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
        RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
        RewriteRule ^ - [L]
        RewriteRule ^ /my_angular_application/index.html
    </Directory>
</VirtualHost>```


@thel0ner Not everybody uses a full fledged web server. A generated index.html should be parseable by a browser “as is”, a modification by the web server should not be necessary. I run the the angular generated code from a server based on https://github.com/eidheim/Simple-Web-Server. When you ask it for index.html, it sends you the file unmodified. I really don’t understand why the mime type is not added to the script references in the index.html. When I add them manually everything is fine, but it is a nuisance.

@ranaivomahaleo And if you are not using a dedicated webwerver such as apache? If you use a very simple server that does no ‘rewrites’ of the generated index.html, but just serves the file as is? In angular 7 this was no problem, and now I have to edit the index.html manually after each build. I have not seen an explanation in this thread of why this cannot be fixed. I seems trivial to me. (as a developer I am aware that things not always are that simple, but at least give an explanation of why it cannot be fixed)

Adding this to my nginx.conf is the only solution that worked for me

http {
    types {
      module js;
    }
    include       /etc/nginx/mime.types;
    # rest of your config...
}

Found the this solution here.

as @Haritsinh mentioned above, I confirm that this is a issue with your server, not ng. perhaps you guys need a better rewrite rules? for example, a extremely poor, simple approach for apache .htaccess would be : ErrorDocument 404 /index.html

This issue affects anyone using Cordova, as Cordova ultimately serves up files via the file:// protocol. I can’t change my “server” to serve up the js files as text/javascript afaik as there is no server. There’s an issue on Cordova’s github, but I don’t know that it’s anything the Cordova team can resolve.

Does anyone know if there’s any support for the file:// with javascript modules?

try browerslist

If you are deploying your build inside the folder in your server, then while generating the prod build make sure you add the folder to the --base-href flag and the folder path. Following is the command if you are deploying inside the “www” folder

ng build --prod --base-href /www/

Finally fixed it in my app after some trial and error. In angular.json, I had to change my output path to "outputPath": "dist/", - See this comment for a deeper dive

This issue appears to happen only if the application’s base href is set with flag --base-href to a folder other than “/”. None of the solutions offered here will be to the rescue if that happens.

If possible, serve your application from root (/) of your web server.

$ng build --aot --prod

Omit --base-href=“project-name”

hi here i have a solution that may help to you.

  1. Build your angular app using command “ng build --prod”
  2. create a js file inside the root project (or anywhere else is also fine, according to the location you have to give the path in 3rd step) . here i am creating as angularserver.js
  3. Write the following lines of code

const express = require(‘express’); const bodyParser = require(‘body-parser’); const cors = require(‘cors’); const path = require(‘path’);

var app = express(); app.use(bodyParser.json()); app.use(cors());

app.listen(4200, () => console.log(‘server started 4200’));

app.use(express.static(path.join(__dirname,‘./dist/<project name>’))); //you can check inside dist folder you must have a folder which is nothing but the projectname; //give the correct path if you create the js file in any other location

app.get(“*”,(req,res)=>{ return res.sendFile(path.join(__dirname,‘./dist/<project name>/index.html’)); });

  1. then now run the command “node angularserver.js”
  2. If you will find any error like cors module not found or something else like that just install those module using “npm install cors --save”
  3. again run node angularserver.js after installing and then go to browser and run ip:4200

Note : above solution is purely based on nodejs, i attached the angular build file in nodejs path and run it. it is working for me. If you have any doubt let me know

I encountered the same problem For a month or two angular 8 build worked fine for me, but maybe browsers changed something and today the build gave me the same error.

"module": "esnext", "target": "es2015",

downgrading to: "module": "es2015", "target": "es5", and changing lazy loading syntax solved this issue, but this is temporary workaround. Is Angular team working on this issue?

my angular packages versions: "@angular/cdk": "8.0.2", "@angular/common": "^8.1.1", "@angular/compiler": "^8.1.1", "@angular/core": "^8.1.1",

You should be defining your output browser support inside the browserslist file. In this case for the lack of support in modern browsers you should be adding something like:

Chrome >= 70 and Chrome <= 75

Adding this inside your browserslist should add support for chrome and chromium based applications.

For additional information you can see this github repo. It has a list of other options for inside the browserslist https://github.com/browserslist/browserslist

I struggled with this issue for a whole day and finally, it was some infra issue with Kubernetes. I don’t have details at the moment, but when it happens to you, don’t think it is just your angular code issue. This can occur from different layers. I’ll update this post about what went wrong in K8 once I got the post-mortem from our ci team.

@Amitesh as noted elsewhere, if you are running into this on Electron this is expected as the file:// protocol does not convey mimetypes to the Chromium engine. The recommended solution for Electron is to implement a custom protocol, which is more reliable and does not require any changes/patches to your build output. It also gives you other benefits as file:// is quite restricted within browser engines.

For more information about this, see the Electron docs. You can find folks discussing this solution (and an example custom protocol) at https://github.com/electron/electron/issues/12011#issuecomment-369705509

As described by @clydin in one of the above comments, this is not a bug in Angular itself.

This is mostly a documentation issue.

I am reposting the quoted message below:

The original issue was caused by a defect within the Azure portal’s blob storage upload functionality. The portal was not properly setting content types when performing the upload. When using object storage to host static sites it is important that the tools used perform a complete upload of the files including content type. First party tools such as the Azure and AWS CLI provide this functionality. If using custom tools (direct API usage, for example) or third-party tools, please ensure that they are also performing these steps.

Version 8.0 of the Angular CLI introduced the differential loading functionality. This leverages the module script functionality in modern browsers to support loading the appropriate scripts for different browsers. Module scripts provide the ability to use the latest ECMAScript functionality within supported browsers and are needed to use native import functionality.

When using module scripts, the HTML specification mandates that the MIME/content type header for the received data be valid. The content type that should be sent by the server for JavaScript files (.js) is text/javascript (or one of the equivalents found here). Most servers and hosting services will do this automatically as JavaScript files are a common file type. If using object storage services for hosting, commonly used first-party tools for those services will also set the appropriate type when uploading. There is no guarantee that a browser will execute a script (module or otherwise) with an invalid content type. This is especially true when using security-related headers such as X-Content-Type-Options=nosniff (details). Even if module scripts are not in use, the correct content type header should still be sent by the server.

Improper or missing content type headers can have a wide range of negative impacts on a site; some of which are security related. For more information on this please see: https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Configuring_server_MIME_types

There is also an unrelated error that some may be receiving that presents itself with the same message but a sent content type of text/html. This error is generally caused by either missing files on the server, an incompatible deploy URL or base HREF application setting for the deployed location, or a misconfigured server rewrite rule for path-based routing. What is happening in this case is that the path-based routing rewrite rule is being triggered due to a file not being present at the requested location. The rewrite rule in turn causes the index HTML file to be sent which has a content type of text/html. If the originally requested file was a script or stylesheet, then the error may be shown since HTML is not a valid script or stylesheet. For information regarding server routing configuration, please see: https://angular.io/guide/deployment#routed-apps-must-fallback-to-indexhtml For information regarding the base HREF, please see: https://angular.io/guide/deployment#the-base-tag

Additional documentation within the deployment guide is in the process of being written to better explain these types of errors as well as steps that can be taken to remedy them.

It was my first time deploying an Angular App (v9) to Azure and I was having an issue with reloading web pages on a route (F5, Ctrl+F5, Ctrl+R).

I was getting an error “Failed to load module script: The server responded with a non-JavaScript MIME type of “text/html””.

I was able to solve this by changing my ng build from ng build --prod --base-href to ng build --prod.

I was able to work around this by manually setting the type attribute for each script and css file in the index.html in the ng build --prod output folder.

e.g. <script type="application/javascript"></script> <link rel="stylesheet" type="text/css">

@adamdport This is unfortunately a limitation of Cordova. It does not support module scripts directly. Even something like the following will fail:

<script type="module">
    import abc from "./xyz.js"
</script>

To work around this limitation, you can use this plugin: cordova-plugin-ionic-webview. It is used automatically with ionic applications but can also be used by any Cordova application. It also simplifies the use of path-based routing which may also be useful to you.

@philtyl this link should help you on how to set up a mime type in Azure app services https://social.microsoft.com/Forums/Azure/en-US/d3af7fa3-dc2b-415b-b281-333c26f833c5/azure-app-service-mime-type-aac?forum=AzureAPIApps

Also, @Splaktar compiled a compressive list on how to add mime types to various cloud providers: https://github.com/angular/angular/issues/35734#issuecomment-593024055

You must change the property “target” in the file “tsconfig.json” to “es5”. Read this blog entry, “Differential Loading by Default”:

@btey This worked for me. In earlier version you will find, target=“es2015”. I just removed 2015 and added 5. target=“es5”

We also solved the issue server-side. In our case, we tweaked a setting in Akamai that will always set the correct mime type for JS files.

defer and nomodule attributes are not identifiable for the thymeleaf resolver and those are not well formatted on the index.html script tags because of that the angular 8 version is not such helpful for the user who use thymeleaf , please give us a solution for that issue , other version upto angular 7 are working well but this version is not work as expected , thanks

i solved this in tsconfig.json change target to es5 and module to commonjs

Ex: { “compileOnSave”: false, “compilerOptions”: { “baseUrl”: “./”, “outDir”: “./dist/out-tsc”, “sourceMap”: true, “declaration”: false, “downlevelIteration”: true, “experimentalDecorators”: true, “module”: “commonjs”, “moduleResolution”: “node”, “importHelpers”: true, “target”: “es5”, “typeRoots”: [ “node_modules/@types” ], “lib”: [ “es2018”, “dom” ] }, “angularCompilerOptions”: { “fullTemplateTypeCheck”: true, “strictInjectionParameters”: true } }

While this only treats the symptom, you can opt out of differential loading to buy you some time to deal with the problem.

We use ADO build pipelines to drop files into blob storage running static website and fronted with a CDN, so I may be looking at just running a custom post-copy PS script again like in the earlier days to apply correct mime types.

@spock123 thank you for your remarks. I’ll try this in the morning.