Laravel-mix: Cannot use https with webpack-dev-server

Created on 12 Jan 2021  路  5Comments  路  Source: JeffreyWay/laravel-mix

  • Laravel Mix Version: 6.0.9
  • Node Version (node -v): 14.15.0
  • NPM Version (npm -v): 6.14.9
  • OS: catalina

Description:

I'm trying to upgrade my Laravel Mix project. I am running webpack-dev-server over https, however, all hot updates are coming back as http and thus not working (I get a mixed content warning as the json is served over HTTP).

Steps To Reproduce:

This is my config:

  .options({
    hmrOptions: {
      host,
      port: 8080,
    },
  })
  .webpackConfig({
    devServer: {
      host,
      port: 8080,
      https: {
        cert: // path to cert,
        key:  // path to key,
    },
});

When I run mix watch --hot it loads correctly initially. However, subsequent hot updates receive a mixed content warning that it is served over HTTP.

I see this line which suggests to me that --https needs to be in the command. However, when I pass that command I get either "unknown option --https" ... or if I do mix watch --hot -- --https it is not respecting my key and cert and host as specified in my webpack config.

Most helpful comment

@silvioiannone your suggestion does not work it completely ignores it as well you cannot pass config to webpack options if you still want to use laravel mix. publicPath is available with mix.options({ publicPath }), but its functionality is completely different. So will this be solved? I want to test webShare API locally... Guess with laravel mix hmr its not possible...

After a lot of trial and error made it work like that:
webpack.config.js

const path = require('path');
const fs = require('fs');

module.exports = {
    resolve: {
        alias: {
            '@': path.resolve(__dirname, 'resources/js'),
        },
        extensions: ['*', '.vue', '.js', '.json'],
    },
    devServer: {
        public: `${process.env.APP_URL}:${process.env.APP_PORT}/`,
        https: {
            key: fs.readFileSync(process.env.APP_SSL_KEY),
            cert: fs.readFileSync(process.env.APP_SSL_CERT),
        },
        injectHot: (config) => {
            config.output.publicPath = `${process.env.APP_URL}:${process.env.APP_PORT}/`;
            return true;
        },
        headers: {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
            "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
        }
    }
};

webpack.mix.js

const mix = require('laravel-mix');
const fs = require('fs');
const path = require('path');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel applications. By default, we are compiling the CSS
 | file for the application as well as bundling up all the JS files.
 |
 */

process.argv.push(
    '--https',
    '--key ' + process.env.APP_SSL_KEY,
    '--cert ' + process.env.APP_SSL_CERT
);

mix.js('resources/js/app.js', 'public/js').vue()
    .postCss('resources/css/app.css', 'public/css', [
        require('postcss-import'),
        require('tailwindcss'),
        require('autoprefixer'),
    ])
    .options({
        hmrOptions: {
            host: process.env.APP_URL.replace(/https?:\/\//, ''),
            port: process.env.APP_PORT,
        },
        https: true,
        autoprefixer: { remove: false }
    })
    .webpackConfig(require('./webpack.config'))
    .sourceMaps(false);

mix.alias({
    '@': path.join(__dirname, 'resources/js'),
    vue$: path.join(__dirname, 'node_modules/vue/dist/vue.esm.js')
});

if (mix.inProduction()) {
    mix.version();
}

The most important part is here:

        injectHot: (config) => {
            config.output.publicPath = `${process.env.APP_URL}:${process.env.APP_PORT}/`;
            return true;
        },

Seems that injectHot is the last point before config json is returned to frontend. So you still can override config.output.publicPath, for some reason no matter how many places i tried to make publicPath to be https it always went back to http... Seems like something was splitting url into parts and putting back default schema...

But this helped.
With lots of console.log saw what each part of config object contains. And noticed that this specific part was http, after changing it to https it worked.

process.env.APP_URL is basically full base url: https://domain.tld

All 5 comments

Note: adding process.argv.push('--https') to the top of my mix config will address the issue.

I used to address this issue by adding --key and --cert flags to my hot task, but it does not appear to allow me to do it this way anymore.

I figured out that there are a few steps involved in fixing this problem. I'm writing my understanding of things and the steps I performed in order to fix it.

First of all: Laravel Mix, as you have noticed, looks at the presence of the --https command line option in order to toggle HTTPS support. The problem is that if we specify the --https flag then webpack-dev-server probably expects the certificate and key to passed via CLI (--key and --cert options). If those CLI options are not present then webpack-dev-server will generate its own self-signed certificates.

process.argv.push(
    '--https',
    '--key ' + process.env.APP_SSL_KEY,
    '--cert ' + process.env.APP_SSL_CERT
);

Secondly, we need to update the Webpack configuration so that it looks a bit like this:

{
    config: {
        publicPath: 'https://' + process.env.APP_DOMAIN + ':8080/',
    },
    devServer: {
        https: {
            key: fs.readFileSync(process.env.APP_SSL_KEY),
            cert: fs.readFileSync(process.env.APP_SSL_CERT)
        }
    }
}

We can easily merge our own Webpack configuration using mix.webpackConfig(config).

P.S. Laravel Mix 6 uses the ^4.0.0-beta.0 version of the webpack-dev-server which introduces some breaking changes.

@silvioiannone your suggestion does not work it completely ignores it as well you cannot pass config to webpack options if you still want to use laravel mix. publicPath is available with mix.options({ publicPath }), but its functionality is completely different. So will this be solved? I want to test webShare API locally... Guess with laravel mix hmr its not possible...

After a lot of trial and error made it work like that:
webpack.config.js

const path = require('path');
const fs = require('fs');

module.exports = {
    resolve: {
        alias: {
            '@': path.resolve(__dirname, 'resources/js'),
        },
        extensions: ['*', '.vue', '.js', '.json'],
    },
    devServer: {
        public: `${process.env.APP_URL}:${process.env.APP_PORT}/`,
        https: {
            key: fs.readFileSync(process.env.APP_SSL_KEY),
            cert: fs.readFileSync(process.env.APP_SSL_CERT),
        },
        injectHot: (config) => {
            config.output.publicPath = `${process.env.APP_URL}:${process.env.APP_PORT}/`;
            return true;
        },
        headers: {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
            "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
        }
    }
};

webpack.mix.js

const mix = require('laravel-mix');
const fs = require('fs');
const path = require('path');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel applications. By default, we are compiling the CSS
 | file for the application as well as bundling up all the JS files.
 |
 */

process.argv.push(
    '--https',
    '--key ' + process.env.APP_SSL_KEY,
    '--cert ' + process.env.APP_SSL_CERT
);

mix.js('resources/js/app.js', 'public/js').vue()
    .postCss('resources/css/app.css', 'public/css', [
        require('postcss-import'),
        require('tailwindcss'),
        require('autoprefixer'),
    ])
    .options({
        hmrOptions: {
            host: process.env.APP_URL.replace(/https?:\/\//, ''),
            port: process.env.APP_PORT,
        },
        https: true,
        autoprefixer: { remove: false }
    })
    .webpackConfig(require('./webpack.config'))
    .sourceMaps(false);

mix.alias({
    '@': path.join(__dirname, 'resources/js'),
    vue$: path.join(__dirname, 'node_modules/vue/dist/vue.esm.js')
});

if (mix.inProduction()) {
    mix.version();
}

The most important part is here:

        injectHot: (config) => {
            config.output.publicPath = `${process.env.APP_URL}:${process.env.APP_PORT}/`;
            return true;
        },

Seems that injectHot is the last point before config json is returned to frontend. So you still can override config.output.publicPath, for some reason no matter how many places i tried to make publicPath to be https it always went back to http... Seems like something was splitting url into parts and putting back default schema...

But this helped.
With lots of console.log saw what each part of config object contains. And noticed that this specific part was http, after changing it to https it worked.

process.env.APP_URL is basically full base url: https://domain.tld

@juslintek This is how I solved it for my project and, regardless of what you say, it's working: https://github.com/silvioiannone/spa-skeleton/blob/next/webpack.mix.js. What you want to look at is the init() function.

You can also check the related webpack.config.js file (loaded by webpack.mix.js) here: https://github.com/silvioiannone/spa-skeleton/blob/next/webpack.config.js

@juslintek the solution with injectHot fixed the same problem on mix 6.0.13. Thank you

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mstralka picture mstralka  路  3Comments

hasnatbabur picture hasnatbabur  路  3Comments

amin101 picture amin101  路  3Comments

jpmurray picture jpmurray  路  3Comments

jpriceonline picture jpriceonline  路  3Comments