Improve SEO in nginx.conf, index.html; update favicon and logos; embedded card now supported; Dockerfile slimmed and modernized
All checks were successful
/ image_build (push) Successful in 1m14s

This commit is contained in:
2026-09-25 12:37:01 -04:00
parent 1c78e959f6
commit 75d05beb7a
13 changed files with 1076 additions and 246 deletions

View File

@@ -1,4 +1,6 @@
.idea
build
node_modules
.env*
.env*
.github
.git

33
.editorconfig Normal file
View File

@@ -0,0 +1,33 @@
# top-most EditorConfig file
root = true
# Universal settings for all file types
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length = 120
tab_width = 2
# React & TypeScript
[*.{ts,tsx,js,jsx}]
quote_type = single
# Sass & CSS
[*.{sass,scss,css}]
insert_final_newline = true
# Webpack configs, Node scripts, and Data files
[*.{json,jsonc,yml,yaml,cjs,mjs}]
indent_size = 2
# HTML templates
[*.{htm,html}]
indent_size = 2
# Markdown overrides
[*.{md,markdown,mdc}]
trim_trailing_whitespace = false

View File

@@ -1,21 +1,24 @@
FROM node:latest AS build
FROM docker.io/node:alpine AS build
WORKDIR /blog
COPY package.json /blog/
RUN npm install
COPY . /blog/
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM docker.io/nginx:trixie
RUN useradd tymur999 -u 3000
RUN chown tymur999:tymur999 -R /etc/logrotate.d/nginx \
/etc/nginx \
/etc/init.d/nginx \
/usr/lib/nginx \
/usr/share/nginx \
/var/cache/nginx \
/var/log/nginx
FROM docker.io/nginx:alpine-slim
RUN addgroup -g 3000 tymur999 && \
adduser -u 3000 -G tymur999 -S -D tymur999 && \
mkdir -p /var/www/html && \
chown -R tymur999:tymur999 /var/cache/nginx /var/log/nginx /etc/nginx/conf.d /var/www/html && \
touch /tmp/nginx.pid && \
chown tymur999:tymur999 /tmp/nginx.pid
USER tymur999:tymur999
COPY --from=build --chown=tymur999:tymur999 /blog/build/ /var/www/html/
COPY --link nginx.conf /etc/nginx/
EXPOSE 8000
COPY --chown=tymur999:tymur999 nginx.conf /etc/nginx/nginx.conf
EXPOSE 8000

View File

@@ -8,75 +8,56 @@ events {
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
# Prevent nginx HTTP Server Detection
server_tokens off;
keepalive_timeout 65;
# Enable compression on all routes
gzip on;
gzip_vary on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_proxied any;
gzip_types application/atom+xml text/javascript application/javascript application/json application/manifest+json application/xml font/opentype image/svg+xml text/css text/plain;
# Core Security Headers (Applies to all routes unless overridden)
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Robots-Tag "index, follow, max-image-preview:large, max-snippet:-1" always;
add_header X-XSS-Protection "1; mode=block" always;
server {
server_name tymur999.com;
listen 8000;
# set max upload size and increase upload timeout:
client_max_body_size 10G;
client_body_timeout 300s;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto https;
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml text/javascript application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
# HTTP response headers borrowed from Nextcloud `.htaccess`
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "noindex, nofollow" always;
add_header X-XSS-Protection "1; mode=block" always;
# Path to the root of your installation
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ /index.html =404;
# Never cache the entry point so users always get the latest bundle pointers
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# Static Assets, WebGL/3D Models, and Fonts
location ~* \.(?:css|js|mjs|svg|gif|ico|jpg|png|webp|wasm|tflite|map|ogg|flac|gltf|glb|otf|ttf|woff2?)$ {
try_files $uri =404; # Fail fast on missing assets, do not serve index.html
access_log off;
# Aggressive caching for hashed bundle assets
add_header Cache-Control "public, max-age=31536000, immutable";
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# Serve static files
location ~ \.(?:css|js|mjs|svg|gif|ico|jpg|png|webp|wasm|tflite|map|ogg|flac)$ {
try_files $uri @index;
add_header Cache-Control "public, max-age=15778463";
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "noindex, nofollow" always;
add_header X-XSS-Protection "1; mode=block" always;
}
location ~ \.(otf|woff2?)$ {
try_files $uri @index;
expires 7d; # Cache-Control policy borrowed from `.htaccess`
}
location / {
try_files $uri @index;
}
location @index {
add_header Cache-Control no-cache;
expires 0;
try_files /index.html =404;
}
}
}
}

