Webpack / React/ Spring Boot: caching bundle.js with https - reactjs

In my Spring-Boot/React application, i want to cache bundle.js(1.2Mb). If i run the app with http://(test-envionment-server-url)/myapp, my bundle.js get called from cache (208 B) with HTTP status 304 every time i refresh the page. Until now everything is fine.
But when i use https://(test-envionment-server-url)/myapp, bundle.js is not cached and each time i refresh it gets all 1.2Mb, yet all other images and font get cached.
I am using Tomcat in local, and JBoss 7.0.x in my test environment.
Here is my webpack.config.js
const webpack = require('webpack');
const autoprefixer = require('autoprefixer');
module.exports = {
cache: true,
entry: [
'webpack-dev-server/client?http://localhost:8008',
'webpack/hot/only-dev-server',
'babel-polyfill',
'./src/index.js',
],
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'react-hot!babel',
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
loaders: [
'url?limit=2048',
'img',
],
},
{
test: /\.(woff|woff2|eot|ttf)$/,
loader: 'url-loader?limit=2048',
},
{
test: /\.scss$/,
loaders: ['style', 'css', 'postcss-loader', 'sass'],
},
],
},
resolve: {
extensions: ['', '.js', '.jsx'],
},
output: {
path:`${__dirname}/(spring-boot-app-url/resources/static`,
publicPath: '/publicpath/',
filename: 'bundle.js',
},
devServer: {
port: 8008,
contentBase: './dist',
historyApiFallback: true,
hot: true,
proxy: {
.....(proxy config)
},
},
},
},
postcss: () => {
return [autoprefixer];
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.optimize.UglifyJsPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production'),
},
}),
],
};
in my Spring i have some Spring security configuration:
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.authorizeRequests()
.antMatchers(TOKEN_NAME).permitAll() // API
...(antMatchers)
.and()
.authorizeRequests()
.antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).permitAll();
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
httpSecurity.headers().cacheControl().disable();
}
Here is the Request header from my Network tab:
Accept:*/*
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Connection:keep-alive
Host:*****
Referer:https://****/appname/
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36
Here is the Response header from Network tab:
Accept-Ranges:bytes
Cache-Control:max-age=3600
Connection:keep-alive
Content-Length:1248915
Content-Type:application/javascript; charset=UTF-8
Date:Mon, 05 Dec 2016 12:56:47 GMT
Last-Modified:Mon, 05 Dec 2016 09:08:06 GMT
Server:JBoss-EAP/7
Strict-Transport-Security:max-age=31536000 ; includeSubDomains
X-Content-Type-Options:nosniff
X-Frame-Options:DENY
X-Powered-By:Undertow/1
X-XSS-Protection:1; mode=block
When i run it with http url, the header request doesn't have any cache control.
Any idea about how to cache bundle.js file even when i run my app using https?
Thank you

Given the provided HTTP response, it seems your server is not sending the appropriate Cache-Control headers. Doing so can cause issues when you're changing the contents of your static resource.
This is why you should:
configure a Cache-Control header to be sent when serving static resources
configure a resource chain with a content strategy (see here)
The overall configuration for your production profile should look like:
spring.resources.cache-period=3600 # caching resources for 1 hour
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**

the problem was in my JBoss Configuration:
in standalone.xml i added gzip compression properties :
<system-properties>
<property name="org.apache.coyote.http11.Http11Protocol.COMPRESSION" value="on"/>
<property name="org.apache.coyote.http11.Http11Protocol.COMPRESSION_MIME_TYPES" value="text/javascript,text/css,text/html,application/xml,application/json"/>
<property name="org.apache.coyote.http11.Http11Protocol.COMPRESSION_MIN_SIZE" value="1024"/>
</system-properties>
right under extensions.

Related

Webpack & NGINX configuration for React app with set path prefix

