2

For each page of my website, it is named index.html and stored inside a folder of the name of the page.

  • example.com/ serves example.com/index.html

  • example.com/about/ serves example.com/about/index.html

  • example.com/work/ serves example.com/work/index.html

While all the links on my site are of the format example.com/folder/, how can I redirect any pages ending with index.html to the parent directory, as that is my preference?

So redirect example.com/index.html to example.com/ and example.com/work/index.html to example.com/work/

What do I need in a .htaccess file to 301 redirect as intended above?

1 Answer 1

7

You can do this using mod_rewrite near the top of your .htaccess file. In its simplest form this would be something like:

RewriteEngine On

# Remove "index.html"
RewriteRule (.*)index\.html$ /$1 [R=301,L]

The regex (.*)index\.html$ matches any URL-path that ends with index.html and captures the part of the URL-path before index.html.

The $1 backreference contains the captured URL-path before the index.html. This would be empty in the case of example.com/index.html or about/ in the case of example.com/about/index.html.

HOWEVER, the above will fail if you are also serving files that end with index.html, eg. /something/foo_index.html, as it will redirect to /something/foo_. If this is the case then you would need to change the regex in the above RewriteRule to something like ^(|.+/)index\.html$ instead. For example:

# Remove "index.html" (whole path segment only)
RewriteRule ^(|.+/)index\.html$ /$1 [R=301,L]

This will then match /index.html and /something/index.html, but not /something/foo_index.html.

^(|.+/) matches either nothing (ie. the document root) or something followed by a slash (ie. a subdirectory).

You should test first with a 302 (temporary) redirect in order to avoid potential caching issues and only change to a 301 (permanent) redirect when you are sure everything is working OK.


Additional:

Avoid redirect loop if rewriting to index.html (front-controller pattern)

The above is not sufficient if you are using a front-controller pattern to internally rewrite requests to index.html (not the case here), since the above would also redirect the rewritten request to index.html and trigger a redirect loop.

In this case you would need to add an additional condition (RewriteCond directive) that ensures that only direct requests (by the user) are redirected and not rewritten requests by your script/directives.

You can do this by either checking against THE_REQUEST (which contains the first line of the request headers and does not change when the request is rewritten) OR the REDIRECT_STATUS environment variable (which is empty on the initial request and set to "200" - as in 200 OK HTTP response - after the first successful rewrite).

For example:

# Remove "index.html" on direct requests only
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(.+/)?index\.html$ /$1 [R=301,L]
5
  • I thought that rewrite rule would redirect requests where the index.html is implicit . I was thinking you'd have to match against the request URI environment variable instead. Feb 7, 2021 at 12:07
  • @StephenOstermiller I used to think that as well (a conflict with mod_dir/ DirectoryIndex) and I've seen this suggested in older articles. However, on Apache 2.2+ mod_rewrite executes before mod_dir - there is no conflict AFAICT. So, maybe this was a problem with Apache 1.3? Not sure. However, it is a problem if you try to use a mod_alias RedirectMatch (which executes later). OR if you are rewriting requests to index.html (a front-controller pattern) - in which case you would indeed need additional checks to avoid a redirect loop.
    – MrWhite
    Feb 7, 2021 at 13:02
  • Aside: "match against the request URI var instead" - The REQUEST_URI server variable would have the same problem (as the RewriteRule pattern). You would need to use either THE_REQUEST or the REDIRECT_STATUS env var. (If this was a problem.) @StephenOstermiller
    – MrWhite
    Feb 7, 2021 at 13:05
  • Thanks for clarifying. I knew I could count on you to have a good understanding of the details. I don't even try to answer questions like this anymore. I just wait until you jump in. Feb 7, 2021 at 14:33
  • Thanks for your answer. I really appreciate you explaining each part as while I have an overview of how it works, that helps me understand what's going on
    – Tim Wilson
    Feb 7, 2021 at 14:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.