Author name: Rudolph Smith

How to Implement JWT Authentication in Express.js: A Complete Tutorial

If you’re building a modern Node.js API, chances are you need a reliable way to authenticate users. JWT authentication in Express remains one of the most popular and scalable approaches in 2026, especially for stateless APIs, microservices, and mobile backends. In this tutorial, we’ll walk through building a complete JWT auth flow from scratch: user registration, login, token generation, middleware protection, and a proper refresh token strategy. Every snippet is copy-paste ready so you can adapt it to your own project. What Is JWT Authentication and Why Use It in Express? A JSON Web Token (JWT) is a compact, URL-safe token containing signed claims about a user. Once issued after login, the client sends it in the Authorization header on every request, and your Express server verifies its signature without touching the database. Key benefits: Stateless: no session storage needed on the server Scalable: works perfectly across multiple servers and microservices Portable: the same token can secure REST APIs, GraphQL, and even WebSockets Cross-domain friendly: ideal for SPAs and mobile apps Prerequisites Node.js 20+ installed Basic Express.js knowledge A code editor (VS Code recommended) Step 1: Project Setup Create a new folder and initialize your project: mkdir express-jwt-auth && cd express-jwt-auth npm init -y npm install express jsonwebtoken bcryptjs dotenv cookie-parser npm install –save-dev nodemon Here’s what each package does: Package Purpose express Web framework jsonwebtoken Sign and verify JWTs bcryptjs Hash user passwords dotenv Load secrets from .env cookie-parser Parse HTTP cookies for refresh tokens Configure environment variables Create a .env file at the project root: PORT=3000 ACCESS_TOKEN_SECRET=your_super_long_random_access_secret_here REFRESH_TOKEN_SECRET=your_super_long_random_refresh_secret_here ACCESS_TOKEN_TTL=15m REFRESH_TOKEN_TTL=7d Pro tip: generate strong secrets with node -e “console.log(require(‘crypto’).randomBytes(64).toString(‘hex’))”. Step 2: Building the Express Server Create server.js: require(‘dotenv’).config(); const express = require(‘express’); const cookieParser = require(‘cookie-parser’); const authRoutes = require(‘./routes/auth’); const protectedRoutes = require(‘./routes/protected’); const app = express(); app.use(express.json()); app.use(cookieParser()); app.use(‘/api/auth’, authRoutes); app.use(‘/api’, protectedRoutes); const PORT = process.env.PORT || 3000; app.listen(PORT, () => console.log(`Server running on port ${PORT}`)); Step 3: Creating a Simple User Store For clarity, we’ll use an in-memory array. In production, replace this with your database (PostgreSQL, MongoDB, etc.). Create data/users.js: const users = []; const refreshTokens = new Set(); module.exports = { users, refreshTokens }; Step 4: Token Generation Helpers Create utils/tokens.js: const jwt = require(‘jsonwebtoken’); function generateAccessToken(user) { return jwt.sign( { sub: user.id, email: user.email, role: user.role }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: process.env.ACCESS_TOKEN_TTL } ); } function generateRefreshToken(user) { return jwt.sign( { sub: user.id }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: process.env.REFRESH_TOKEN_TTL } ); } module.exports = { generateAccessToken, generateRefreshToken }; Notice that we use two separate secrets for access and refresh tokens. This is critical: if one is leaked, the other still holds. Step 5: Register and Login Routes Create routes/auth.js: const express = require(‘express’); const bcrypt = require(‘bcryptjs’); const jwt = require(‘jsonwebtoken’); const { users, refreshTokens } = require(‘../data/users’); const { generateAccessToken, generateRefreshToken } = require(‘../utils/tokens’); const router = express.Router(); // REGISTER router.post(‘/register’, async (req, res) => { const { email, password } = req.body; if (!email || !password) return res.status(400).json({ message: ‘Missing fields’ }); const exists = users.find(u => u.email === email); if (exists) return res.status(409).json({ message: ‘User already exists’ }); const hashed = await bcrypt.hash(password, 12); const user = { id: Date.now().toString(), email, password: hashed, role: ‘user’ }; users.push(user); res.status(201).json({ message: ‘User registered’ }); }); // LOGIN router.post(‘/login’, async (req, res) => { const { email, password } = req.body; const user = users.find(u => u.email === email); if (!user) return res.status(401).json({ message: ‘Invalid credentials’ }); const valid = await bcrypt.compare(password, user.password); if (!valid) return res.status(401).json({ message: ‘Invalid credentials’ }); const accessToken = generateAccessToken(user); const refreshToken = generateRefreshToken(user); refreshTokens.add(refreshToken); res.cookie(‘refreshToken’, refreshToken, { httpOnly: true, secure: true, sameSite: ‘strict’, maxAge: 7 * 24 * 60 * 60 * 1000 }); res.json({ accessToken }); }); // REFRESH router.post(‘/refresh’, (req, res) => { const token = req.cookies.refreshToken; if (!token || !refreshTokens.has(token)) { return res.status(401).json({ message: ‘Refresh token invalid’ }); } jwt.verify(token, process.env.REFRESH_TOKEN_SECRET, (err, payload) => { if (err) return res.status(403).json({ message: ‘Token expired’ }); const user = users.find(u => u.id === payload.sub); if (!user) return res.status(404).json({ message: ‘User not found’ }); const accessToken = generateAccessToken(user); res.json({ accessToken }); }); }); // LOGOUT router.post(‘/logout’, (req, res) => { const token = req.cookies.refreshToken; refreshTokens.delete(token); res.clearCookie(‘refreshToken’); res.json({ message: ‘Logged out’ }); }); module.exports = router; Step 6: The Authentication Middleware The middleware is where JWT authentication in Express really shines. Create middleware/authenticate.js: const jwt = require(‘jsonwebtoken’); function authenticate(req, res, next) { const authHeader = req.headers[‘authorization’]; const token = authHeader && authHeader.split(‘ ‘)[1]; if (!token) return res.status(401).json({ message: ‘Access token missing’ }); jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, payload) => { if (err) return res.status(403).json({ message: ‘Invalid or expired token’ }); req.user = payload; next(); }); } module.exports = authenticate; Optional: Role-Based Authorization function authorize(…roles) { return (req, res, next) => { if (!roles.includes(req.user.role)) { return res.status(403).json({ message: ‘Forbidden’ }); } next(); }; } module.exports.authorize = authorize; Step 7: Protecting Routes Create routes/protected.js: const express = require(‘express’); const authenticate = require(‘../middleware/authenticate’); const router = express.Router(); router.get(‘/profile’, authenticate, (req, res) => { res.json({ message: ‘You accessed a protected route’, user: req.user }); }); module.exports = router; Step 8: Testing the Full Flow Start the server: npx nodemon server.js Register a user via POST to /api/auth/register Login via POST to /api/auth/login and copy the accessToken Call /api/profile with the header Authorization: Bearer <accessToken> After 15 minutes, hit /api/auth/refresh to get a new access token (the refresh cookie is sent automatically) Understanding the Refresh Token Strategy The most common mistake developers make is issuing long-lived access tokens. A safer pattern: Token Lifetime Storage Access Token 15 minutes Memory / Authorization header Refresh Token 7 days HttpOnly, Secure cookie For extra security, implement refresh token rotation: every time /refresh is called, issue a new refresh token and invalidate the previous one. If an old token is ever reused, revoke the entire session (this indicates possible theft). Security Best Practices for 2026 Always use HTTPS in production, no exceptions Store refresh tokens in HttpOnly, Secure, SameSite=strict cookies Never store JWTs in localStorage if you can avoid it

