An Angular deployment combines a production build, correct base paths, server fallback rules, cache headers, environment configuration, and post-deploy verification. A successful local build is only the beginning of release confidence.
Start with the rendering contract, because it decides what must run in production. A client-rendered application needs only static browser files, a prerendered application needs generated HTML for every known route, and an SSR or hybrid application also needs a request-time runtime.
Do not place secrets in environment files compiled into a browser bundle. Every value shipped to the browser can be inspected. Keep credentials and privileged operations on a server and provide only public configuration to Angular.
ng build invokes the workspace build target and uses the production configuration by default unless the project changes that default. The application builder compiles TypeScript and templates, bundles dependencies, minifies output, removes dead code, and emits the configured outputPath under dist by default.
Read the build summary instead of assuming the output directory. Fail continuous integration on compilation errors and configured size budgets. Investigate CommonJS warnings because those dependencies can reduce bundle optimization.
| Build check | Release question |
|---|---|
| Output path | Which directory must the host publish? |
| Initial bundle budget | Did startup JavaScript and CSS remain within the agreed limit? |
| Lazy bundle budget | Did one feature unexpectedly pull a large dependency? |
| Source maps | Are production maps disabled, private, or uploaded only to the intended monitoring service? |
| Browser targets | Does the Browserslist configuration match supported Angular browsers? |
# Build the default production configuration
ng build
# Build a named configuration when angular.json defines one
ng build --configuration staging
A named configuration can replace files and override build options. It is configuration selection, not a secure place for browser secrets.
All static hosts need the same three inputs: a build command, the exact directory containing index.html, and a fallback rule for Angular routes. Provider auto-detection is convenient, but the release owner should still verify those values against angular.json and the emitted dist tree.
A fallback is a rewrite, not a redirect. The browser must keep /orders/42 in the address bar while the host serves index.html, allowing the Angular router to interpret the URL. Real assets and API paths must be checked before the catch-all rule so a missing JavaScript file does not receive HTML with status 200.
# Build the same command used by the host
npm ci
npm run build
# Confirm index.html is inside the directory you plan to publish
# Replace storefront with the Angular project name
ls dist/storefront/browser
If index.html is one directory deeper than the configured publish directory, the host may show a blank listing, a provider 404, or an unrelated default page.
A project site hosted below a repository path needs a matching base href so router links, scripts, styles, and lazy chunks resolve below that path. For a repository named shop, build with /shop/ rather than /.
GitHub Pages is static hosting. Choose a workflow or Pages deployment tool that publishes the actual browser output directory. Configure a 404 fallback compatible with client-side routes, or use a hash-based URL strategy when the host cannot rewrite deep links.
# Replace shop with the repository name
ng build --base-href /shop/
Publish the generated browser artifacts reported by the build. Test the site root, a lazy route, and a direct refresh of a nested URL after deployment.
name: Deploy Angular to Pages
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 24
cache: npm
- run: npm ci
- run: npx ng build --base-href /storefront/
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v4
with:
path: dist/storefront/browser
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy
id: deployment
uses: actions/deploy-pages@v4
In repository Settings > Pages, select GitHub Actions as the source. Replace storefront in both the base href and artifact path; a user or organization site hosted at the domain root uses / as its base href.
Both platforms can import a Git repository, detect Angular, create a preview for each pull request, and promote a production branch. Detection does not remove the need to verify the build command, project root in a monorepo, publish directory, Node version, environment variables, and router behavior.
[build]
command = "npm run build"
publish = "dist/storefront/browser"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
Replace storefront with the project output. This catch-all belongs to a client-rendered build; an SSR integration needs its runtime route handling instead.
# Git flow: import the repository in the Vercel dashboard
# CLI flow: authenticate, create a preview, then deploy production
vercel
vercel --prod
Inspect the preview URL before --prod. If auto-detection selects the wrong root or output in a monorepo, correct the project settings rather than moving build artifacts by hand.
Use Firebase Hosting or Cloudflare Pages for browser-only and fully static output. Firebase App Hosting is the appropriate Firebase path when an Angular application needs a managed server-rendered runtime; classic Hosting remains a static-file service unless explicitly connected to dynamic backends.
{
"hosting": {
"public": "dist/storefront/browser",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{ "source": "**", "destination": "/index.html" }
]
}
}
The rewrite supports Angular deep links. Keep API or function rewrites above this catch-all so they are not swallowed by index.html.
AWS Amplify Hosting and Azure Static Web Apps both provide repository-driven static deployments, preview environments, managed TLS, and CDN delivery. Their setup screens use different names, but the contract remains build command plus browser output plus SPA fallback.
version: 1
frontend:
phases:
preBuild:
commands:
- npm ci
build:
commands:
- npm run build
artifacts:
baseDirectory: dist/storefront/browser
files:
- "**/*"
cache:
paths:
- node_modules/**/*
Change the base directory to the actual Angular output. Configure the SPA rewrite in Amplify Hosting after this build specification is working.
{
"navigationFallback": {
"rewrite": "/index.html",
"exclude": ["/assets/*", "/*.css", "/*.js", "/*.map"]
}
}
Place staticwebapp.config.json where it is copied into the build output. Exclusions preserve genuine missing-file responses instead of returning HTML for asset requests.
A container is useful when the team needs a portable image, explicit web-server rules, or deployment to a container platform. Build Angular in one stage and copy only the browser artifacts into a small Nginx image; do not ship the compiler, source tree, or node_modules in the runtime layer.
FROM node:24-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist/storefront/browser /usr/share/nginx/html
EXPOSE 80
Replace storefront with the project name and pin image digests in a controlled production pipeline. The runtime image contains only Nginx configuration and deployable files.
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
location /assets/ {
try_files $uri =404;
}
location / {
try_files $uri $uri/ /index.html;
}
}
The asset location returns a real 404, while application routes fall back to index.html. Add cache and security headers according to the production edge architecture.
A deployment is not complete when upload succeeds. Open the production URL in a clean browser session, inspect network and console errors, test a direct deep-link refresh, verify API origins and CORS, and run one critical user journey.
Use immutable long-lived caching for fingerprinted assets, but keep the HTML shell short-lived or revalidated so it can point to the newest filenames. Retain the previous release artifacts and configuration until the new version passes smoke checks.
Deploy the artifacts produced by the selected render mode. A client-rendered route needs browser assets and fallback routing, a server-rendered route needs a compatible request-time server runtime, and a prerendered route needs every expected HTML path generated during the build.
For an existing application, ng add @angular/ssr adds the server entry and rendering configuration. Angular can also emit a fully static application by setting outputMode to static; that build does not require a Node.js server. Do not deploy a server bundle to a static-only host and expect request-time rendering.
| Mode | Deployment contract |
|---|---|
| RenderMode.Client | Serve browser assets and return the application shell for valid client routes that are not real files or APIs. |
| RenderMode.Server | Run the generated server bundle, forward requests through a supported runtime, and deploy browser assets beside it. |
| RenderMode.Prerender | Publish generated HTML plus assets and rebuild when route content or prerender parameters change. |
| Hybrid routes | Deploy both server and browser outputs and verify each route uses its configured render mode. |
| Hydration | Serve server-generated HTML consistently and enable client hydration without DOM rewrites that cause mismatches. |
{
"projects": {
"storefront": {
"architect": {
"build": {
"options": {
"outputMode": "static"
}
}
}
}
}
}
Static output prerenders configured routes without generating a request-time Node server. Confirm parameterized routes and their fallback behavior before choosing this mode.
A release can contain correct code and still fail because HTML, hashed assets, API endpoints, or runtime configuration are cached with the wrong policy. Treat cache headers and public configuration as versioned deployment inputs.
| Artifact | Recommended handling |
|---|---|
| Hashed JavaScript and CSS | Cache for a long time with immutable because a content change creates a new filename. |
| index.html or route HTML | Revalidate or use a short lifetime so it references the current hashed assets. |
| Service worker manifest | Deploy atomically with its matching assets and test update behavior across an existing browser session. |
| Public runtime configuration | Version and validate values such as API origin; never place credentials or signing material in it. |
| Source maps | Keep private or upload directly to the intended monitoring service when production debugging requires them. |
A repeatable pipeline should build once, test the exact artifact, publish it atomically, verify the deployed origin, and preserve a fast rollback. Rebuilding separately for each environment makes artifact identity and incident diagnosis harder.
Diagnose production failures from the first incorrect layer: DNS and TLS, host routing, HTML, static assets, Angular bootstrap, API traffic, then SSR or hydration. Repeatedly rebuilding without identifying that layer can hide the symptom while preserving the faulty configuration.
| Symptom | Likely cause and check |
|---|---|
| Provider 404 at the site root | The publish directory is wrong. Point it at the folder that directly contains the emitted index.html. |
| Home works; nested refresh is 404 | The CSR fallback is missing or ordered after a conflicting rule. Request the nested URL directly with the network panel open. |
| Blank page below a subpath | The base href does not match the deployment path. Inspect script, stylesheet, and lazy-chunk URLs in emitted HTML. |
| JavaScript has text/html MIME type | A missing asset was rewritten to index.html. Exclude assets from the SPA catch-all and correct the requested filename or base path. |
| API works locally but fails in production | Check the public API origin, HTTPS, CORS response headers, credentials mode, authentication callback URL, and proxy order. |
| Chunk load error after release | Old HTML or a service worker points at removed chunks. Use atomic releases, revalidate HTML, retain old assets briefly, and test an already-open tab. |
| SSR process exits or never becomes healthy | Verify the platform start command, PORT binding, runtime version, server dependencies, browser-only APIs, memory limit, and startup logs. |
| Hydration mismatch or layout jump | Server and browser rendered different DOM. Remove nondeterministic template output and move browser-only initialization to browser render hooks. |
Publish the generated directory that directly contains index.html, not the workspace or dist root. Configure unknown application routes such as /orders to fall back to that index.html, while allowing real assets and backend endpoints to resolve normally. Set the base href to the actual deployment path; a GitHub project site usually needs /repository-name/ rather than /. Because outputPath is configurable, inspect the generated files instead of assuming the directory name.
Cache hashed JavaScript, CSS, fonts, and images for a long time because their names change with each build. Keep index.html short-lived or revalidated so it points to the latest files.
No. CSR and outputMode static builds can use static hosting. Request-time RenderMode.Server routes require a supported Angular adapter or a server runtime that executes the generated server application.
Explore 500+ free tutorials across 20+ languages and frameworks.