Deep Dive Article
How to Deploy Angular 18 & 19 Applications on cPanel Hosting: Complete Developer Guide

Angular is a powerful framework for building dynamic web applications. However, deploying an Angular application to cPanel hosting requires more than just uploading your files. In this comprehensive guide, I'll walk you through every step of deploying Angular applications on cPanel-based hosting, including static deployments and advanced Server-Side Rendering (SSR) setups.
Before you start: This guide is split into separate sections per Angular version and deployment type. Jump directly to the section that matches your setup. If you need the original official reference, Namecheap also maintains a deployment guide at namecheap.com/support/knowledgebase.
Step 0 — Find Your Angular Version
Not sure which Angular version you're on? Run this in your project directory:
ng versionLook for the line that says Angular: in the output. Example:
Angular: 18.2.5Also note your Node.js version:
node --version
# Example output: v20.11.0You'll need both numbers before continuing.
Step 1 — Prerequisites & Node.js Compatibility
Install Angular CLI (if not already installed)
npm install -g @angular/cliVerify it worked:
ng versionNode.js Version Requirements
Different Angular versions require different minimum Node.js versions. Your local Node.js must match what's available on your cPanel server. Mismatches cause broken builds or failed npm install on the server.
| Angular Version | Minimum Node.js | Recommended Node.js |
|---|---|---|
| Angular 16 | 16.14.x | 18.x LTS |
| Angular 17 | 18.13.x | 18.x or 20.x LTS |
| Angular 18 | 18.19.x | 20.x LTS |
| Angular 19 | 20.11.x | 20.x or 22.x LTS |
How to check your cPanel's available Node.js versions:
- Log into cPanel
- Search for Setup Node.js App
- Click + CREATE APPLICATION
- Open the Node.js version dropdown and note which versions are listed
If your local version doesn't match, use nvm to switch:
# Install nvm (macOS / Linux)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# Install and use the matching version
nvm install 20
nvm use 20
# Confirm
node --versionFor Windows, use nvm-windows.
Ensure node_modules is present
npm installCheck your app works locally before deploying
ng serveVisit http://localhost:4200. If it loads without errors, you're ready to build.
Choosing Your Deployment Path
What rendering type are you using?
Does your project have SSR / Server-Side Rendering?
│
├─ NO → You have a standard CSR app
│ → Follow: Path A (works for all Angular versions)
│
└─ YES → What Angular version?
│
├─ Angular 16 → Follow: Path B
├─ Angular 17 or 18 → Follow: Path C
└─ Angular 19 → Follow: Path D
How to tell if your project uses SSR:
# Check if @angular/ssr or @nguniversal is in your dependencies
grep -E '"@angular/ssr|@nguniversal' package.jsonIf the command returns a line, you're using SSR. If nothing prints, you have a CSR app.
Path A — CSR (Client-Side Rendering) — Works for All Angular Versions
Use this path if your app does not use Server-Side Rendering. This is the simplest deployment — just static files served via Apache.
A1. Build for Production
ng buildThis creates a dist/ folder in your project root. The structure will be either:
# Angular 16 and below
dist/your-app-name/
├── index.html
├── main.abc123.js
├── styles.def456.css
└── assets/
# Angular 17 and above
dist/your-app-name/browser/
├── index.html
├── main.abc123.js
├── styles.def456.css
└── assets/
Note for Angular 17+: The files you upload are inside thebrowser/subfolder, not the rootdist/folder.
Make sure there are no errors during the build. If errors appear, fix them before continuing.
You can change the output path in angular.json:
"options": {
"outputPath": "dist/your-app-name"
}A2. Upload Files to cPanel
What to upload:
Upload the contents of the build folder (not the folder itself) to your domain's root directory in cPanel:
- For main domain → upload to
public_html/ - For addon domain → upload to its root folder
- For subdomain → upload to its assigned folder
Via File Manager:
- Log into cPanel
- Go to Domains → Domains menu → find your domain → click its root folder
- Click Upload in the top menu
- Compress your build output into a
.zipfirst, upload the zip, then right-click → Extract
Via FTP (FileZilla or similar):
- Host: ftp.yourdomain.com (or your server's IP)
- Username: your cPanel username
- Password: your cPanel password
- Port: 21 (FTP) or 22 (SFTP)
Connect, navigate to public_html/, and drag the files across.
Do NOT upload:node_modules/,.git/,README.md,.gitignore, source.tsfiles, or.envfiles.
A3. Create the .htaccess File
This is the most important step for CSR apps. Without it, any route other than / will return a 404 error.
In cPanel File Manager:
- Navigate to your domain's root folder (where you uploaded files)
- Go to Settings → enable Show Hidden Files (dotfiles)
- Click + File, name it
.htaccess, click Create New File - Right-click the file → Edit
- Paste this content:
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . /index.html [L]6. Click Save Changes and close the editor
If your app is in a subfolder (e.g. yourdomain.com/myapp/), change RewriteBase / to RewriteBase /myapp/.
See the official Namecheap video guide on editing the .htaccess file if you get stuck here.A4. Test Your Deployment
Open a new browser tab and visit your domain. Your Angular app should load. Try navigating to a deep route like /about or /contact directly in the URL bar — if it works, the .htaccess is set up correctly.
✅ Done! CSR deployment complete.
Path B — SSR/SSG: Angular 16 (Universal)
Only follow this path if you are on Angular 16. If you're on Angular 17 or higher, skip to Path C or Path D.
Angular 16 uses @nguniversal/express-engine and a server.ts file. The setup on cPanel requires a small modification to server.ts to work with Apache Passenger.
B1. Modify server.ts
Open server.ts in your project root. Find this function and replace it:
Remove this (old code):
function run(): void {
const port = process.env['PORT'] || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}Replace with this (new code):
function isRunningOnApachePassenger(): boolean {
return moduleFilename.includes('lsnode.js');
}
function run(): void {
// Start up the Node server
const server = app();
if (isRunningOnApachePassenger()) {
server.listen(() => {
console.log('Node Express listening to Passenger Apache');
});
return;
}
const port = process.env['PORT'] || 4000;
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}Then find this block lower in the file:
Remove this:
if (
moduleFilename === __filename ||
moduleFilename.includes('iisnode')
)Replace with:
if (
moduleFilename === __filename ||
moduleFilename.includes('iisnode') ||
isRunningOnApachePassenger()
)B2. Build the SSR App
npm run build:ssrThis creates two output folders inside dist/your-app-name/:
dist/your-app-name/
├── browser/ ← static files served to the client
└── server/ ← Node.js server code
Make sure there are no build errors before continuing.
You can change the output path in angular.json:
"options": {
"outputPath": "dist/your-app-name"
}B3. Prepare Files for Upload
Exclude these from your ZIP archive:
node_modules/.git/README.md.gitignore
Include everything else — especially dist/, package.json, and package-lock.json.
Important: Do NOT usepublic_html/as the upload destination for SSR apps on Namecheap. Create a separate directory for the app. The Node.js server cannot run frompublic_html/.
B4. Upload to cPanel
- Log into cPanel → File Manager
- Create a new directory outside
public_html/(e.g./home/username/my-angular-app/) - Upload your ZIP there, then extract
B5. Set Up the Node.js App in cPanel
- In cPanel, search for Setup Node.js App and open it
- Click + CREATE APPLICATION
- Fill in the settings:
| Field | Value |
|---|---|
| Node.js version | Match your local version (e.g. 18.x) |
| Application mode | Production |
| Application root | /home/username/my-angular-app/ |
| Application URL | Select your domain |
| Application startup file | dist/your-app-name/server/main.js |
4. Add any environment variables from your .env file if needed
5. Click CREATE
6. Stop the app temporarily
7. Go to Detected Configuration Files section
8. Click Run NPM Install
9. Click START APP
Visit your domain in a browser — your SSR app should be running.
✅ Done! Angular 16 SSR deployment complete.
Path C — SSR/SSG: Angular 17 & 18
Follow this path if you are on Angular 17 or 18. These versions useng buildinstead ofnpm run build:ssr, and output aserver.mjsstartup file instead ofmain.js.
The key differences from Angular 16:
- No
server.tsmodifications needed - Build command is just
ng build - Startup file is
dist/your-app-name/server/server.mjs
C1. Build the App
ng buildOutput structure:
dist/your-app-name/
├── browser/ ← static files
└── server/ ← Node.js server
└── server.mjs ← this is your startup file
Make sure there are no build errors.
You can customize the output path in angular.json:
"options": {
"outputPath": "dist/your-app-name"
}C2. Prepare Files for Upload
Exclude the following from your upload ZIP:
node_modules/.git/README.md.gitignore
Include everything else.
Important: Do NOT upload to public_html/ for SSR apps. Create a separate directory.C3. Upload to cPanel
- Log into cPanel → File Manager
- Create a directory outside
public_html/(e.g./home/username/my-angular-app/) - Upload your ZIP there, then extract
C4. Set Up the Node.js App in cPanel
- In cPanel, open Setup Node.js App
- Click + CREATE APPLICATION
- Fill in the settings:
| Field | Value |
|---|---|
| Node.js version | Match your local version (18.x or 20.x) |
| Application mode | Production |
| Application root | /home/username/my-angular-app/ |
| Application URL | Select your domain |
| Application startup file | dist/your-app-name/server/server.mjs |
4. Add environment variables from your .env or .env.local file if applicable
5. Click CREATE
6. Stop the app temporarily
7. Navigate to Detected Configuration Files
8. Click Run NPM Install
9. Click START APP
10. Open your domain in a browser. Your Angular SSR app should load correctly.
✅ Done! Angular 17/18 SSR deployment complete.
Path D — SSR/SSG: Angular 19
Follow this path if you are on Angular 19. Angular 19 has SSR built-in with no extra packages needed, and it defaults to ES Modules (server.mjs).D1. Ensure SSR is Configured
If you created your project with SSR enabled from the start, you're already set. If not, add it:
ng add @angular/ssrD2. Ensure ES Module Support in package.json
Angular 19 uses ES Modules by default. Add "type": "module" to your package.json if it's not already there:
{
"name": "my-angular-app",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "ng serve",
"build": "ng build",
"serve:ssr": "node dist/my-angular-app/server/server.mjs"
}
}D3. Build the App
ng buildOutput structure:
dist/your-app-name/
├── browser/
│ ├── index.html
│ ├── main.abc123.js
│ └── assets/
└── server/
└── server.mjs ← startup file (ES Module format)
D4. Verify Server Startup File (Optional but Recommended)
Angular 19 auto-generates server.mjs. You can review or customize it. A typical minimal version looks like this:
import express from 'express';
import compression from 'compression';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __dirname = dirname(fileURLToPath(import.meta.url));
const app = express();
app.use(compression());
app.use(express.static(join(__dirname, '../browser')));
app.get('*', (req, res) => {
res.sendFile(join(__dirname, '../browser/index.html'));
});
const PORT = process.env.PORT || 4000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
D5. Prepare and Upload Files
Exclude from upload:
node_modules/.git/README.md.gitignore
Important: Upload to a directory outside public_html/, not inside it.
- Log into cPanel → File Manager
- Create
/home/username/my-angular-app/ - Upload and extract your ZIP there
D6. Set Up the Node.js App in cPanel
- Open Setup Node.js App in cPanel
- Click + CREATE APPLICATION
- Fill in the settings:
| Field | Value |
|---|---|
| Node.js version | 20.x or 22.x |
| Application mode | Production |
| Application root | /home/username/my-angular-app/ |
| Application URL | Select your domain |
| Application startup file | dist/your-app-name/server/server.mjs |
4. Add any environment variables needed
5. Click CREATE
6. Stop the app
7. Click Run NPM Install
8. Click START APP
Visit your domain. Angular 19 SSR app is live.
✅ Done! Angular 19 SSR deployment complete.
.htaccess Reference
For CSR apps (Path A) — All Angular versions
Place this in your domain's root folder:
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . /index.html [L]For CSR app in a subfolder (e.g. yourdomain.com/app/)
RewriteEngine On
RewriteBase /app/
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . /app/index.html [L]With compression and caching (recommended for production)
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . /index.html [L]
# GZIP Compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/css
AddOutputFilterByType DEFLATE application/javascript application/json
</IfModule>
# Browser caching — JS and CSS files have hashed names so cache forever
<FilesMatch "\.(js|css)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
# Images cache for 1 year
<FilesMatch "\.(jpg|jpeg|png|gif|ico|svg|webp|woff2)$">
Header set Cache-Control "public, max-age=31536000"
</FilesMatch>
# HTML must always be revalidated
<FilesMatch "\.html$">
Header set Cache-Control "public, max-age=3600, must-revalidate"
</FilesMatch>cPanel Node.js App Setup Reference
This applies to Paths B, C, and D (all SSR deployments).
Key settings explained
Node.js version — must match the version you used locally. Check with node --version.
- Application mode — always set to
Productionfor live deployments. - Application root — the folder where you uploaded your project files. Should be outside
public_html/. Example:/home/username/my-angular-app/ - Application URL — select the domain you want the app to run on.
- Application startup file — the path to your server entry point, relative to the Application root:
| Angular Version | Startup File |
|---|---|
| Angular 16 | dist/your-app-name/server/main.js |
| Angular 17 | dist/your-app-name/server/server.mjs |
| Angular 18 | dist/your-app-name/server/server.mjs |
| Angular 19 | dist/your-app-name/server/server.mjs |
After creating the app
Always follow this order:
- Stop the app
- Go to Detected Configuration Files
- Click Run NPM Install — this installs dependencies on the server
- Click START APP
For more detail on working with Node.js apps in cPanel, see the Namecheap Node.js App guide.
Performance & Caching
Reduce bundle size with lazy loading
Load route components only when the user visits that route:
// app.routes.ts
export const routes: Routes = [
{
path: 'dashboard',
loadComponent: () =>
import('./dashboard/dashboard.component').then(m => m.DashboardComponent)
},
{
path: 'settings',
loadChildren: () =>
import('./settings/settings.routes').then(m => m.SETTINGS_ROUTES)
}
];Use OnPush change detection (Angular 17+)
@Component({
selector: 'app-products',
templateUrl: './products.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProductsComponent {}Analyze your bundle size
npm install -D webpack-bundle-analyzer
ng build --stats-json
npx webpack-bundle-analyzer dist/your-app/stats.jsonTarget bundle size: under 500 KB for initial load.
Test performance after deploying
Use PageSpeed Insights with your live URL. Target scores above 85 for Performance, Accessibility, and Best Practices.
Troubleshooting
404 errors when navigating to any route other than /
Cause: .htaccess file is missing or not configured correctly.
Fix:
- In cPanel File Manager, go to your domain's root folder
- Settings → enable Show Hidden Files (dotfiles)
- Confirm
.htaccessexists - Open and verify the RewriteRule content matches the configuration above
- Contact your host if
mod_rewriteis disabled
Styles or images not loading — app looks unstyled
Cause: Wrong baseHref in angular.json.
Fix: If your app is at the domain root, baseHref should be /. If it's in a subfolder like /myapp/, set it to /myapp/.
{
"projects": {
"my-app": {
"architect": {
"build": {
"options": {
"baseHref": "/",
"outputPath": "dist/my-app"
}
}
}
}
}
}Rebuild and re-upload after changing this.
Node.js app shows "Application is down" in cPanel
Fix steps:
- Go to Setup Node.js App in cPanel
- Click your app
- Check the Detected Configuration Files section — look for error output
- Common causes:
- Wrong startup file path: Double-check the table in the Node.js App Setup section
- Missing dependencies: Stop → Run NPM Install → Start
- Wrong Node.js version: Stop → change version → Run NPM Install → Start
- Angular 16 only: Confirm the
server.tsmodifications from Path B were applied before building
npm install fails on server — dependency errors
Cause: Your local Node.js version and cPanel's Node.js version don't match.
Fix: Check your cPanel's available Node.js versions, then use nvm locally to match:
nvm install 20
nvm use 20
npm install
ng buildThen re-upload and re-run NPM Install in cPanel.
app.server.module not found (Angular 16 specific)
Cause: Old @nguniversal package is not installed or the build script is wrong.
Fix:
npm install @nguniversal/express-engine
npm run build:ssrRoutes work on first load but break on page refresh (SSR apps)
Cause: The Node.js server isn't catching all routes and forwarding to Angular.
Fix: Make sure your server.ts (Angular 16) or server.mjs (Angular 17+) has a catch-all route handler:
// Add this at the end of your server setup
app.get('*', (req, res) => {
res.sendFile(join(__dirname, '../browser/index.html'));
});Angular Version Comparison
| Feature | Angular 16 | Angular 17 | Angular 18 | Angular 19 |
|---|---|---|---|---|
| Minimum Node.js | 16.14 | 18.13 | 18.19 | 20.11 |
| SSR package | @nguniversal |
@angular/ssr |
@angular/ssr |
Built-in |
| Build command (SSR) | npm run build:ssr |
ng build |
ng build |
ng build |
| Startup file | server/main.js |
server/server.mjs |
server/server.mjs |
server/server.mjs |
| server.ts modification | Required | Not needed | Not needed | Not needed |
| ES Modules default | No | No | Optional | Yes |
| Standalone components | Optional | Default | Default | Default |
| Built-in control flow | No | Yes (@if, @for) |
Yes | Yes |
FAQ
Q: Can I deploy multiple Angular apps on the same cPanel account?
Yes. Create a separate directory for each app outside public_html/ (e.g. /home/username/app1/, /home/username/app2/). Create a separate Node.js application in cPanel for each one, each with its own domain or subdomain and startup file.
Q: My app is at yourdomain.com/blog/ — how do I configure the routing?
In angular.json, set baseHref to /blog/ before building. In your .htaccess, set RewriteBase /blog/ and point the rewrite rule to /blog/index.html. See the subfolder .htaccess example in the .htaccess Reference section.
Q: How do I update the app after making code changes?
Rebuild locally with ng build, ZIP the updated files, re-upload to the same directory (overwrite existing), and restart the Node.js app in cPanel if you're using SSR.
Q: Can Angular and a PHP API coexist in the same cPanel account?
Yes. Keep your Angular app in its own folder and your PHP API in public_html/api/ or a separate subdomain. They don't conflict.
Q: How do I add environment variables for production?
In cPanel's Node.js App setup, scroll down to the Environment Variables section and add your key-value pairs. In Angular, use environment.prod.ts for build-time variables and process.env in your server file for runtime variables.
Q: Do I need to run npm install locally before uploading?
No. Do not upload node_modules/. Instead, upload your project without it and run NPM Install inside cPanel's Node.js App setup — it installs dependencies directly on the server.
Q: The Namecheap guide looks different from this one — which is correct?
Both are correct for their respective Angular versions. The official Namecheap guide covers the same deployment paths. This guide adds clearer version-specific separation, a decision tree, Node.js compatibility details, and Angular 19 coverage.
Deployment Checklist
Before going live, verify:
- [ ]
node --versionlocally matches what's in cPanel - [ ]
ng buildcompletes with no errors - [ ] Correct output folder uploaded (
browser/for Angular 17+, rootdist/for Angular 16 CSR) - [ ]
.htaccesscreated and saved in the domain root (CSR only) - [ ]
node_modules/not uploaded — NPM Install run in cPanel instead - [ ] Node.js startup file path matches your Angular version (see table above)
- [ ] Node.js app is running (green status in cPanel)
- [ ] All routes load correctly (test by visiting
/aboutdirectly in the browser) - [ ] Styles, images, and fonts load correctly
- [ ] HTTPS is enabled (use cPanel's free Let's Encrypt SSL)
For the official Namecheap deployment reference, visit namecheap.com/support/knowledgebase.