Express Admin Panel Routes Not Working on cPanel with Passenger (React App Works Fine)
14:14 28 Nov 2025

Problem Summary

I have a Node.js monorepo with both an EJS-based admin panel and a React frontend. The app works perfectly on localhost, but when deployed to cPanel with Passenger, only the React app works—the admin panel routes return 404 or don't load.

Environment

  • Server: cPanel with CloudLinux Passenger

  • Node.js Version: 22

  • Stack: Express.js, EJS (admin), React (frontend)

  • Deployment: Passenger with PassengerBaseURI "/lwbl"

Current Configuration

.htaccess (auto-generated by cPanel)

# DO NOT REMOVE. CLOUDLINUX PASSENGER CONFIGURATION BEGIN
PassengerAppRoot "/home/devdusjq/public_html/lwbl"
PassengerBaseURI "/lwbl"
PassengerNodejs "/home/devdusjq/nodevenv/public_html/lwbl/22/bin/node"
PassengerAppType node
PassengerStartupFile index.js
# DO NOT REMOVE. CLOUDLINUX PASSENGER CONFIGURATION END

Express Server (index.js)

const express = require("express");
const app = express();
const path = require("path");
const cookieParser = require("cookie-parser");
const authValidator = require("./middleware/authValidator");
require("dotenv").config();
require("./db");

// Middleware
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(cookieParser());

// Serve uploads folder
app.use("/uploads", express.static(path.join(__dirname, "uploads")));

// Serve admin assets (CSS, JS, images)
app.use(
  "/admin/assets",
  express.static(path.join(__dirname, "views/admin/assets"))
);

// Set view engine for EJS
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views/admin"));

// API routes for React (NO AUTH on /api)
app.use("/api", require("./controller/apiHandler"));

// Apply validator ONLY to admin routes
app.use("/admin", authValidator());

// Admin EJS routes - BEFORE React fallback
app.use("/admin", require("./ejs_routes/admin"));
app.use("/admin/categories", require("./ejs_routes/categories"));
app.use("/admin/products", require("./ejs_routes/products"));
app.use("/admin/orders", require("./ejs_routes/orders"));
app.use("/admin/permissions", require("./ejs_routes/permission"));
app.use("/admin/roles", require("./ejs_routes/roles"));
app.use("/admin/subscriptions", require("./ejs_routes/subscriptions"));
app.use("/admin/moderation-logs", require("./ejs_routes/moderationLog"));

// REACT APP - Serve static files at /lwbl
const reactAppPath = path.join(__dirname, "build");
app.use("/lwbl", express.static(reactAppPath));

app.get("/lwbl/*", (req, res) => {
  res.sendFile(path.join(reactAppPath, "index.html"));
});

// Root redirect
app.get("/", (req, res) => {
  res.redirect("/lwbl");
});

// Start server
const PORT = process.env.PORT || 6871;
app.listen(PORT, () => {
  console.log(`App is live on port: ${PORT}`);
  console.log(`React app: http://localhost:${PORT}/lwbl`);
  console.log(`Admin panel: http://localhost:${PORT}/admin`);
});

Expected vs Actual Behavior

Expected (Localhost - Working)

  • http://localhost:6871/lwbl → React app loads

  • http://localhost:6871/admin → Admin panel loads

Actual (cPanel Production - Not Working)

  • https://mydomaincom/lwbl → React app loads

  • https://mydomaincom/lwbl/admin → 404 or blank page

What I've Tried

  1. Verified all files (ejs_routes/*, views/admin/*) are uploaded to cPanel

  2. Checked stderr.log - no errors shown

  3. Confirmed file permissions are correct (755 for directories)

  4. Verified authValidator middleware isn't blocking requests (works on localhost)

Questions

  1. Does PassengerBaseURI "/lwbl" affect how Express routes are resolved?

  2. Should admin routes be /lwbl/admin instead of just /admin when using Passenger?

  3. Is there a routing conflict between the React catch-all (/lwbl/*) and admin routes?

    
    

Any guidance on resolving admin panel routing with Passenger would be greatly appreciated!

node.js express cpanel