Hey folks, I’ve been stuck trying to get Netlify to correctly serve my sitemap.xml
and robots.txt
in a Vite + React Router app. The app is built with React and uses client-side routing (react-router-dom
). I’m deploying the built dist/
folder directly to Netlify (using drag-and-drop).
What I’ve done so far:
Placed sitemap.xml
and robots.txt
inside the public/
folder so they end up inside dist/
after vite build
Verified they exist in dist/
after build
Created a __redirects
file inside dist/
with:bashCopyEdit/sitemap.xml /sitemap.xml 200 /robots.txt /robots.txt 200 /* /index.html 200
Also tried creating a netlify.toml
at the root with:tomlCopyEdit[[redirects]] from = “/sitemap.xml” to = “/sitemap.xml” status = 200 [[redirects]] from = “/robots.txt” to = “/robots.txt” status = 200 [[redirects]] from = “/*” to = “/index.html” status = 200
The issue:
No matter what I do, Netlify keeps routing /sitemap.xml
and /robots.txt
through the SPA (React), returning index.html
instead of the actual files.
I’ve even opened the deployed dist/
folder and verified that:
__redirects
is present
sitemap.xml
and robots.txt
are present
Yet, when I access https://my-site.netlify.app/sitemap.xml
— I get the React app, not the raw XML.
What am I missing?
- Is there a Netlify cache or something overriding the redirects?
- Any Vite-specific quirks I’m missing?
If anyone has solved this (or has a working config they could share), I’d really appreciate it 
Tech stack:
- Vite
- React + React Router (SPA)
- Tailwind
- Deployed via Netlify Drop (manual upload of
dist/
)
Thanks in advance!
@riston996 It’s hard to know what is real information in your post, and what might be entirely wrong/fake, since the post appears to have been written by ChatGPT.
So I’m hesitant to spend much time even reading it.
E.g. At a glance I can see you say __redirects
several times, but that’s wrong, it would be _redirects
You shouldn’t need redirect/rewrite for a file that exists, e.g. (/sitemap.xml to /sitemap.xml
), that’s what Shadowing already accounts for. I would only expect to see the contents of the index.html
for paths that didn’t exist as their own files.
Use can use the Deploy File Browser to see what you’ve actually deployed.
You should remove unnecessary rewrites/redirects in your _redirects
and netlify.toml
to avoid confusion.
1 Like
lol, I spend a long time using different methods to debug it and then asked chatgpt summarise all steps taken but the issue is the same.
Tried your suggestions, removed everything and tried again, but still serving index.html instead of the files.
The deploy file browser shows the file, but when i request for file, reactrouter intercepts it and serves me “index”
@riston996 Excuse my ignorance but when you say “reactrouter” my understanding is that’s “client side routing”, is that correct?
Or are you dealing with something that’s running via serverless/edge functions?
If it’s only a client side router, then it cannot intercept direct requests for the files.
As a ‘static site’, it should be as I’ve outlined.
If you had no rewrites/redirects at all in your _redirects
or netlify.toml
you would expect:
- Requests to
/
to load (because the /index.html
file actually exists)
- Navigating to other pages to work (because it’s happening via ajax)
- Direct requests to other pages to fail with
404
(as there is no .html
for them, and no rewrite)
- Direct requests for your
/sitemap.xml
and /robots.txt
to work (provided the files exist)
If you had a single rewrite of:
_redirects
/* /index.html 200
Then you would expect:
- Requests to
/
to load (because the /index.html
file actually exists)
- Navigating to other pages to work (because it’s happening via ajax)
- Direct requests to other pages to work as they’re being rewritten to
/index.html
- Direct requests for your
/sitemap.xml
and /robots.txt
to work due to shadowing
I’d only expect the contents of the /index.html
to be served if…
A
The file being requested didn’t exist
If it did exist it should be served due to shadowing
If it didn’t exist it’d satisfy the wildcard rewrite and serve the /index.html
or
B
Your rewrite was defined with force
In which case shadowing is ignored and the rewrite to /index.html
always occurs
Thanks a lot nathan, its my bad, it is lollapalooza effect. I had installed it as PWA. The “workbox” library which meant precache the data was intercepting the route resulting it serving index.html. after i added exception to the routes it got solved. This should have been a really simple problem but i have made the app unnecessary complicated by also making it a PWA.
But thanks for your feedback fueled the further solving of this problem.
It’s no problem, in future when requesting assistance from fellow humans you should provide a link to the project instead of just a placeholder. It’ll let them test their own assumptions and you’ll get a faster more accurate response.
Great work hunting the issue down.
1 Like