0

I have an ugly filesystem path on the server: example.com/a/b/c/d/e/f/main.html. For simplicity, I will use example.com/httpdocs/main.html in the body of this question.

I want that what the user sees is example.com/easy-to-remember. I will use example.com/abc in this question.

For reasons out of the question scope, I would like to do that with only the .htaccess file, not php-routing.

Let's start with the case that works:

RewriteEngine On

# Rewrite abc/ directory for loading of source material
    # Rewrite only if there is no actual file or directory that matches this request
    #   This can especially be useful for loading resources  
    #   because then the full paths won't be redirected if they are correct.
    # Note that RewriteRule never sees leading slashes
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^abc/(.*)$ httpdocs/$1 [NC,L]

With that, the user can open example.com/abc/main.html (expressive path) and is shown the page that is internally stored at example.com/httpdocs/main.html (ugly path). The user never sees the internal path; the url remains /abc/main.html in their browser.

Up to this point, everything works as intended. The CSS source files, specified in a relative path in main.html are still loaded correctly.
But maybe main.html is a very useless name. I'd rather have example.com/abc as a link, instead of example.com/abc/main.html. That'd be more expressive and would allow internal changes of the file extension without changing the links.

# Rewrite abc without trailing slash to main.html
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^abc$ httpdocs/main.html
    # PROBLEM: This turns the current url into "example.com/abc", so "./libs"
    # is now looked for in "example.com/libs" instead of "example.com/httpdocs/libs"  

This rule still works for loading main.html without displaying that name in the url to the user.
But the relative paths are now resolved wrongly.

/httpdocs/main.html relies on /httpdocs/libs/source.css. In the first case, where we access /abc/main.html, the sources are loaded from /abc/libs/source.css and these loads are redirected to /httpdocs/libs/source.css so everything worked fine. In this second case where we access /abc, the css is loaded from /libs/source.css instead, omitting the /httpdocs directory - and that results in a 404, so the page is now not rendered correctly anymore (when accessed in this way).

The obvious solution would be to keep the first case, and if I really don't want to show the name main.html to the user, I could in addition add a rule that redirects /abc/expressive-name to /abc/main.html (or directly to /httpdocs/main.html).
But I am curious whether there are ways to achieve what I was trying to do without adding a second depth level (another slash something) to the url.

I want to keep the loading of the sources relative, so that if I ever have to move the whole filesystem to another subdomain or so, I can just copy the whole thing over. Without that constraint, I could simply make all loads absolute.

1

As you found out,you must keep the URL of the main file at the same level for the relative links to work. A simple way is to use the URL /abc/ instead of /abc.

The difference is what the browser sees as the current directory or base URL. The base URL is the URL with everything removed after the last /. So with /abc the base URL is / while with /abc/ the base URL is /abc.

So you can write:

RewriteRule ^abc/$ httpdocs/main.html

or you can omit the filename in the URL if you supply it with

#RewriteCond %{REQUEST_FILENAME} !-f
#RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^abc(.*)$ httpdocs$1
DirectoryIndex main.html

The RewriteCond rules are just to make sure that the target is not an existing file or directory. As you know that the target doesn't exist, you can omit the rules.

If your main file is always named main.html, you can add DirectoryIndex just to the root directory (or a common ancestor). It would make the RewriteRules shorter, because you don't need separate RewriteRules for the directory and for the main file.

A get request to /abc needs an external redirect to /abc/, but apache will do that automatically if it detects that /abc is a directory.

A useful tool for external redirects is the program wget, it can display the redirects the server sends.

  • Thanks so much! What is the difference between the two approaches? – lucidbrot Jul 22 '18 at 8:28
  • 1
    does that change answer your question? – RalfFriedl Jul 22 '18 at 11:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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