I'm working on a React app and I have it working when I deploy it as a Docker container, in a Rancher cluster, to what I'll call a "normal" endpoint. Meaning if I set up the endpoint to be test.site.com, everything works fine. The problem is my company is insisting on using path to direct traffic to different services. For example, test.site.com/my-site will direct traffic to my React app, but test.site.com/other-site directs traffic to another service. I'm having an issue where when I deploy my site to test.site.com/my-site, my app can't seem to resolve some of its assets. Opening the console in the browser gives errors such as:
GET https://test.site.com/cesium/Cesium.js net::ERR_ABORTED 405
GET https://test.site.com/cesium/Widgets/widgets.css net::ERR_ABORTED 404
GET https://test.site.com/bundle.js net::ERR_ABORTED 404
(Cesium is a library I'm using in my app https://cesium.com/blog/2018/03/05/integrating-cesium-and-react/)
I've only recently started working with webpack, so I think there's probably some configuration there to fix this? Here's the relevant config files.
webpack.config.js
const path = require('path')
const HtmlWebPackPlugin = require('html-webpack-plugin')
const webpack = require('webpack')
const HtmlPlugin = require('html-webpack-plugin')
const HtmlTagsPlugin = require('html-webpack-tags-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const dotenv = require('dotenv')
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js',
publicPath: '/',
},
resolve: {
modules: [path.join(__dirname, 'src'), 'node_modules'],
alias: {
react: path.join(__dirname, 'node_modules', 'react'),
},
extensions: ['.jsx', '.js'],
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
},
],
},
{
test: /\.(png|svg|jpe?g|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
],
},
devServer: {
historyApiFallback: {
index: '/',
},
},
externals: {
cesium: 'Cesium',
},
plugins: [
new HtmlWebPackPlugin({
template: './src/index.html',
}),
new webpack.DefinePlugin({
'process.env': JSON.stringify(dotenv.config().parsed), // it will automatically pick up key values from .env file
}),
new CopyWebpackPlugin({
patterns: [
{
from: 'node_modules/cesium/Build/Cesium',
to: 'cesium',
},
],
}),
new HtmlPlugin({
template: './src/index.html',
}),
new HtmlTagsPlugin({
append: false,
tags: ['cesium/Widgets/widgets.css', 'cesium/Cesium.js'],
}),
new webpack.DefinePlugin({
CESIUM_BASE_URL: JSON.stringify('/cesium'),
}),
],
}
Dockerfile
FROM node:14-slim AS builder
ENV NODE_ENV production
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
FROM nginx:1.21.0-alpine as production
ENV NODE_ENV production
COPY --from=builder /app/build /usr/share/nginx/html
RUN echo $(ls -1 /usr/share/nginx/html)
RUN chown nginx:nginx -R /usr/share/nginx
COPY nginx.conf /etc/nginx/conf.d/default.conf
RUN echo $(ls -1 /etc/nginx/conf.d/)
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
nginx.conf
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

Error when calling API in Front using React

I have an error that shows when I try to click button to subscribe. This is the error message in browser console:
Access to fetch at 'https://api.exhia.com/api/accounts/addaccount' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
This the the code example that handle the click:
handleFormSubmit(event) {
event.preventDefault();
this.setState({ msg: 'checked' });
const validation = this.validator.validate(this.state);
this.setState({ validation });
this.submitted = true;
if (
this.state.checked === true &&
validation.subConfirm_pwd.isInvalid === false
) {
fetch(`${API}/api/accounts/addaccount`, {
method: 'POST', // 'GET', 'PUT', 'DELETE'
body: JSON.stringify({
email: this.state.sub_mail,
password: this.state.sub_pwd,
acceptCGU: this.state.checked,
}),
headers: {
'Content-Type': 'application/json;charset=utf-8',
Accept: 'application/json',
},
})
.then(res => res.json())
.....
I am working on prod to handle this issue, this is my webpack.config:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
var OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'build'),
publicPath: '/',
filename: 'bundle.js'
},
devServer: {
contentBase: path.join(__dirname, './build'),
compress: true,
port: 3000,
historyApiFallback: true,
},
performance: {
hints: false,
maxEntrypointSize: 512000,
maxAssetSize: 512000
},
module: {
rules: [
{
test: /\.(css|sass|scss)$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
importLoaders: 2
},
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
},
},
],
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ["file-loader"]
},
{
test: /\.(woff(2)?|ttf|otf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
}
}
]
}
]
},
//remove comments from JS files
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false,
},
},
}),
new OptimizeCSSAssetsPlugin({
cssProcessorPluginOptions: {
preset: ['default', { discardComments: { removeAll: true } }],
}
})
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css"
}),
new ManifestPlugin(),
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: path.resolve('./public/index.html'),
}),
]
};
I am not sure where the bug comes from. How can I fix it?
http://localhost:3000 has been blocked by CORS policy so All you need to do is add an HTTP header to the server which serves your API. That means changing the API.
In your case this is either ( fatboar.extia.com ) or (api.exhia.com) - I can't tell which as you seem to use them interchangeably in your question / comments.
This site provides details on how to do that: https://enable-cors.org/server.html - should you need further guidance I would investigate how to add header for your particular platform. You should be able to see the header coming back in a tool like Postman (https://www.getpostman.com/) - I would explore the API first with that to check the header, then proceed with your code in the browser.
1) Access-Control-Allow-Origin: http://localhost:3000
Note. When you move to production, you may need to change the above to match the server making the call. So for example, locally at the moment you are hosting the app on localhost:3000. When you move to production assuming you are hosting on blah.com, your access control header would also need to change.
2) Access-Control-Allow-Origin: http://blah.com
Do not be tempted by answers suggesting a wild card e.g.
3) Access-Control-Allow-Origin: *
This will allow any authorised person to access the API from any domain, which is likely not what you want. So any site can make a request to your site on behalf of their visitors and process its response.
This is because you are trying to call a service which is running in different server.
More on cors issue
If it is for development environment, you should be using some kind of proxy for example webpack Dev server proxy
Another option is to disable the cors check in browser by some kind of plug in, simple search in chrome app store will give you many plug ins.