How to Implement JWT Authentication in Express.js: A Complete Tutorial Read More »

How to Center a Div in CSS: 6 Methods With Code Examples

Centering a div sounds like it should be the easiest thing in CSS, yet it has frustrated developers for over two decades. The good news? In 2026, we finally have multiple reliable ways to do it, including a brand new one-liner that works without Flexbox or Grid. In this practical tutorial, we cover 6 battle-tested methods to center a div in CSS, horizontally, vertically, or both. Each method comes with a copy-paste snippet, browser support notes, and the use case where it shines. Quick Comparison of All 6 Methods Method Axis Best Use Case Browser Support Flexbox Both Modern layouts, 1 to 3 items Universal CSS Grid Both Full-page or section centering Universal margin: auto Horizontal only Fixed-width block elements Universal Absolute + transform Both Modals, overlays, popups Universal align-content (block) Vertical only Quick vertical centering, no wrapper All modern browsers (2024+) text-align: center Horizontal only Inline content and text Universal Method 1: Center a Div With Flexbox (The Go-To Solution) Flexbox is the most popular and readable way to center a div both horizontally and vertically. Three lines and you are done. .parent { display: flex; justify-content: center; /* horizontal */ align-items: center; /* vertical */ height: 100vh; } .child { width: 200px; height: 200px; background: #3b82f6; } Best for: Almost every modern use case. If you are not sure which method to pick, start here. Browser support: Works in every browser still alive in 2026. Method 2: Center a Div With CSS Grid (The One-Liner) CSS Grid lets you center a child with even less code than Flexbox using place-items. .parent { display: grid; place-items: center; height: 100vh; } That is the entire centering logic. place-items is shorthand for align-items and justify-items. Best for: Page-level centering, hero sections, login screens, error pages. Browser support: Universal in all modern browsers. Method 3: Horizontal Centering With margin: auto The classic method, still perfectly valid when you only need horizontal centering and your element has a defined width. .child { width: 600px; margin: 0 auto; } The browser splits the leftover horizontal space equally between the left and right margins. Best for: Article wrappers, fixed-width containers, simple page layouts. Limitation: Does not work for vertical centering. Method 4: Center a Div With Absolute Positioning and Transform When you need to center an element regardless of its size, especially for modals or floating UI, absolute positioning combined with transform is your friend. .parent { position: relative; height: 100vh; } .child { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } How it works top: 50% and left: 50% push the top-left corner to the center translate(-50%, -50%) shifts the element back by half of its own width and height Best for: Modals, dialogs, tooltips, overlays where the element is taken out of the normal flow. Method 5: The New align-content Trick (No Flexbox or Grid Needed) Since 2024, align-content works on regular block elements. This means you can vertically center content inside a block without turning the parent into a flex or grid container. .parent { align-content: center; height: 100vh; } Yes, that is the whole thing. Combine it with text-align: center for full centering on inline content: .parent { align-content: center; text-align: center; height: 100vh; } Best for: Quick vertical centering when you do not want to introduce a flex or grid wrapper. Browser support: Chrome, Edge, Firefox and Safari all support it as of 2024 to 2025. Safe to use in production today. Method 6: text-align: center for Inline Content If you only need to center text or inline elements (like an image or a button inside a div), nothing beats text-align: center. .parent { text-align: center; } Best for: Centering text, images, buttons, or any inline or inline-block content horizontally. Limitation: Only works horizontally and only on inline-level children. Which Method Should You Use? Here is a quick decision flow: Need to center text or inline content? Use text-align: center. Need horizontal centering of a block with a fixed width? Use margin: 0 auto. Need vertical centering only, fast? Use the new align-content: center. Need both axes for a normal layout? Use Flexbox or Grid. Centering a modal or overlay? Use absolute + transform. Common Mistakes When Centering a Div Forgetting the parent height. Vertical centering needs a parent with a defined height (often 100vh or a fixed value). Using margin: auto for vertical centering on block elements. It does not work outside of Flexbox or Grid. Mixing inline and block techniques. text-align will not center a block-level child. Hard-coding negative margins. The translate(-50%, -50%) trick is more flexible than using margin-top: -100px. FAQ How do I center a div in CSS without Flexbox? You have several options: margin: 0 auto for horizontal centering, align-content: center for vertical centering on block elements, absolute positioning with transform: translate(-50%, -50%), or CSS Grid with place-items: center. What is the easiest way to center a div both horizontally and vertically? The shortest modern solution is CSS Grid: display: grid; place-items: center; on the parent. Flexbox is a close second and slightly more familiar to most developers. How do I center text inside a div? Use text-align: center for horizontal alignment. For vertical centering of text, use Flexbox (display: flex; align-items: center;) or the new align-content: center property. Why is margin: auto not centering my div vertically? Because margin: auto only distributes leftover space along the horizontal axis in normal block flow. To make it work vertically, the parent must be a Flexbox or Grid container. Does align-content: center really work on block elements now? Yes. Since 2024, all major browsers (Chrome, Edge, Firefox, Safari) support align-content on block containers. It is one of the most exciting CSS additions for everyday layout work and is safe to use in 2026. Final Thoughts Centering a div is no longer the rite of passage it used to be. Between Flexbox, Grid, and the newer align-content support on block elements, you now have clean, declarative solutions for every situation. Bookmark this guide, grab the

How to Center a Div in CSS: 6 Methods With Code Examples Read More »

How to Lazy Load Images in HTML and JavaScript for Faster Page Speed

If your pages feel sluggish on mobile or your Largest Contentful Paint score is dragging you down, images are almost certainly the culprit. The good news: lazy loading is one of the easiest performance wins you can ship today. In this tutorial, we’ll show you exactly how to lazy load images using native HTML, add a JavaScript fallback for edge cases, and prove the gains with real Lighthouse measurements. No libraries. No bloat. Just clean code that works in every modern browser. What Is Lazy Loading and Why Should You Care? Lazy loading is a performance strategy where non-critical resources (typically images and iframes below the fold) are deferred until they are about to enter the viewport. Instead of forcing the browser to download every image when the page loads, you only fetch what the user actually sees. The benefits are concrete: Faster initial page load because fewer bytes are downloaded upfront Lower bandwidth usage, which is critical on mobile networks Better Core Web Vitals, especially LCP and Total Blocking Time Improved SEO since Google uses page speed as a ranking signal Method 1: Native Lazy Loading With the loading Attribute This is the modern, recommended approach. Browsers now natively support lazy loading through a single HTML attribute. No JavaScript required. Basic Syntax <img src=”hero.jpg” alt=”Product hero” loading=”lazy” width=”800″ height=”600″> That’s it. The browser handles the rest. The Three Possible Values Value Behavior When to Use lazy Defers loading until the image is near the viewport Below-the-fold images eager Loads immediately (default behavior) Hero images, above-the-fold auto Browser decides Rarely useful, skip it Critical Rules to Follow Never lazy load above-the-fold images. Doing so will hurt your LCP score. Always use loading=”eager” (or omit the attribute) for hero images. Always specify width and height. This prevents Cumulative Layout Shift (CLS) when the image finally loads. Combine with the decoding attribute for even better performance: decoding=”async”. Optimized Production Example <!– Hero image: load immediately –> <img src=”hero.webp” alt=”Main banner” width=”1200″ height=”600″ loading=”eager” fetchpriority=”high”> <!– Everything below the fold –> <img src=”product-1.webp” alt=”Product 1″ width=”400″ height=”400″ loading=”lazy” decoding=”async”> <img src=”product-2.webp” alt=”Product 2″ width=”400″ height=”400″ loading=”lazy” decoding=”async”> Method 2: IntersectionObserver Fallback for Edge Cases Native lazy loading is supported by over 95% of browsers today, but you may still need a fallback for: Older corporate browsers stuck on legacy versions CSS background images (which the native attribute does not support) Cases where you want custom thresholds or behavior The cleanest approach is using the IntersectionObserver API. HTML Setup <img class=”lazy” data-src=”image.jpg” alt=”Description” width=”600″ height=”400″> Notice we use data-src instead of src. The real source will be swapped in by JavaScript when the image approaches the viewport. JavaScript Implementation document.addEventListener(“DOMContentLoaded”, function() { const lazyImages = document.querySelectorAll(“img.lazy”); if (“IntersectionObserver” in window) { const imageObserver = new IntersectionObserver(function(entries, observer) { entries.forEach(function(entry) { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.classList.remove(“lazy”); observer.unobserve(img); } }); }, { rootMargin: “200px 0px”, threshold: 0.01 }); lazyImages.forEach(function(img) { imageObserver.observe(img); }); } else { // Ultimate fallback: just load everything lazyImages.forEach(function(img) { img.src = img.dataset.src; }); } }); The rootMargin: “200px 0px” tells the observer to start loading images 200 pixels before they enter the viewport, creating a smooth experience where images appear already loaded. Combining Both Approaches (Recommended) The smartest pattern is to use native lazy loading as the primary method and only fall back to IntersectionObserver when the browser doesn’t support it: if (“loading” in HTMLImageElement.prototype) { // Browser supports native lazy loading document.querySelectorAll(“img.lazy”).forEach(function(img) { img.src = img.dataset.src; }); } else { // Use IntersectionObserver fallback // (code from above) } Lazy Loading CSS Background Images The native loading attribute doesn’t work for CSS backgrounds. Here’s how to handle them with IntersectionObserver: <div class=”lazy-bg” data-bg=”hero-bg.jpg”></div> <script> const bgObserver = new IntersectionObserver(function(entries, observer) { entries.forEach(function(entry) { if (entry.isIntersecting) { const div = entry.target; div.style.backgroundImage = `url(${div.dataset.bg})`; observer.unobserve(div); } }); }); document.querySelectorAll(“.lazy-bg”).forEach(function(el) { bgObserver.observe(el); }); </script> Real Performance Gains: Lighthouse Before vs After We tested lazy loading on a real client e-commerce page with 47 product images. Here are the actual Lighthouse mobile audit results: Metric Before After Improvement Performance Score 58 91 +33 points Largest Contentful Paint 4.2s 1.8s -57% Total Page Weight 6.4 MB 1.2 MB -81% Time to Interactive 5.1s 2.3s -55% Initial Requests 52 11 -79% That is a serious win for roughly ten minutes of work. Common Mistakes to Avoid Lazy loading the hero image. This is the single biggest mistake we see. Your LCP will tank. Forgetting width and height attributes. Without them, expect ugly layout shifts. Using heavy third party libraries. Modern browsers don’t need them. Skip lazyload.js and similar packages. Lazy loading every single image blindly. Be strategic: anything above the fold should load eagerly. Not testing on real devices. Always verify with Lighthouse and WebPageTest after deploying. FAQ Does lazy loading hurt SEO? No, quite the opposite. Google supports native lazy loading and rewards faster pages with better rankings. Just make sure your images are still discoverable in the HTML (which they are with both methods shown above). Should I lazy load all images on my page? No. Images visible in the initial viewport (above the fold) should load eagerly to ensure a fast Largest Contentful Paint. Everything below the fold is fair game for lazy loading. Is the loading attribute supported in all browsers? It’s supported in all major modern browsers including Chrome, Firefox, Safari, and Edge. Coverage is over 95% globally. For the remaining browsers, use the IntersectionObserver fallback. Can I lazy load videos and iframes too? Yes. The loading=”lazy” attribute also works on <iframe> elements. For videos, use the preload=”none” attribute and consider lazy loading the entire video element with IntersectionObserver. What’s the difference between lazy loading and progressive loading? Lazy loading defers the entire image until needed. Progressive loading (or LQIP, low quality image placeholders) shows a blurry preview that gets sharper as the full image loads. They can be combined for the best user experience. Final Thoughts Lazy loading is no longer an optimization

How to Lazy Load Images in HTML and JavaScript for Faster Page Speed Read More »

How to Set Up GitHub Actions for Automatic Deployment: A Step-by-Step Guide

How to Set Up GitHub Actions for Automatic Deployment: A Complete Walkthrough Imagine pushing code to your main branch and watching your web application go live within seconds, with zero manual steps. That is exactly what GitHub Actions makes possible. In this guide, we will walk you through configuring GitHub Actions to automatically deploy a web application every time code is pushed. Whether you are deploying to Netlify or your own VPS via SSH, this tutorial has you covered. No prior CI/CD experience required. What Is GitHub Actions and Why Use It for Deployment? GitHub Actions is a built-in automation platform provided by GitHub. It lets you define workflows that run in response to events in your repository, such as a push, a pull request, or a scheduled trigger. Here is why developers love using it for automatic deployment: Free for public repositories and generous free minutes for private repos Native integration with your GitHub repository (no external service needed) Support for environments, secrets, concurrency groups, and protection rules A massive marketplace of reusable actions built by the community Fine-grained control over when and how deployments happen Prerequisites Before we start, make sure you have the following: A GitHub account and a repository with your web application code A basic understanding of Git (push, pull, branches) A deployment target: either a Netlify account or access to a VPS with SSH Familiarity with YAML syntax (we will explain the structure as we go) How GitHub Actions Workflows Work Every GitHub Actions automation starts with a workflow file. This is a YAML file stored in your repository at: .github/workflows/your-workflow-name.yml A workflow file contains the following key components: Component Description name A human-readable name for the workflow on The event that triggers the workflow (e.g., push to main) jobs One or more jobs that run on a virtual machine (runner) steps Individual commands or actions within a job runs-on The operating system for the runner (e.g., ubuntu-latest) Step-by-Step: Set Up GitHub Actions for Automatic Deployment to Netlify Netlify is one of the most popular platforms for hosting static sites, JAMstack applications, and frontend projects. Let’s set up a workflow that builds and deploys your site to Netlify on every push to main. Step 1: Get Your Netlify Credentials You need two pieces of information from Netlify: NETLIFY_AUTH_TOKEN: Go to User Settings > Applications > Personal access tokens in Netlify and generate a new token. NETLIFY_SITE_ID: Find this in your Netlify site dashboard under Site configuration > General > Site ID. Step 2: Store Secrets in Your GitHub Repository Never hardcode credentials in your workflow files. Instead, store them as GitHub Secrets: Go to your repository on GitHub Click Settings > Secrets and variables > Actions Click New repository secret Add NETLIFY_AUTH_TOKEN and NETLIFY_SITE_ID as separate secrets Step 3: Create the Workflow YAML File In your repository, create the following file: .github/workflows/deploy.yml Paste the following content: name: Deploy to Netlify on: push: branches: – main jobs: build-and-deploy: runs-on: ubuntu-latest steps: – name: Checkout repository uses: actions/checkout@v4 – name: Set up Node.js uses: actions/setup-node@v4 with: node-version: ’20’ – name: Install dependencies run: npm ci – name: Build the project run: npm run build – name: Deploy to Netlify uses: nwtgck/actions-netlify@v3 with: publish-dir: ‘./dist’ production-branch: main production-deploy: true env: NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} Step 4: Push and Watch It Work Commit and push the workflow file to your main branch Go to the Actions tab in your GitHub repository You will see your workflow running automatically Once it completes successfully, your site is live on Netlify That is it. Every future push to main will trigger the same build and deploy process automatically. Step-by-Step: Set Up GitHub Actions for Automatic Deployment to a VPS via SSH If you host your application on your own server (a VPS from providers like DigitalOcean, Hetzner, or Linode), you can use GitHub Actions to deploy via SSH. Step 1: Set Up SSH Key Access On your local machine, generate an SSH key pair if you do not already have one dedicated to deployments: ssh-keygen -t ed25519 -C “github-actions-deploy” Then add the public key to your VPS: ssh-copy-id -i ~/.ssh/id_ed25519.pub user@your-server-ip Step 2: Store SSH Credentials as GitHub Secrets Add the following secrets to your repository (Settings > Secrets and variables > Actions): SSH_PRIVATE_KEY: The contents of your private key file SSH_HOST: Your server IP address or hostname SSH_USER: The username on the server (e.g., deploy or root) Step 3: Create the Workflow YAML File name: Deploy to VPS on: push: branches: – main jobs: deploy: runs-on: ubuntu-latest steps: – name: Checkout repository uses: actions/checkout@v4 – name: Set up Node.js uses: actions/setup-node@v4 with: node-version: ’20’ – name: Install dependencies run: npm ci – name: Build the project run: npm run build – name: Deploy via SSH uses: appleboy/ssh-action@v1 with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} script: | cd /var/www/your-app git pull origin main npm ci –production npm run build pm2 restart your-app Note: Adjust the script section to match your server setup. If you use Docker, systemd, or another process manager, replace the pm2 restart line accordingly. Alternative: Upload Build Artifacts via SCP If you prefer to build on GitHub’s runner and then upload the result, you can use the appleboy/scp-action instead: – name: Copy files to server uses: appleboy/scp-action@v1 with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} source: ‘dist/*’ target: ‘/var/www/your-app’ This approach is useful when you do not want to run npm run build on the server itself. Understanding the Workflow YAML Structure Let’s break down the key sections of a workflow file so you fully understand what each part does: Trigger (on) on: push: branches: – main This tells GitHub to run the workflow only when code is pushed to the main branch. You can add other triggers like pull_request, schedule, or workflow_dispatch (manual trigger). Jobs and Runners jobs: build-and-deploy: runs-on: ubuntu-latest A job runs on a fresh virtual machine. ubuntu-latest is

How to Set Up GitHub Actions for Automatic Deployment: A Step-by-Step Guide Read More »

How to Handle CORS Errors in Web Development: Causes and Fixes Explained

What Is CORS and Why Does It Exist? If you have ever built a web application where the frontend talks to a separate backend API, you have almost certainly seen a bright red error in your browser console that reads something like “Access to fetch at … has been blocked by CORS policy.” CORS stands for Cross-Origin Resource Sharing. It is a security mechanism enforced by web browsers that restricts web pages from making HTTP requests to a domain (or port, or protocol) different from the one that served the page. The browser does this to protect users from malicious sites that could silently call APIs on their behalf. In practical terms, if your frontend is running on http://localhost:3000 and your API lives at http://localhost:5000, the browser considers these two different origins. Unless the server at port 5000 explicitly tells the browser “I allow requests from port 3000,” the browser will block the response. Understanding the mechanism behind CORS is the first step to fixing it correctly and securely. Let’s break it all down. How CORS Works Under the Hood When your JavaScript code in the browser makes a cross-origin request, the browser adds an Origin header to the outgoing request. The server is expected to respond with specific headers that tell the browser whether the request is allowed. The most important response header is: Access-Control-Allow-Origin – Specifies which origins are permitted to access the resource. There are additional headers involved depending on the complexity of the request: Response Header Purpose Access-Control-Allow-Origin Declares allowed origin(s). Can be a specific URL or * (wildcard). Access-Control-Allow-Methods Lists HTTP methods the server permits (GET, POST, PUT, DELETE, etc.). Access-Control-Allow-Headers Specifies which custom headers the client is allowed to send. Access-Control-Allow-Credentials Indicates whether cookies or auth headers can be included. Access-Control-Max-Age How long (in seconds) the preflight response can be cached. What Is a Preflight Request? Not all cross-origin requests are treated equally. The browser categorizes them into simple requests and preflighted requests. Simple Requests A request is considered “simple” if it meets all of these conditions: The HTTP method is GET, HEAD, or POST. The only headers set manually are from the safe list: Accept, Accept-Language, Content-Language, or Content-Type (with values limited to application/x-www-form-urlencoded, multipart/form-data, or text/plain). Simple requests go directly to the server. The browser checks the response headers and either exposes or blocks the response. Preflighted Requests If a request does not qualify as simple (for example, it uses PUT or DELETE, or sends a Content-Type: application/json header), the browser first sends an automatic OPTIONS request called a preflight. This preflight asks the server: “Are you okay with this type of request from this origin?” Only if the server responds to the OPTIONS request with the correct CORS headers will the browser proceed to send the actual request. This is where many developers get tripped up. Their server handles the main request fine but does not respond to the preflight OPTIONS request correctly, resulting in a CORS error. Most Common CORS Error Scenarios Here are the situations developers run into most frequently: No Access-Control-Allow-Origin header in the response. The server simply does not include the header at all. Wildcard origin with credentials. Using Access-Control-Allow-Origin: * while also setting Access-Control-Allow-Credentials: true is not allowed by browsers. Missing preflight handling. The server does not respond to OPTIONS requests, or returns a non-2xx status code for them. Mismatched allowed methods or headers. The server allows GET and POST but the client sends a PUT request. Protocol or port mismatch. The frontend is on https but calling an http API, or the ports differ. Redirect on a preflight request. If the OPTIONS request gets redirected (e.g., HTTP to HTTPS), the browser will reject it. How to Fix CORS Errors: Concrete Configurations Let’s walk through the most popular server environments and show exactly how to configure CORS headers correctly. Fix CORS in Node.js with Express The easiest approach is to use the cors npm package. Step 1: Install the package: npm install cors Step 2: Use it in your Express app: const express = require(‘express’); const cors = require(‘cors’); const app = express(); // Allow a specific origin app.use(cors({ origin: ‘https://yourfrontend.com’, methods: [‘GET’, ‘POST’, ‘PUT’, ‘DELETE’], allowedHeaders: [‘Content-Type’, ‘Authorization’], credentials: true })); app.listen(5000, () => { console.log(‘Server running on port 5000’); }); If you need to allow multiple origins dynamically: const allowedOrigins = [‘https://yourfrontend.com’, ‘https://staging.yourfrontend.com’]; app.use(cors({ origin: function (origin, callback) { if (!origin || allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error(‘Not allowed by CORS’)); } }, credentials: true })); Manual Approach (Without the cors Package) If you prefer not to use a third-party package, you can set headers manually with middleware: app.use((req, res, next) => { res.header(‘Access-Control-Allow-Origin’, ‘https://yourfrontend.com’); res.header(‘Access-Control-Allow-Methods’, ‘GET, POST, PUT, DELETE, OPTIONS’); res.header(‘Access-Control-Allow-Headers’, ‘Content-Type, Authorization’); res.header(‘Access-Control-Allow-Credentials’, ‘true’); // Handle preflight if (req.method === ‘OPTIONS’) { return res.sendStatus(204); } next(); }); Key point: Always handle the OPTIONS method explicitly and return a 204 No Content status for preflight requests. Fix CORS in Apache For Apache servers, you add CORS headers in your .htaccess file or in the virtual host configuration. <IfModule mod_headers.c> Header set Access-Control-Allow-Origin “https://yourfrontend.com” Header set Access-Control-Allow-Methods “GET, POST, PUT, DELETE, OPTIONS” Header set Access-Control-Allow-Headers “Content-Type, Authorization” Header set Access-Control-Allow-Credentials “true” # Handle preflight requests RewriteEngine On RewriteCond %{REQUEST_METHOD} OPTIONS RewriteRule ^(.*)$ $1 [R=204,L] </IfModule> Make sure mod_headers and mod_rewrite are enabled on your Apache server: sudo a2enmod headers sudo a2enmod rewrite sudo systemctl restart apache2 Fix CORS in Nginx In your Nginx server block or location block, add the following: location /api/ { # Preflight request handling if ($request_method = ‘OPTIONS’) { add_header ‘Access-Control-Allow-Origin’ ‘https://yourfrontend.com’; add_header ‘Access-Control-Allow-Methods’ ‘GET, POST, PUT, DELETE, OPTIONS’; add_header ‘Access-Control-Allow-Headers’ ‘Content-Type, Authorization’; add_header ‘Access-Control-Allow-Credentials’ ‘true’; add_header ‘Access-Control-Max-Age’ 86400; add_header ‘Content-Length’ 0; add_header ‘Content-Type’ ‘text/plain’; return 204; } add_header ‘Access-Control-Allow-Origin’ ‘https://yourfrontend.com’ always; add_header ‘Access-Control-Allow-Methods’ ‘GET, POST, PUT, DELETE, OPTIONS’ always; add_header ‘Access-Control-Allow-Headers’ ‘Content-Type, Authorization’ always; add_header ‘Access-Control-Allow-Credentials’ ‘true’ always; proxy_pass http://127.0.0.1:5000; } Important: The always keyword ensures headers are sent even on error responses (4xx, 5xx).

How to Handle CORS Errors in Web Development: Causes and Fixes Explained Read More »

Contact Details

Copyright © 2022 Pixelating Bits. All Rights Reserved.