Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.htaccess for an SPA, rewrite incoming requests to index.html, except for static resources

EDIT 1 Thank you very much for your responsiveness and patience!.

I am developing a SPA router, so everything happens in index.html. It uses HTML5 History API to rewrite the history and it would be awesome to create SEO friendly URLs.

Some sections are already on the index.html and others are loaded dynamically with ajax or fetch (if that matters)

The app has the next basic directory structure

assets/ 
css/
img/
js/
modules/
partials/
vendor/
index.html

Now in the router mainly there are sections and subsections, I am rewriting sections as a subdirectory, and subsections as sub-subdirectory

example.com/index.html <--- where the magic happens

But index.html is also ugly, it would be preferable to rewrite it to (not really needed as it happens naturally, but nice to have)

example.com R=301 <permanent

Then, when the user navigates to a section the url will be rewritten by history api as:

example.com/customers

But if the user reloads the website, he gets error 404

Then, when the user navigates to a subsection of that section he will see in the url bar

example.com/customers/casestudy

But again, if the user reloads, he gets error 404

What I want is to permanently re-write those URLs to index.html and its router/state manager will restore(load/show) the correct state.

The goal of this, is that URLs are SEO friendly and that search engines can index correctly each section of the website. Also if we need to hand a URL of our site to a customer/partner/stakeholder/anybody

example.com/mydata is better than example.com/index.html/#/dataFragment25654789

Thanks in advance

EDIT 02 The next .htaccess works fine


  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]

except when trying to load a subsection

example.com/customer/mydata

Then it gets uncaught SyntaxError: Unexpected Token '<' the same as @MrWhite answer

OLD QUESTION

I want to permanently rewrite requests to any file or subdirectory to my index.html as it will dispatch the correct views, but preserving the subdirectory structure, no files allowed. I can't get it to work especially with sub-subdirectories - example.com/index.html or anyother.file ---> example.com/ - example.com/mydirectory/ or example.com/mydirectory/anyfile.file to /index.html URL should look example.com/mydirectory - example.com/mydirectory/mydirectory or example.com/mydirectory/anyfile.file to /index.html URL should look example.com/mydirectory/mydirectory

like image 940
aalvarado Avatar asked Sep 06 '25 04:09

aalvarado


1 Answers

It sounds (from your updated question) that you "simply" need a standard front-controller type pattern... rewrite everything to /index.html (in the document root) except for requests to static resources.

For example, in your root .htaccess file:

# Allow mod_dir to serve index.html when requesting the directory
DirectoryIndex index.html

RewriteEngine On

# Front-controller (exclude static resources)
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_URI} !^/(assets|css|img|js|modules|partials|vendor)/
RewriteRule . index.html [L]

index.html must then look at the requested URL in order to determine how to route the request.

Note that for any requests involving multiple path segments like /customers/casestudy then you will need to ensure you are using root-relative URLs (starting with a slash) or absolute URLs (scheme + hostname) to your static resources (JS, CSS, images, etc.). Any relative URLs will naturally be relative to the client-side URL and will likely fail to resolve.

The first RewriteRule above prevents the following rule being reprocessed after the initial rewrite, to prevent a rewrite loop (and minor optimisation).

The condition that checks the requested URL against the REQUEST_URI server variable ensures we don't rewrite any resources in the known subdirectories. This is a more optimal way of checking that the file (or directory) does not exist on the filesystem before rewriting, since filesystem checks are relatively expensive. However, on systems that don't necessarily have a concisely defined structure then the filesystem check may be necessary.

But index.html is also ugly, it would be preferable to rewrite it to (not really needed as it happens naturally, but nice to have)

Yes, you don't need to specify /index.html in the URL as mod_dir will issue an internal subrequest for index.html when requesting the root directory (that's what the DirectoryIndex index.html directive does).

However, you could issue an external redirect if /index.html is requested directly by the user (or inadvertently linked to by 3rd parties / search engines). Internally you should be linking to /, not /index.html - as you don't want to redirect normal users.

For example, immediately after the RewriteEngine directive (and before the front-controller):

# Redirect "/index.html" to "/"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^index\.html$ / [R=302,L]

The condition that checks against the REDIRECT_STATUS environment variable ensures we don't get a redirect loop (since the later rule rewrites the request back to index.html). REDIRECT_STATUS is not set on the initial request and is set to "200" after the first successful rewrite.

Note that this is currently a 302 (temporary) redirect. Only change it to 301 (permanent) after you have confirmed that this works OK. 301s are cached persistently by the browser so can make testing problematic.

like image 53
MrWhite Avatar answered Sep 09 '25 02:09

MrWhite