.htaccess with single page website

.htaccessurl-routing

I have a single page website with URL parameters that should still open the single index.html (made with Vue.JS and Vue.JS Router). For example, there is www.example.com/, www.example.com/user, and www.example.com/user/project/myproject. Now, as you might expect, I'm not getting this to work with my .htaccess file.

I've tried a lot of ways to get this to work:

Order allow, deny
Deny from all
<FilesMatch ^(index\.html)?$>
  Allow from all
</FilesMatch>

IndexIgnore *
DirectoryIndex index.html

Which didn't do anything. Then I replaced the content of my .htaccess file with this:

RewriteEngine On
RewriteRule ^$ index.html/ [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.html/$1 [QSA,L]

Options All -Indexes

It changed something, but not the correct thing. As www.example.com/user gave an HTTP error 404 as the directory isn't found obviously.

My goal

With a tree like this:

├───assets
├───downloads
│index.html
│robots.txt

I want to be able to access only index.html that is used for any parameter combination. Access to assets and downloads should be accessible as people should be able to view the page correctly and to download content if they want to. assets contains static resources, likewise for downloads.

I have looked at a ton of sources, but none seem to give me the perfect outcome. I'm hoping that you can help me out.

Thanks for your time!

UPDATE

Combining MrWhite's and Lacek's answer seems to do the job, but there is a significant issue now. All files seem to be loaded in with the content of index.html. So every image, js, CSS, etc. has an HTML, BODY, HEAD, DIV, SCRIPT (and so on) tag as data instead of their actual data, like pixel data for images.

This is what my .htaccess looks like now:

Options All -Indexes
DirectoryIndex index.html

<Files index.html>
    AcceptPathInfo On
</Files>

RewriteEngine On

RewriteRule ^index\.html - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.html [L]

And the virtual host config (domain censored):

<VirtualHost *:80>
    ServerAdmin admin@example.com
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example/html

    AliasMatch "/.*" "/var/www/example/html/index.html"
    Alias "/assets"  "/var/www/example/html/assets/"
    Alias "/downloads"  "/var/www/example/html/downloads/"

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Check the comments below and below the answers for more context.

Sources

ServerFault – 301 redirect entire domain to a single page .HTACCESS

ServerFault – htaccess rewriterules for multiple pages with parameters

ServerFault – Having issues with my websites htaccess

HTAccess Guide

HTAccess cheat sheet

HTAccess Cheat Sheet – Duck Duck Go

Best Answer

assets are indeed static resources.

If your "assets" are client-side static resources such as CSS, JS and images then you can't block them in .htaccess as they need to be publicly accessible for the client-side HTML to be able to access. (?)

RewriteRule ^$ index.html/ [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.html/$1 [QSA,L]

This is passing the requested URL as additional pathname information (ie. path-info) to index.html, eg. Request /foo/bar and it will send the request to index.html/foo/bar. The issue here is that the default text/html handler does not accept path-info by default and will trigger a 404.

But Vue.JS may not require this as path-info anyway - it may simply "scan the URL" as you suggest. In which case, you can simplify the directives something like:

Options All -Indexes
DirectoryIndex index.html

RewriteEngine On

RewriteRule ^index\.html - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.html [L]

This is a more "standard" front-controller. The QSA flags are not required here.

This rewrites everything that does not map to a file or directory to index.html. The first RewriteRule is simply an optimisation to prevent the rewritten request to index.html being retested.

You don't need to explicitly rewrite the root request (ie. RewriteRule ^$ index.html/) since mod_dir will do this via an internal subrequest having specified DirectoryIndex index.html.

Related Topic