1087
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -57,19 +57,23 @@
"babel-loader": "^10.0.0",
"babel-preset-react-app": "^10.1.0",
"browserslist": "^4.28.1",
"copy-webpack-plugin": "^14.0.0",
"css-loader": "^7.1.4",
"css-minimizer-webpack-plugin": "^8.0.0",
"file-loader": "^6.2.0",
"fork-ts-checker-webpack-plugin": "^9.1.0",
"html-webpack-plugin": "^5.6.6",
"image-minimizer-webpack-plugin": "^5.0.0",
"mini-css-extract-plugin": "^2.10.0",
"postcss": "^8.5.8",
"postcss-loader": "^8.2.1",
"sass": "^1.97.3",
"sass-loader": "^16.0.7",
"sass-migrator": "^2.5.7",
"sharp": "^0.35.4",
"source-map-loader": "^5.0.0",
"style-loader": "^4.0.0",
"svgo": "^4.1.0",
"terser-webpack-plugin": "^5.3.17",
"ts-loader": "^9.5.4",
"ts-node": "^10.9.2",

View File

@@ -4,13 +4,31 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Tymur Arsentiev's portfolio and blog site"
/>
<!-- Core SEO -->
<title>Tymur Arsentiev - Blog & Portfolio</title>
<meta name="description" content="Tymur Arsentiev's portfolio and blog site" />
<meta name="author" content="Tymur Arsentiev" />
<link rel="canonical" href="https://tymur999.com/" />
<!-- Open Graph / Social Sharing (Facebook, LinkedIn, Discord, etc.) -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://tymur999.com/" />
<meta property="og:title" content="Tymur Arsentiev - Portfolio & Blog" />
<meta property="og:description" content="Tymur Arsentiev's portfolio and blog site" />
<meta property="og:image" content="https://tymur999.com/logo192.png" />
<!-- Twitter Cards -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:url" content="https://tymur999.com/" />
<meta name="twitter:title" content="Tymur Arsentiev - Portfolio & Blog" />
<meta name="twitter:description" content="Tymur Arsentiev's portfolio and blog site" />
<meta name="twitter:image" content="https://tymur999.com/logo192.png" />
<!-- Existing App Manifest & Icons -->
<link rel="icon" href="/logo512.png" />
<link rel="apple-touch-icon" href="/logo192.png" />
<link rel="manifest" href="/manifest.json" />
<title>tymur999.com</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 KiB

After

Width:  |  Height:  |  Size: 230 KiB

View File

@@ -1,6 +1,6 @@
{
"short_name": "tymur999.com",
"name": "Tymur Arsentiev's blog",
"name": "Tymur Arsentiev's blog and portfolio",
"icons": [
{
"src": "favicon.ico",

View File

@@ -1,3 +1,2 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
Allow: /

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -7,6 +7,8 @@ import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import TerserPlugin from "terser-webpack-plugin";
import {Options as MDXOptions} from '@mdx-js/loader';
import "webpack-dev-server";
import CopyWebpackPlugin from "copy-webpack-plugin";
import ImageMinimizerPlugin, {type CustomOptions, Minimizer} from "image-minimizer-webpack-plugin";
const isProduction = process.env.NODE_ENV === "production";
@@ -15,7 +17,21 @@ const config: webpack.Configuration = {
entry: "./src/index.tsx",
optimization: {
minimize: isProduction,
minimizer: [new TerserPlugin()]
minimizer: [
new TerserPlugin(),
// use Sharp lossless image compression
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.sharpMinify,
options: {
encodeOptions: {
jpeg: { quality: 80 },
png: { compressionLevel: 8 }
}
},
} as Minimizer<CustomOptions>
})
]
},
output: {
path: path.resolve(import.meta.dirname, "build"),
@@ -51,9 +67,13 @@ const config: webpack.Configuration = {
loader: "@svgr/webpack",
options: {
prettier: false,
svgo: false,
svgo: true,
svgoConfig: {
plugins: [{ removeViewBox: false }],
multipass: true,
plugins: [
'preset-default',
'prefixIds'
],
},
titleProp: true,
ref: true,
@@ -140,7 +160,18 @@ const config: webpack.Configuration = {
new HtmlWebpackPlugin({
template: path.resolve(import.meta.dirname, "public/index.html"),
favicon: path.resolve(import.meta.dirname, "src/img/favicon.ico"),
inject: true,
inject: true
}),
// copy public files to build dir
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
to: '.',
// html file is handled by HtmlWebpackPlugin
globOptions: { ignore: ['**/index.html'] }
}
]
}),
new webpack.DefinePlugin({
"process.env": JSON.stringify(process.env),
@@ -165,4 +196,4 @@ const config: webpack.Configuration = {
},
};
export default config;
export default config;