React js using create-app how to set X-FRAME-OPTIONS on webpack

I've searched over the net, not sure how to set X-FRAME-OPTIONS in my react app, the web.config.js looks like this, it's using inline option
when I load index.html it gives response X-FRAME-OPTIONS:DENY I need it to change it to X-FRAME-OPTIONS:SAMEORIGIN, as I need to open an iframe within my app. Right now I'm getting a chrome error and firefox error.
Not sure how I can update my web.config.js in development, I'm super confused.
module.exports = {
devtool: 'eval',
entry: {
app: [
'react-hot-loader/patch',
'webpack-dev-server/client?http://0.0.0.0' + web_port,
'webpack/hot/only-dev-server',
'./src/index'
],
vendor: [
'react',
'react-dom',
'react-router',
'react-router-dom',
'react-forms-ui',
'mobx',
'mobx-react',
'sockjs-client',
'react-table',
'react-bootstrap-table',
],
fonts: glob.sync("./src/webfonts/*")
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].bundle.js',
publicPath: '/static/'
},
X-Frame-Options is a HTTP header and setting it depends on the application you use as HTTP server, not on the files being served. In this case, if you want to set a header for webpack-dev-server, you can do it like this (setting in webpack.config.js):
devServer: {
...
headers: {
'X-Frame-Options': 'sameorigin'
}
}
nextjs
put below code in next.config.js
module.exports = {
async headers() {
return [
{
source: '/((?!embed).*)',
headers: [
{
key: 'X-Frame-Options',
value: 'SAMEORIGIN',
}
]
}
];
}
}
You can set raw http headers in public/index.html inside the head tag:
<head>
<meta http-equiv="X-Frame-Options" content="sameorigin"/>
</head>

Webpack dev server React Content Security Policy error

I have my single page app running on webpack-dev-server. I can load and reload my entry route over at localhost:8080 and it works every time. However i can load localhost:8080/accounts/login only via a link from within the app i.e whenever i reload localhost:8080/accounts/login from the browser refresh button i get
Cannot GET /accounts/login/
as the server response, and on the console i get
Content Security Policy: The page’s settings blocked the loading of a
resource at self (“default-src http://localhost:8080”). Source:
;(function installGlobalHook(window) { ....
This is my CSP header on the single page app's index.html
<meta http-equiv="Content-Security-Policy"
content="default-src * 'self' 'unsafe-inline' 'unsafe-eval'">
I am also not using any devtool on my webpack.config.json. What am i missing.
If you use Webpack in your project, please add output.publicPath = '/' and devServer.historyApiFallback = true in your webpack config file.
More info: React-router urls don't work when refreshing or writting manually
I struggled a couple hours to fix this issue. There is a just simple code that you have to add. Just follow the instruction of below. If you face problem to browse from specific url to another url, you will be able to fix that also. If you would like to configure from webpack config file, then write below's code.
devServer: {
historyApiFallback: true
}
And If you would like to run by cli command, then use the below's code.
"start": "webpack-dev-server --history-api-fallback"
It worked for me. I had not to do anything else to fix this issue like meta tag or something else.
If you're using Rails and Webpacker and get this error, note that the initializer config/initializers/content_security_policy.rb has a Content Security Policy for Rails.env.development. Changing :https to :http on that line solved the error for me. (And remember that localhost is not the same as 127.0.0.1 as far as the CSP is concerned.)
adding output: { ..., publicPath: "/", } and devServer: { historyApiFallback: true } worked
in webpack.config.js
const path = require("path");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "public"),
filename: "main.js",
publicPath: "/", // ++
},
target: "web",
devServer: {
port: "6060",
static: ["./public"],
open: true,
hot: true,
liveReload: true,
historyApiFallback: true, // ++
},
resolve: {
extensions: [".js", ".jsx", ".json", ".ts"],
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: "babel-loader",
},
// CSS rules
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
],
},
};
I had similar issue. Had to remove the contentBase line from devServer configuration in webpack.config.js.
This is my webpack.config.js:
var path = require("path");
module.exports = {
devtool: 'inline-source-map',
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
publicPath: "/assets/",
filename: "bundle.js"
},
devServer: {
port: 9002
},
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' }
]
}
};

