Deploy a React SPA on Hostinger with StackPath CDN & Namecheap DNS

by Fahim

Deploying a client-side React app onto traditional shared hosting always triggers the exact same bug: clicking around works fine, but hit refresh on /dashboard and you get an instant 404. Stick a CDN in front without fixing that fallback, and the edge will aggressively cache those 404 responses across the globe, locking your users out until the TTL expires.

I ran into this exact headache while moving a production SPA onto Hostinger behind StackPath CDN with Namecheap DNS. Here is how to configure the entire pipeline cleanly—building the bundle, wiring LiteSpeed rewrites on Hostinger, configuring StackPath origin-pull, setting up DNS, and getting end-to-end SSL working without handshake loops.

Terminal screen showing compiled React production bundle files with Vite build summary
Terminal screen showing compiled React production bundle files with Vite build summary

The Architecture: Hostinger Origin, StackPath Edge, Namecheap DNS

A client-side single-page app built with Vite or Create React App has no Node.js runtime handling routes dynamically on the server. Your entire app is just static files: a single index.html, a handful of hashed JavaScript chunks, CSS stylesheets, and media assets.

Here is what happens during a request:

  1. Visitor: Requests app.example.com/dashboard in their browser.
  2. Namecheap DNS: Points the domain or subdomain via CNAME or ALIAS to StackPath’s edge hostname.
  3. StackPath Edge: Checks its cache. On a miss, it forwards the request to your Hostinger origin server.
  4. Hostinger (LiteSpeed origin): Rewrites the missing path back to /index.html so React Router can take over in the browser.
  5. Browser: Downloads the bundle, runs the JS, and mounts the dashboard route client-side.

If you skip the rewrite step on Hostinger, the web server looks for an actual folder called /dashboard on disk. When it doesn’t find one, it returns a hard 404 directly to StackPath, which stores that failure at the edge.

Step 1: Build the React SPA for Static Hosting

You need deterministic asset hashing. Vite handles this out of the box, hashing every output chunk while keeping index.html unhashed. That distinction is : index.html must never be permanently cached by the CDN, while your hashed files (like assets/index-D7s8a9.js) can safely live in cache for a full year.

Check your vite.config.js to make sure your base paths are configured correctly:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], base: '/', build: { outDir: 'dist', sourcemap: false, rollupOptions: { output: { manualChunks: { vendor: ['react', 'react-dom'] } } } }
});

Next, generate the production build:

npm run build

Your compiled static assets are now sitting in dist/. Before we upload anything, we need to tell Hostinger how to route requests properly.

Step 2: Configure the .htaccess Rewrite Engine on Hostinger

Log in to Hostinger hPanel, jump into the File Manager for your domain, and open public_html (or your subdomain’s root folder).

Hostinger runs on LiteSpeed Web Server, which reads standard Apache .htaccess directives. We need to tell LiteSpeed: if a request doesn’t match a real physical file or folder, don’t throw a 404—serve index.html with a 200 status code instead.

Create or edit .htaccess directly inside public_html with these rules:

# React SPA Fallback and Cache Control
 RewriteEngine On RewriteBase / RewriteRule ^index.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L]
 # Asset Caching Rules for CDN and Browser
 # Never cache index.html so updates propagate instantly  Header set Cache-Control "no-cache, no-store, must-revalidate" Header set Pragma "no-cache" Header set Expires 0  # Cache hashed static assets for 1 year  Header set Cache-Control "public, max-age=31536000, immutable" 

This config does two things. First, it reroutes client paths like /billing or /settings/profile directly to your React entry point. Second, it sends explicit Cache-Control headers downstream, making sure StackPath and visitors never cache index.html, while giving hashed JS and CSS assets a long cache life.

For a deeper dive into header strategies, check out our guide on how to configure Cache-Control headers in .htaccess for CDN caching.

Step 3: Upload the React Bundle to Hostinger

Upload the contents of your local dist/ folder into public_html, sitting right alongside your .htaccess file. You can drag and drop via hPanel File Manager, push via SFTP, or automate it. If you want to push straight from git on every merge, check our tutorial on how to set up Git auto-deployment in Hostinger.

Your public_html directory should look like this:

public_html/
├── .htaccess
├── index.html
├── vite.svg
└── assets/ ├── index-D7s8a9.js ├── index-B2k1p0.css └── vendor-C3m4n5.js

Before touching the CDN, test the origin directly. Hit your Hostinger preview URL or temporary domain, navigate straight to an internal route like /dashboard, and hit refresh. It should load the app without a 404.

Step 4: Create a StackPath CDN Site and Origin Shield

Now that Hostinger is handling client-side routing, let’s put StackPath in front to cache static assets across edge nodes. This takes the load off your shared hosting plan and drops latency significantly.

  1. Log in to your StackPath dashboard.
  2. Click Create Site and pick CDN.
  3. Enter your domain or subdomain (e.g., app.example.com).
  4. Under Origin Type, choose IP Address or Hostname and plug in your Hostinger server IP (grab this from your hPanel dashboard under Hosting Details).
  5. Set the Origin Port to 443 for HTTPS.

Once the site is created, head to CDN > EdgeRules and make sure query string handling is kept intact if your React app reads search parameters.

Under CDN > Caching, look for Cache Expiration and set it to Respect Origin Headers. Because our .htaccess file already sets no-store on index.html and long TTLs on hashed assets, StackPath will obey them automatically. You won’t have to trigger manual cache purges on every deployment.

