From dacd9607e49c6993a98cf0829f0985ae8c28c339 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 5 Mar 2025 20:14:49 -0800 Subject: [PATCH] update ttl, use file based hashing to prevent stale data. --- nginx.conf | 103 ++++++++++++++++++++++++++++++++++++++----- src/server/Master.ts | 21 ++++++++- webpack.config.js | 68 +++++++++++++++++++++------- 3 files changed, 164 insertions(+), 28 deletions(-) diff --git a/nginx.conf b/nginx.conf index 80aafacf7..953d552d3 100644 --- a/nginx.conf +++ b/nginx.conf @@ -40,24 +40,107 @@ server { access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; - # Static file caching for the "static" directory - location ~ ^/static/(.*)$ { + # Static file handling with proper MIME types and consistent caching + location ~* ^/static/(.*)$ { proxy_pass http://127.0.0.1:3000; + + # Include MIME types + include /etc/nginx/mime.types; + + # Cache configuration for static files proxy_cache STATIC; - proxy_ignore_headers Cache-Control; - proxy_cache_valid 200 302 60m; + proxy_cache_valid 200 302 24h; # Cache successful responses for 24 hours + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; + proxy_cache_lock on; + + # Show cache status in response headers + add_header X-Cache-Status $upstream_cache_status; + + # Standard proxy headers + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Default cache policy for static files + add_header Cache-Control "public, max-age=86400"; # 24 hours + } + + # Binary files caching + location ~* \.(bin|dat|exe|dll|so|dylib)$ { + proxy_pass http://127.0.0.1:3000; + add_header Cache-Control "public, max-age=31536000, immutable"; # 1 year for binary files + + proxy_cache STATIC; + proxy_cache_valid 200 302 24h; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; proxy_cache_lock on; add_header X-Cache-Status $upstream_cache_status; - # Cache bypass if needed (for development/debugging) - proxy_cache_bypass $http_pragma $http_authorization; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Specific file type caching rules (outside the /static/ location) + location ~* \.js$ { + proxy_pass http://127.0.0.1:3000; + add_header Content-Type application/javascript; + add_header Cache-Control "public, max-age=31536000, immutable"; # 1 year for JS files - # No TTL as requested (browser will check each time) - expires -1; - add_header Cache-Control "no-cache"; + proxy_cache STATIC; + proxy_cache_valid 200 302 24h; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; + proxy_cache_lock on; + add_header X-Cache-Status $upstream_cache_status; - # Standard proxy headers + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location ~* \.css$ { + proxy_pass http://127.0.0.1:3000; + add_header Content-Type text/css; + add_header Cache-Control "public, max-age=31536000, immutable"; # 1 year for CSS files + + proxy_cache STATIC; + proxy_cache_valid 200 302 24h; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; + proxy_cache_lock on; + add_header X-Cache-Status $upstream_cache_status; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location ~* \.html$ { + proxy_pass http://127.0.0.1:3000; + add_header Content-Type text/html; + add_header Cache-Control "public, max-age=86400"; # 24 hours for HTML files + + proxy_cache STATIC; + proxy_cache_valid 200 302 24h; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; + proxy_cache_lock on; + add_header X-Cache-Status $upstream_cache_status; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Root location - make sure index.html is the default + location = / { + proxy_pass http://127.0.0.1:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/src/server/Master.ts b/src/server/Master.ts index 6df862a29..fc05a3144 100644 --- a/src/server/Master.ts +++ b/src/server/Master.ts @@ -23,8 +23,25 @@ const server = http.createServer(app); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); app.use(express.json()); -// Serve static files from the 'out' directory -app.use(express.static(path.join(__dirname, "../../static"))); +app.use( + express.static(path.join(__dirname, "../../static"), { + maxAge: "1y", // Set max-age to 1 year for all static assets + setHeaders: (res, path) => { + // You can conditionally set different cache times based on file types + if (path.endsWith(".html")) { + // HTML files get shorter cache time + res.setHeader("Cache-Control", "public, max-age=60"); + } else if (path.match(/\.(js|css|svg)$/)) { + // JS, CSS, SVG get long cache with immutable + res.setHeader("Cache-Control", "public, max-age=31536000, immutable"); + } else if (path.match(/\.(bin|dat|exe|dll|so|dylib)$/)) { + // Binary files also get long cache with immutable + res.setHeader("Cache-Control", "public, max-age=31536000, immutable"); + } + // Other file types use the default maxAge setting + }, + }), +); app.use(express.json()); app.set("trust proxy", 3); diff --git a/webpack.config.js b/webpack.config.js index 1e77454e8..9555bbcbb 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -14,7 +14,7 @@ export default (env, argv) => { entry: "./src/client/Main.ts", output: { publicPath: "/", - filename: "static/js/bundle.js", + filename: "js/[name].[contenthash].js", // Added content hash path: path.resolve(__dirname, "static"), clean: true, }, @@ -22,11 +22,17 @@ export default (env, argv) => { rules: [ { test: /\.bin$/, - use: "raw-loader", + type: "asset/resource", // Changed from raw-loader + generator: { + filename: "binary/[name].[contenthash][ext]", // Added content hash + }, }, { test: /\.txt$/, - use: "raw-loader", + type: "asset/resource", // Changed from raw-loader + generator: { + filename: "text/[name].[contenthash][ext]", // Added content hash + }, }, { test: /\.ts$/, @@ -57,7 +63,7 @@ export default (env, argv) => { test: /\.(png|jpe?g|gif)$/i, type: "asset/resource", generator: { - filename: "static/images/[name]-[hash:8][ext]", + filename: "images/[name].[contenthash][ext]", // Added content hash }, }, { @@ -66,20 +72,17 @@ export default (env, argv) => { }, { test: /\.svg$/, - type: "asset/inline", + type: "asset/resource", // Changed from asset/inline for caching + generator: { + filename: "images/[name].[contenthash][ext]", // Added content hash + }, }, { test: /\.(woff|woff2|eot|ttf|otf)$/, - use: [ - { - loader: "file-loader", - options: { - name: "[name].[ext]", - outputPath: "static/fonts/", - publicPath: "../fonts/", - }, - }, - ], + type: "asset/resource", // Changed from file-loader + generator: { + filename: "fonts/[name].[contenthash][ext]", // Added content hash and fixed path + }, }, ], }, @@ -96,6 +99,17 @@ export default (env, argv) => { new HtmlWebpackPlugin({ template: "./src/client/index.html", filename: "index.html", + // Add optimization for HTML + minify: isProduction + ? { + collapseWhitespace: true, + removeComments: true, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, + useShortDoctype: true, + } + : false, }), new webpack.DefinePlugin({ "process.env.WEBSOCKET_URL": JSON.stringify( @@ -107,13 +121,35 @@ export default (env, argv) => { }), new CopyPlugin({ patterns: [ - { from: "resources", to: path.resolve(__dirname, "static") }, + { + from: "resources", + to: ".", // Copy to the output directory (static) + // Add content hashing to copied files + transform: function (content, path) { + return content; // Return unmodified content + }, + // Don't hash HTML files from resources + noErrorOnMissing: true, + }, ], options: { concurrency: 100, }, }), ], + optimization: { + // Add optimization configuration for better caching + runtimeChunk: "single", + splitChunks: { + cacheGroups: { + vendor: { + test: /[\\/]node_modules[\\/]/, + name: "vendors", + chunks: "all", + }, + }, + }, + }, devServer: isProduction ? {} : {