Search code examples
apache.htaccessserversingle-page-applicationvitepress

.htaccess rewrite all routes except some to a different directory


I have trouble combining redirection in .htaccess and internal rewriting too have a Vitepress home page to a website:

There are several sub directories containing their own websites, for example:

/app-a/*
/app-b/*

Requests with one of those as base should remain unchanged. All other requests should be prefixed with /home, so /about would become /home/about and so on. This should be a redirect that's visible to the client.

However additionally, /home/ should serve /home/index.html, while /home/about should either serve /home/about.html or /home/about/index.html (depending on which one of those exists). This should not be a redirect visible to the client (only if the client requests /home/index.html it should get redirected to /home/).

So the process would be:

  1. Is it one of the sub-directories that exist? If so, keep it unchanged.
  2. Else make it point to /home/...
  3. Determine the HTML file to serve (either <path>.html or <path>/index.html).

I found many similar questions but none that combined the two requirements and I was not able to get it to work myself. This answer for example explains how to exclude some folders, but it explicitly states which subfolders to exclude - even inside the /home/ directory.

This is how far I've gotten:

RewriteEngine On

RewriteCond %{ENV:HTTPS} !on [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$
RewriteRule ^(.*)$ https://%1/$1 [R,L]

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

RewriteCond %{REQUEST_FILENAME} !^/app-a/
RewriteCond %{REQUEST_FILENAME} !^/app-b/
RewriteCond %{REQUEST_FILENAME} !^/home/
RewriteRule ^(.*) /home$1 [L]

Solution

  • After lots of screwing around, I was able to make it work in most cases using this config:

    RewriteEngine On
    
    # force HTTPS
    RewriteCond %{ENV:HTTPS} !on [NC]
    RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$
    RewriteRule ^(.*)$ https://%1/$1 [R,L]
    
    # redirect from <path>.html to <path>
    RewriteCond %{THE_REQUEST} /([^.]+)\.html [NC]
    RewriteRule ^ /%1 [NC,L,R]
    
    # serve <path>.html when requesting <path>
    RewriteCond %{REQUEST_FILENAME}.html -f
    RewriteRule ^ %{REQUEST_URI}.html [NC,L]
    
    # serve /home/<path> if /<path> is no file or directory
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^/?(.*)$ /home/$1 [L]
    
    # serve /index (/home/index.html) at root
    RewriteRule ^$ /index [L]
    
    # 404 error page
    ErrorDocument 404 /home/404.html
    

    However, this solution is not perfect. I still have the problem that 404s are not treated correctly.

    • When I request a nonexistent page like /abc123/, I get a 500 error.
    • When I request a page that is a file as a directory like /about/ instead of /about, I get a 301 error.