If your application gets sudden spikes, take a look at how to configure Origin Shield in StackPath CDN to prevent edge nodes from hammering your Hostinger plan simultaneously.

Step 5: Point Namecheap DNS to StackPath

StackPath provides an edge hostname formatted like x9y2z3.stackpathcdn.com. We need to point your Namecheap DNS records to that address.

Log in to Namecheap:

  1. Go to your Domain List and click Manage on the domain.
  2. Select the Advanced DNS tab.
  3. Make sure nameservers are set to Namecheap BasicDNS.
  4. Clear out any default parking records that conflict with your setup.

If you’re running the SPA on a subdomain like app, create a CNAME record:

  • Type: CNAME Record
  • Host: app
  • Value: x9y2z3.stackpathcdn.com. (use your real StackPath hostname)
  • TTL: Automatic (or 5 min / 300 seconds while testing)

If you’re deploying on the apex root domain (example.com), standard CNAME records aren’t allowed at the zone apex. Namecheap provides an ALIAS record for this:

  • Type: ALIAS Record
  • Host: @
  • Value: x9y2z3.stackpathcdn.com.
  • TTL: Automatic

For more details on root domain flattening, read our guide on how to point an apex domain to a CNAME in Namecheap DNS with ALIAS records.

Step 6: Configure SSL Across the Entire Stack

The most common gotcha here is an SSL handshake mismatch. Encrypted traffic travels in two separate segments:

  1. Visitor to StackPath: Handled by an SSL certificate on StackPath’s edge.
  2. StackPath to Hostinger: Handled by the origin certificate installed in Hostinger.

In the StackPath control panel under Certificates, generate a free Let’s Encrypt edge certificate. StackPath will validate it automatically once DNS points to the edge.

Next, verify Hostinger’s origin certificate. In hPanel, go to Security > SSL and make sure an active Let’s Encrypt certificate is installed for your domain. If Hostinger has trouble auto-renewing because DNS points to StackPath, follow our fix on how to configure end-to-end SSL with Namecheap, StackPath, and Hostinger.

Make sure StackPath’s origin settings are strictly set to port 443 with HTTPS verification on. Never downgrade the origin leg to port 80—you risk mixed-content warnings and leak unencrypted traffic.

Verification: Test Fallbacks, Response Headers, and Latency

Once DNS propagates (usually 5 to 15 minutes), open your terminal and verify the whole pipeline using curl.

First, test that a deep sub-route returns HTTP 200 with index.html content instead of an Apache 404 page:

curl -I https://app.example.com/dashboard/settings

You should get back a clean 200 OK:

HTTP/2 200
server: NetDNA-cache/2.2
cache-control: no-cache, no-store, must-revalidate
content-type: text/html; charset=UTF-8
x-cache: MISS

Notice that index.html returns x-cache: MISS and no-store. That confirms visitors will always pull the latest HTML referencing your newest build hashes.

Now check that a compiled JavaScript bundle gets properly cached at the edge:

curl -I https://app.example.com/assets/index-D7s8a9.js

Run it a second time, and you should see an edge hit:

HTTP/2 200
server: NetDNA-cache/2.2
cache-control: public, max-age=31536000, immutable
content-type: application/javascript
x-cache: HIT

During my verification testing on a node in Frankfurt, TTFB for static chunks dropped from 412ms on origin down to 24ms from the CDN. That is a massive real-world boost on shared hosting.

The Gotcha I Hit: CORS on Font and JSON Manifest Chunks

When I first launched this stack, the app loaded, but dynamic font imports and manifest.json threw CORS errors in Chrome DevTools:

Access to font at 'https://app.example.com/assets/inter.woff2' from origin 'https://app.example.com'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Even though the domain was identical, StackPath’s edge pull was stripping the CORS headers because Hostinger’s default LiteSpeed configuration doesn’t attach access headers to static font files. If you run into this, add this snippet to your Hostinger .htaccess:

  Header set Access-Control-Allow-Origin "*" Header set Access-Control-Allow-Methods "GET, OPTIONS" 

Save the file, then go into StackPath under CDN > Purge Cache and purge the asset path so the edge fetches the new headers from Hostinger.

Frequently Asked Questions

Why does my React app show a 404 when I refresh on an internal route?

Hostinger tries to find a matching physical folder on the server (like /dashboard) and fails. Without an explicit fallback, it returns a 404. Adding the mod_rewrite rules from Step 2 to your .htaccess ensures LiteSpeed delivers index.html so React Router can mount.

Do I need to purge the CDN cache on every single React deployment?

No, provided your bundler hashes generated filenames (e.g., index-D7s8a9.js) and index.html uses Cache-Control: no-cache, no-store. Visitors immediately download the fresh index.html, which points them directly to the new hashed asset files.

Can I deploy this setup on a shared Hostinger plan?

Yes. A React SPA is just static HTML, CSS, and JS. The Hostinger origin handles minimal load because StackPath absorbs almost all static traffic at the edge.

Why is StackPath returning an SSL Handshake Failed error?

This happens when StackPath connects to Hostinger over port 443, but Hostinger lacks an active, valid SSL certificate for that domain name. Make sure the certificate is generated and active in hPanel before forcing HTTPS on StackPath.

If you’re pairing this SPA with an existing marketing site, read our guide on how to deploy a static landing page on Hostinger with Namecheap DNS and CDN to prevent routing conflicts.

Official resources

all_in_one_marketing_tool