I just deployed my portfolio site and discovered that typing /blog directly into the browser returns a 404. Works fine clicking around the app, breaks completely on refresh or direct links. Here's what's going on and how to fix it.
The Problem: Server vs. Client Routing
When someone types yourdomain.com/blog into their browser, the request hits your server first. The server looks for a file or directory called "blog" - which doesn't exist. Your routes live in React Router, not as actual files on disk. 404.
Clicking links inside your app works because React Router intercepts those and handles them in the browser. The server never sees those requests. Direct URL access and page refreshes bypass React Router entirely.
The Fix: .htaccess
After trying a few things, I went with an .htaccess file. It works on Hostinger (where this site lives) and most other Apache-based hosts.
# Enable rewriting
RewriteEngine On
# If the request is not for a real file or directory
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Rewrite all requests to the root index.html
RewriteRule ^ index.html [QSA,L]
Put this in your public directory before deploying.
What This Does
The server now serves index.html for any URL that isn't a real file or folder. User visits yourdomain.com/blog, server returns index.html, React loads, React Router reads the URL and renders the blog page.
Other Hosting Platforms
.htaccess works on most traditional hosts, but some platforms do it differently:
- Netlify:
_redirectsfile with/* /index.html 200 - Vercel:
vercel.jsonwith rewrites - Firebase:
firebase.jsonrewrites
I'd still include .htaccess as a fallback - it doesn't hurt to have it there.
Done
My /blog route works now. Direct links work, bookmarks work, refresh works. Search engines can actually crawl the pages too.