webpack dev server CORS issue

I am using webpack-dev-server v1.10.1 to boost up my Redux project and I have the options below:
contentBase: `http://${config.HOST}:${config.PORT}`,
quiet: false,
noInfo: true,
hot: true,
inline: true,
lazy: false,
publicPath: configWebpack.output.publicPath,
headers: {"Access-Control-Allow-Origin": "*"},
stats: {colors: true}
In the JS, I am using request from superagent to generate a HTTP GET call
request
.get(config.APIHost + apiUrl)
.set('Accept', 'application/json')
.withCredentials()
.end(function (err, res) {
if (!err && res.body) {
disptach(() => {
return {
type: actionType || GET_DATA,
payload: {
response: res.body
}
}
});
}
});
But I got the CORS error:
XMLHttpRequest cannot load http://localhost:8000/api/getContentByType?category=all. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:5050' is therefore not allowed access
Any suggestion to resolve this? Thanks a lot
Another way to work around it is to directly add the required CORS headers to the dev server:
devServer: {
...
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"
}
}
Doc links
Webpack dev server
Access-Control-Allow-Headers
Access-Control-Allow-Methods
Access-Control-Allow-Origin
With webpack-dev-server 1.15.X you can use this configuration in your config file:
devServer: {
contentBase: DIST_FOLDER,
port: 8888,
// Send API requests on localhost to API server get around CORS.
proxy: {
'/api': {
target: {
host: "0.0.0.0",
protocol: 'http:',
port: 8080
},
pathRewrite: {
'^/api': ''
}
}
}
},
With this example you will redirect all calls from http://0.0.0.0:8888/api/* to http://0.0.0.0:8080/* and CORS solved
Had the same issue, but my api was on a https protocol (https://api....). Had to start the server with https and use https://localhost:8080
devServer: {
https: true,
headers: {
"Access-Control-Allow-Origin": "*",
},
// ....
}
You're running your JavaScript from localhost:5050 but your API server is localhost:8000. This violates the same origin policy, so the browser disallows it.
You can either modify your API server so that CORS is enabled, or follow the instructions on the webpack-dev-server page under "Combining with an existing server" to combine asset serving with webpack-dev-server and your own API server.
There are 2 solutions for this. first one is setting up proxy on the client side, second one is setting CORS on the server. CORS is server issue, server does not allow access from different source. Even using different ports is considered to be different source
First Solution
IN your backend code, you have to set this headers: this is example of in express node.js
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader(
"Access-Control-Allow-Methods",
"OPTIONS, GET, POST, PUT, PATCH, DELETE"
);
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
next();
});
Second Solution:
in webpack config.js, if you want to pass any variable, we export
module.exports=function(env){
return {}}
instead of
module.exports={}
we inject this env through the script.
"dev-server": "webpack-dev-server --env.api='https://jsonplaceholder.typicode.com/users'",
now webpack has access to this env. in webpack.config.js
module.exports = function ({
api = "https://jsonplaceholder.typicode.com/users",
}) {
return {
entry: { main: "./src/index.js" },
output: {
path: path.resolve(__dirname, "public"),
filename: "[name]-bundle.js",
publicPath: "/",
},
mode: "development",
module: {
rules: [
{
loader: "babel-loader",
test: /\.js$/,
exclude: [/node_modules/],
},
{
// Load other files, images etc
test: /\.(png|j?g|gif|ico)?$/,
use: "url-loader",
},
{
test: /\.s?css$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
],
},
//Some JavaScript bundlers may wrap the application code with eval statements in development.
//If you use Webpack, using the cheap-module-source-map setting in development to avoid this problem
devtool: "cheap-module-eval-source-map",
devServer: {
contentBase: path.join(__dirname, "public"),
historyApiFallback: true,
proxy: {
"/api": {
changeOrigin: true,
cookieDomainRewrite: "localhost",
target: api,
onProxyReq: (proxyReq) => {
if (proxyReq.getHeader("origin")) {
proxyReq.setHeader("origin", api);
}
},
},
},
},
};
};

Resources